Commit 8a556f764036db1d7164b9afd3828bdf4f034e8d
1 parent
0427feeb
feat:性能优化-首页'wow精选'增加生命周期感知,适时暂停动画
Showing
2 changed files
with
186 additions
and
20 deletions
lib/app/app.dart
@@ -12,6 +12,8 @@ import 'package:wow_english/route/route.dart'; | @@ -12,6 +12,8 @@ import 'package:wow_english/route/route.dart'; | ||
12 | 12 | ||
13 | import '../route/custom_navigator_observer.dart'; | 13 | import '../route/custom_navigator_observer.dart'; |
14 | 14 | ||
15 | +final RouteObserver<PageRoute> customerRouteObserver = CustomNavigatorObserver(); | ||
16 | + | ||
15 | class App extends StatelessWidget { | 17 | class App extends StatelessWidget { |
16 | const App([this._navigatorObserver]); | 18 | const App([this._navigatorObserver]); |
17 | 19 | ||
@@ -51,7 +53,7 @@ class App extends StatelessWidget { | @@ -51,7 +53,7 @@ class App extends StatelessWidget { | ||
51 | navigatorObservers: <NavigatorObserver>[ | 53 | navigatorObservers: <NavigatorObserver>[ |
52 | // 带入ApmNavigatorObserver实例用于路由监听 | 54 | // 带入ApmNavigatorObserver实例用于路由监听 |
53 | // 如果不带入SDK监听器将无法获知页面(PV)入栈退栈行为,错误率(Dart异常数/FlutterPV次数)将异常攀升。 | 55 | // 如果不带入SDK监听器将无法获知页面(PV)入栈退栈行为,错误率(Dart异常数/FlutterPV次数)将异常攀升。 |
54 | - _navigatorObserver ?? ApmNavigatorObserver.singleInstance, CustomNavigatorObserver() | 56 | + _navigatorObserver ?? ApmNavigatorObserver.singleInstance, customerRouteObserver |
55 | ], | 57 | ], |
56 | ), | 58 | ), |
57 | )), | 59 | )), |
lib/pages/home/widgets/ShakeImage.dart
1 | - | ||
2 | import 'dart:async'; | 1 | import 'dart:async'; |
2 | +import 'dart:ui'; | ||
3 | 3 | ||
4 | import 'package:flutter/cupertino.dart'; | 4 | import 'package:flutter/cupertino.dart'; |
5 | +import 'package:flutter/scheduler.dart'; | ||
5 | import 'package:wow_english/common/extension/string_extension.dart'; | 6 | import 'package:wow_english/common/extension/string_extension.dart'; |
6 | 7 | ||
8 | +import '../../../app/app.dart'; | ||
9 | +import '../../../utils/log_util.dart'; | ||
10 | + | ||
7 | ///带左右摇晃的wow封面 | 11 | ///带左右摇晃的wow封面 |
8 | class ShakeImage extends StatefulWidget { | 12 | class ShakeImage extends StatefulWidget { |
9 | const ShakeImage({super.key}); | 13 | const ShakeImage({super.key}); |
@@ -12,16 +16,40 @@ class ShakeImage extends StatefulWidget { | @@ -12,16 +16,40 @@ class ShakeImage extends StatefulWidget { | ||
12 | _ShakeImageState createState() => _ShakeImageState(); | 16 | _ShakeImageState createState() => _ShakeImageState(); |
13 | } | 17 | } |
14 | 18 | ||
15 | -class _ShakeImageState extends State<ShakeImage> with SingleTickerProviderStateMixin, | ||
16 | - WidgetsBindingObserver { | 19 | +class _ShakeImageState extends State<ShakeImage> |
20 | + with SingleTickerProviderStateMixin, WidgetsBindingObserver, RouteAware { | ||
17 | late AnimationController _controller; | 21 | late AnimationController _controller; |
18 | late Animation<double> _animation; | 22 | late Animation<double> _animation; |
19 | late Timer _timer; | 23 | late Timer _timer; |
24 | + late final AppLifecycleListener appLifecycleListener; | ||
25 | + static const TAG = 'ShakeImage'; | ||
20 | 26 | ||
21 | @override | 27 | @override |
22 | void initState() { | 28 | void initState() { |
29 | + var lifecycleState = SchedulerBinding.instance.lifecycleState; | ||
30 | + printLog( | ||
31 | + 'lifecycleState:$lifecycleState'); // lifecycleState:AppLifecycleState.resumed | ||
32 | + | ||
33 | + appLifecycleListener = AppLifecycleListener( | ||
34 | + onStateChange: onStateChange, | ||
35 | + onResume: onResume, | ||
36 | + onInactive: onInactive, | ||
37 | + onHide: onHide, | ||
38 | + onShow: onShow, | ||
39 | + onPause: onPause, | ||
40 | + onRestart: onRestart, | ||
41 | + onDetach: onDetach, | ||
42 | + onExitRequested: onExitRequested, | ||
43 | + ); | ||
44 | + | ||
23 | super.initState(); | 45 | super.initState(); |
24 | WidgetsBinding.instance.addObserver(this); | 46 | WidgetsBinding.instance.addObserver(this); |
47 | + WidgetsBinding.instance.addPostFrameCallback((_) { | ||
48 | + final route = ModalRoute.of(context); | ||
49 | + if (route is PageRoute) { | ||
50 | + customerRouteObserver.subscribe(this, route); | ||
51 | + } | ||
52 | + }); | ||
25 | 53 | ||
26 | _controller = AnimationController( | 54 | _controller = AnimationController( |
27 | duration: const Duration(seconds: 2), | 55 | duration: const Duration(seconds: 2), |
@@ -29,21 +57,37 @@ class _ShakeImageState extends State<ShakeImage> with SingleTickerProviderStateM | @@ -29,21 +57,37 @@ class _ShakeImageState extends State<ShakeImage> with SingleTickerProviderStateM | ||
29 | ); | 57 | ); |
30 | 58 | ||
31 | _animation = TweenSequence([ | 59 | _animation = TweenSequence([ |
32 | - TweenSequenceItem(tween: Tween(begin: 0.0, end: 0.05).chain(CurveTween(curve: Curves.easeInOut)), weight: 1), | ||
33 | - TweenSequenceItem(tween: Tween(begin: 0.05, end: -0.1).chain(CurveTween(curve: Curves.easeInOut)), weight: 1), | ||
34 | - TweenSequenceItem(tween: Tween(begin: -0.1, end: 0.2).chain(CurveTween(curve: Curves.easeInOut)), weight: 1), | ||
35 | - TweenSequenceItem(tween: Tween(begin: 0.2, end: -0.2).chain(CurveTween(curve: Curves.easeInOut)), weight: 1), | ||
36 | - TweenSequenceItem(tween: Tween(begin: -0.2, end: 0.1).chain(CurveTween(curve: Curves.easeInOut)), weight: 1), | ||
37 | - TweenSequenceItem(tween: Tween(begin: 0.1, end: -0.05).chain(CurveTween(curve: Curves.easeInOut)), weight: 1), | ||
38 | - TweenSequenceItem(tween: Tween(begin: -0.05, end: 0.0).chain(CurveTween(curve: Curves.easeInOut)), weight: 1), | 60 | + TweenSequenceItem( |
61 | + tween: Tween(begin: 0.0, end: 0.05) | ||
62 | + .chain(CurveTween(curve: Curves.easeInOut)), | ||
63 | + weight: 1), | ||
64 | + TweenSequenceItem( | ||
65 | + tween: Tween(begin: 0.05, end: -0.1) | ||
66 | + .chain(CurveTween(curve: Curves.easeInOut)), | ||
67 | + weight: 1), | ||
68 | + TweenSequenceItem( | ||
69 | + tween: Tween(begin: -0.1, end: 0.2) | ||
70 | + .chain(CurveTween(curve: Curves.easeInOut)), | ||
71 | + weight: 1), | ||
72 | + TweenSequenceItem( | ||
73 | + tween: Tween(begin: 0.2, end: -0.2) | ||
74 | + .chain(CurveTween(curve: Curves.easeInOut)), | ||
75 | + weight: 1), | ||
76 | + TweenSequenceItem( | ||
77 | + tween: Tween(begin: -0.2, end: 0.1) | ||
78 | + .chain(CurveTween(curve: Curves.easeInOut)), | ||
79 | + weight: 1), | ||
80 | + TweenSequenceItem( | ||
81 | + tween: Tween(begin: 0.1, end: -0.05) | ||
82 | + .chain(CurveTween(curve: Curves.easeInOut)), | ||
83 | + weight: 1), | ||
84 | + TweenSequenceItem( | ||
85 | + tween: Tween(begin: -0.05, end: 0.0) | ||
86 | + .chain(CurveTween(curve: Curves.easeInOut)), | ||
87 | + weight: 1), | ||
39 | ]).animate(_controller); | 88 | ]).animate(_controller); |
40 | 89 | ||
41 | - Timer(const Duration(seconds: 1), () { | ||
42 | - _controller.forward(from: 0.0); | ||
43 | - _timer = Timer.periodic(const Duration(seconds: 4), (Timer timer) { | ||
44 | - _controller.forward(from: 0.0); | ||
45 | - }); | ||
46 | - }); | 90 | + printLog("--initState"); |
47 | 91 | ||
48 | // _controller.addStatusListener((status) { | 92 | // _controller.addStatusListener((status) { |
49 | // if (status == AnimationStatus.completed) { | 93 | // if (status == AnimationStatus.completed) { |
@@ -54,6 +98,67 @@ class _ShakeImageState extends State<ShakeImage> with SingleTickerProviderStateM | @@ -54,6 +98,67 @@ class _ShakeImageState extends State<ShakeImage> with SingleTickerProviderStateM | ||
54 | // }); | 98 | // }); |
55 | } | 99 | } |
56 | 100 | ||
101 | + void _startAnimation() { | ||
102 | + Timer(const Duration(seconds: 1), () { | ||
103 | + _controller.forward(from: 0.0); | ||
104 | + _timer = Timer.periodic(const Duration(seconds: 4), (Timer timer) { | ||
105 | + _controller.forward(from: 0.0); | ||
106 | + }); | ||
107 | + }); | ||
108 | + printLog('_startAnimation'); | ||
109 | + } | ||
110 | + | ||
111 | + void _stopAnimation() { | ||
112 | + _timer.cancel(); | ||
113 | + printLog('_stopAnimation'); | ||
114 | + } | ||
115 | + | ||
116 | + ///进下一页 | ||
117 | + @override | ||
118 | + void didPushNext() { | ||
119 | + super.didPushNext(); | ||
120 | + printLog('--didPushNext'); | ||
121 | + _stopAnimation(); | ||
122 | + } | ||
123 | + | ||
124 | + ///下一个页面退回到当前页 | ||
125 | + @override | ||
126 | + void didPopNext() { | ||
127 | + printLog('--didPopNext'); | ||
128 | + super.didPopNext(); | ||
129 | + _startAnimation(); | ||
130 | + } | ||
131 | + | ||
132 | + ///进入到当前页,在initState之后执行 | ||
133 | + @override | ||
134 | + void didPush() { | ||
135 | + super.didPush(); | ||
136 | + printLog('--didPush'); | ||
137 | + _startAnimation(); | ||
138 | + } | ||
139 | + | ||
140 | + ///退出当前页到上一页,监听导航栈中的路由被弹出(即当前页面被移除)的事件 | ||
141 | + @override | ||
142 | + void didPop() { | ||
143 | + super.didPop(); | ||
144 | + printLog('--didPop'); | ||
145 | + _stopAnimation(); | ||
146 | + } | ||
147 | + | ||
148 | + @override | ||
149 | + Future<bool> didPushRouteInformation(RouteInformation routeInformation) { | ||
150 | + printLog('--didPushRouteInformation routeInformation=$routeInformation'); | ||
151 | + return super.didPushRouteInformation(routeInformation); | ||
152 | + } | ||
153 | + | ||
154 | + ///系统导航栏上的后退按钮(Android)或物理返回按钮被按下时调用,这个方法允许你拦截和处理系统的返回操作。 | ||
155 | + @override | ||
156 | + Future<bool> didPopRoute() { | ||
157 | + printLog('--didPopRoute'); | ||
158 | + return super.didPopRoute(); | ||
159 | + } | ||
160 | + | ||
161 | + ///前后台切换 | ||
57 | @override | 162 | @override |
58 | void didChangeAppLifecycleState(AppLifecycleState state) { | 163 | void didChangeAppLifecycleState(AppLifecycleState state) { |
59 | if (state == AppLifecycleState.paused) { | 164 | if (state == AppLifecycleState.paused) { |
@@ -65,6 +170,8 @@ class _ShakeImageState extends State<ShakeImage> with SingleTickerProviderStateM | @@ -65,6 +170,8 @@ class _ShakeImageState extends State<ShakeImage> with SingleTickerProviderStateM | ||
65 | 170 | ||
66 | @override | 171 | @override |
67 | void dispose() { | 172 | void dispose() { |
173 | + printLog('dispose'); | ||
174 | + customerRouteObserver.unsubscribe(this); | ||
68 | WidgetsBinding.instance.removeObserver(this); | 175 | WidgetsBinding.instance.removeObserver(this); |
69 | _controller.dispose(); | 176 | _controller.dispose(); |
70 | _timer.cancel(); | 177 | _timer.cancel(); |
@@ -82,9 +189,66 @@ class _ShakeImageState extends State<ShakeImage> with SingleTickerProviderStateM | @@ -82,9 +189,66 @@ class _ShakeImageState extends State<ShakeImage> with SingleTickerProviderStateM | ||
82 | child: child, | 189 | child: child, |
83 | ); | 190 | ); |
84 | }, | 191 | }, |
85 | - child: Image.asset('xe_shop'.assetPng, | ||
86 | - width: 153), | 192 | + child: Image.asset('xe_shop'.assetPng, width: 153), |
87 | ), | 193 | ), |
88 | ); | 194 | ); |
89 | } | 195 | } |
90 | -} | ||
91 | \ No newline at end of file | 196 | \ No newline at end of file |
197 | + | ||
198 | + /// 监听状态 | ||
199 | + onStateChange(AppLifecycleState state) { | ||
200 | + printLog('app_state:$state'); | ||
201 | + } | ||
202 | + | ||
203 | + // =============================== 根据App状态的产生的各种回调 =============================== | ||
204 | + | ||
205 | + /// 可见,并且可以响应用户操作时的回调 | ||
206 | + /// 比如应用从后台调度到前台时,在 onShow() 后面 执行 | ||
207 | + /// 注意:这个回调,初始化时 不执行 | ||
208 | + onResume() { | ||
209 | + printLog('---onResume'); | ||
210 | + _startAnimation(); | ||
211 | + } | ||
212 | + | ||
213 | + /// 可见,但无法响应用户操作时的回调 | ||
214 | + onInactive() { | ||
215 | + printLog('---onInactive'); | ||
216 | + } | ||
217 | + | ||
218 | + /// 隐藏时的回调 | ||
219 | + onHide() { | ||
220 | + printLog('---onHide'); | ||
221 | + } | ||
222 | + | ||
223 | + /// 显示时的回调,应用从后台调度到前台时 | ||
224 | + onShow() { | ||
225 | + printLog('---onShow'); | ||
226 | + } | ||
227 | + | ||
228 | + /// 暂停时的回调(后台) | ||
229 | + onPause() { | ||
230 | + printLog('---onPause'); | ||
231 | + _stopAnimation(); | ||
232 | + } | ||
233 | + | ||
234 | + /// 暂停后恢复时的回调 | ||
235 | + onRestart() { | ||
236 | + printLog('---onRestart'); | ||
237 | + } | ||
238 | + | ||
239 | + /// 这两个回调,不是所有平台都支持, | ||
240 | + /// 当退出 并将所有视图与引擎分离时的回调(IOS 支持,Android 不支持) | ||
241 | + onDetach() { | ||
242 | + printLog('---onDetach'); | ||
243 | + } | ||
244 | + | ||
245 | + /// 在退出程序时,发出询问的回调(IOS、Android 都不支持) | ||
246 | + /// 响应 [AppExitResponse.exit] 将继续终止,响应 [AppExitResponse.cancel] 将取消终止。 | ||
247 | + Future<AppExitResponse> onExitRequested() async { | ||
248 | + printLog('---onExitRequested'); | ||
249 | + return AppExitResponse.exit; | ||
250 | + } | ||
251 | + | ||
252 | + void printLog(String msg) { | ||
253 | + // Log.d('$TAG $msg'); | ||
254 | + } | ||
255 | +} |