Line data Source code
1 : import 'package:cached_network_image/cached_network_image.dart';
2 : import 'package:flutter/material.dart';
3 : import 'package:flutter/services.dart';
4 : import 'package:google_fonts/google_fonts.dart';
5 : import 'package:mvvm_builder/mvvm_builder.dart';
6 : import 'package:pal/src/injectors/user_app/user_app_injector.dart';
7 : import 'package:pal/src/services/package_version.dart';
8 : import 'package:pal/src/theme.dart';
9 : import 'package:pal/src/ui/client/helpers/user_update_helper/user_update_helper_presenter.dart';
10 : import 'package:pal/src/ui/client/helpers/user_update_helper/user_update_helper_viewmodel.dart';
11 : import 'package:pal/src/ui/client/helpers/user_update_helper/widgets/animated_progress_bar.dart';
12 : import 'package:pal/src/ui/client/helpers/user_update_helper/widgets/release_note_cell.dart';
13 : import 'package:pal/src/ui/client/widgets/animated/animated_scale.dart';
14 : import 'package:pal/src/ui/client/widgets/animated/animated_translate.dart';
15 : import 'package:pal/src/ui/shared/helper_shared_viewmodels.dart';
16 :
17 : abstract class UserUpdateHelperView {
18 : void playAnimation(
19 : MvvmContext context,
20 : bool isReversed,
21 : int index,
22 : Function callback,
23 : );
24 : void onThanksButtonCallback();
25 : }
26 :
27 : class UserUpdateHelperPage extends StatelessWidget
28 : implements UserUpdateHelperView {
29 : final HelperBoxViewModel helperBoxViewModel;
30 : final HelperTextViewModel titleLabel;
31 : final List<HelperTextViewModel> changelogLabels;
32 : final HelperTextViewModel thanksButtonLabel;
33 : final PackageVersionReader packageVersionReader;
34 : final HelperImageViewModel helperImageViewModel;
35 : final Function onPositivButtonTap;
36 :
37 1 : UserUpdateHelperPage({
38 : Key key,
39 : @required this.helperBoxViewModel,
40 : @required this.titleLabel,
41 : @required this.changelogLabels,
42 : @required this.onPositivButtonTap,
43 : this.helperImageViewModel,
44 : this.thanksButtonLabel,
45 : this.packageVersionReader,
46 0 : }) : assert(helperBoxViewModel != null),
47 0 : assert(titleLabel != null),
48 0 : assert(changelogLabels != null);
49 :
50 : final _mvvmPageBuilder =
51 : MVVMPageBuilder<UserUpdateHelperPresenter, UserUpdateHelperModel>();
52 :
53 1 : @override
54 : Widget build(BuildContext context) {
55 2 : return _mvvmPageBuilder.build(
56 1 : key: UniqueKey(),
57 : context: context,
58 1 : multipleAnimControllerBuilder: (tickerProvider) {
59 1 : return [
60 : // Changelog
61 1 : AnimationController(
62 : vsync: tickerProvider,
63 1 : duration: Duration(
64 : milliseconds: 1100,
65 : ),
66 : ),
67 : // Progress bar
68 1 : AnimationController(
69 : vsync: tickerProvider,
70 1 : duration: Duration(
71 : milliseconds: 5000,
72 : ),
73 : ),
74 : // Image logo
75 1 : AnimationController(
76 : vsync: tickerProvider,
77 1 : duration: Duration(
78 : milliseconds: 700,
79 : ),
80 : ),
81 : // Title
82 1 : AnimationController(
83 : vsync: tickerProvider,
84 1 : duration: Duration(
85 : milliseconds: 700,
86 : ),
87 : ),
88 : ];
89 : },
90 1 : animListener: (context, presenter, model) {
91 1 : if (model.changelogCascadeAnimation) {
92 1 : this.playAnimation(
93 : context,
94 1 : model.isReversedAnimations,
95 : 0,
96 1 : presenter.onCascadeAnimationEnd,
97 : );
98 : }
99 1 : if (model.progressBarAnimation) {
100 1 : this.playAnimation(
101 : context,
102 1 : model.isReversedAnimations,
103 : 1,
104 1 : presenter.onProgressBarAnimationEnd,
105 : );
106 : }
107 1 : if (model.imageAnimation) {
108 1 : this.playAnimation(
109 : context,
110 1 : model.isReversedAnimations,
111 : 2,
112 1 : presenter.onImageAnimationEnd,
113 : );
114 : }
115 1 : if (model.titleAnimation) {
116 1 : this.playAnimation(
117 : context,
118 1 : model.isReversedAnimations,
119 : 3,
120 1 : presenter.onTitleAnimationEnd,
121 : );
122 : }
123 : },
124 2 : presenterBuilder: (context) => UserUpdateHelperPresenter(
125 : this,
126 1 : packageVersionReader ?? UserInjector.of(context).packageVersionReader,
127 : ),
128 1 : builder: (context, presenter, model) {
129 1 : return this._buildPage(context, presenter, model);
130 : },
131 : );
132 : }
133 :
134 1 : Widget _buildPage(
135 : final MvvmContext context,
136 : final UserUpdateHelperPresenter presenter,
137 : final UserUpdateHelperModel model,
138 : ) {
139 1 : return AnimatedOpacity(
140 1 : duration: Duration(milliseconds: 500),
141 : curve: Curves.fastOutSlowIn,
142 1 : opacity: model.helperOpacity,
143 1 : child: Scaffold(
144 1 : key: ValueKey('pal_UserUpdateHelperWidget_Scaffold'),
145 2 : backgroundColor: helperBoxViewModel?.backgroundColor,
146 1 : body: SafeArea(
147 1 : child: Container(
148 : width: double.infinity,
149 1 : child: Container(
150 1 : child: Column(
151 1 : children: [
152 6 : if (helperImageViewModel?.url != null && helperImageViewModel.url.length > 0)
153 1 : Flexible(
154 1 : key: ValueKey('pal_UserUpdateHelperWidget_Icon'),
155 : flex: 4,
156 1 : child: _buildMedia(context),
157 : ),
158 1 : Flexible(
159 1 : key: ValueKey('pal_UserUpdateHelperWidget_AppSummary'),
160 : flex: 2,
161 1 : child: _buildAppSummary(context, model),
162 : ),
163 1 : Expanded(
164 1 : key: ValueKey('pal_UserUpdateHelperWidget_ReleaseNotes'),
165 : flex: 5,
166 1 : child: _buildReleaseNotes(context, model),
167 : ),
168 1 : _buildThanksButton(context, model, presenter),
169 : ],
170 : ),
171 : ),
172 : ),
173 : ),
174 : ),
175 : );
176 : }
177 :
178 1 : Widget _buildMedia(
179 : final MvvmContext context,
180 : ) {
181 1 : return Container(
182 : width: double.infinity,
183 1 : child: Padding(
184 : padding: const EdgeInsets.all(25.0),
185 1 : child: AnimatedScaleWidget(
186 1 : widget: ClipRRect(
187 1 : borderRadius: BorderRadius.circular(15.0),
188 1 : child: CachedNetworkImage(
189 1 : key: ValueKey('pal_UserUpdateHelperWidget_Image'),
190 2 : imageUrl: helperImageViewModel?.url,
191 : fit: BoxFit.contain,
192 1 : placeholder: (context, url) =>
193 2 : Center(child: CircularProgressIndicator()),
194 0 : errorWidget: (BuildContext context, String url, dynamic error) {
195 0 : return Image.asset(
196 : 'packages/pal/assets/images/create_helper.png',
197 : );
198 : },
199 : ),
200 : ),
201 2 : animationController: context.animationsControllers[2],
202 : ),
203 : ),
204 : );
205 : }
206 :
207 1 : Container _buildAppSummary(
208 : final MvvmContext context,
209 : final UserUpdateHelperModel model,
210 : ) {
211 1 : return Container(
212 : width: double.infinity,
213 1 : child: AnimatedTranslateWidget(
214 4 : position: Tween<Offset>(begin: Offset(-1.0, 0.0), end: Offset(0, 0)),
215 2 : animationController: context.animationsControllers[3],
216 1 : widget: Column(
217 : mainAxisAlignment: MainAxisAlignment.center,
218 1 : children: [
219 1 : Text(
220 2 : titleLabel?.text ?? 'New application update',
221 1 : key: ValueKey('pal_UserUpdateHelperWidget_AppSummary_Title'),
222 1 : style: TextStyle(
223 2 : fontSize: titleLabel?.fontSize ?? 27.0,
224 2 : fontWeight: titleLabel?.fontWeight ?? FontWeight.normal,
225 2 : color: titleLabel?.fontColor ??
226 0 : PalTheme.of(context.buildContext).colors.light,
227 1 : ).merge(
228 3 : GoogleFonts.getFont(titleLabel?.fontFamily ?? 'Montserrat'),
229 : ),
230 : ),
231 1 : SizedBox(
232 : height: 10.0,
233 : ),
234 1 : Text(
235 2 : 'Version ${model.appVersion ?? '1.0.0'}',
236 1 : key: ValueKey('pal_UserUpdateHelperWidget_AppSummary_Version'),
237 1 : style: TextStyle(
238 : fontSize: 13.0,
239 : fontWeight: FontWeight.bold,
240 : color: Colors.white,
241 : ),
242 : ),
243 : ],
244 : ),
245 : ),
246 : );
247 : }
248 :
249 1 : SizedBox _buildThanksButton(
250 : final MvvmContext context,
251 : final UserUpdateHelperModel model,
252 : final UserUpdateHelperPresenter presenter,
253 : ) {
254 1 : return SizedBox(
255 1 : key: ValueKey('pal_UserUpdateHelperWidget_ThanksButton'),
256 : width: double.infinity,
257 1 : child: Padding(
258 : padding: const EdgeInsets.only(
259 : left: 20.0,
260 : right: 20.0,
261 : bottom: 15.0,
262 : ),
263 1 : child: RaisedButton(
264 1 : key: ValueKey('pal_UserUpdateHelperWidget_ThanksButton_Raised'),
265 4 : color: PalTheme.of(context.buildContext).colors.dark,
266 1 : onPressed: model.showThanksButton
267 0 : ? () {
268 0 : HapticFeedback.selectionClick();
269 0 : presenter.onThanksButtonCallback();
270 : }
271 : : null,
272 1 : shape: RoundedRectangleBorder(
273 1 : borderRadius: BorderRadius.circular(8.0),
274 : ),
275 1 : child: AnimatedSwitcher(
276 1 : duration: Duration(milliseconds: 300),
277 1 : transitionBuilder: (Widget child, Animation<double> animation) =>
278 1 : ScaleTransition(
279 : child: child,
280 : scale: animation,
281 : ),
282 1 : child: model.showThanksButton
283 1 : ? Padding(
284 : padding: const EdgeInsets.symmetric(vertical: 12.0),
285 1 : child: Text(
286 2 : thanksButtonLabel?.text ?? 'Thank you !',
287 1 : style: TextStyle(
288 2 : fontSize: thanksButtonLabel?.fontSize ?? 18.0,
289 2 : color: thanksButtonLabel?.fontColor ?? Colors.white,
290 : fontWeight:
291 2 : thanksButtonLabel?.fontWeight ?? FontWeight.normal,
292 1 : ).merge(
293 1 : GoogleFonts.getFont(
294 2 : titleLabel?.fontFamily ?? 'Montserrat'),
295 : ),
296 : ),
297 : )
298 1 : : AnimatedProgressBar(
299 2 : animationController: context.animationsControllers[1],
300 : ),
301 : ),
302 : ),
303 : ),
304 : );
305 : }
306 :
307 1 : SingleChildScrollView _buildReleaseNotes(
308 : final MvvmContext context,
309 : final UserUpdateHelperModel model,
310 : ) {
311 1 : return SingleChildScrollView(
312 1 : child: Container(
313 : width: double.infinity,
314 1 : child: Padding(
315 : padding: const EdgeInsets.symmetric(horizontal: 27.0),
316 1 : child: Column(
317 1 : key: ValueKey('pal_UserUpdateHelperWidget_ReleaseNotes_List'),
318 1 : children: _buildReleaseNotesLabels(context, model),
319 : ),
320 : ),
321 : ),
322 : );
323 : }
324 :
325 1 : List<Widget> _buildReleaseNotesLabels(
326 : final MvvmContext context,
327 : final UserUpdateHelperModel model,
328 : ) {
329 1 : List<Widget> labels = [];
330 : int index = 0;
331 2 : for (HelperTextViewModel label in changelogLabels) {
332 3 : var stepTime = 1.0 / changelogLabels.length;
333 1 : var animationStart = stepTime * index;
334 1 : var animationEnd = stepTime + animationStart;
335 :
336 1 : labels.add(
337 1 : ReleaseNoteCell(
338 1 : index: index++,
339 : customLabel: label,
340 2 : animationController: context.animationsControllers[0],
341 1 : positionCurve: Interval(
342 : animationStart,
343 : animationEnd,
344 : curve: Curves.decelerate,
345 : ),
346 1 : opacityCurve: Interval(
347 : animationStart,
348 : animationEnd,
349 : curve: Curves.decelerate,
350 : ),
351 : ),
352 : );
353 : }
354 : return labels;
355 : }
356 :
357 1 : @override
358 : void playAnimation(
359 : MvvmContext context,
360 : bool isReversed,
361 : int index,
362 : Function callback,
363 : ) {
364 : if (isReversed) {
365 0 : context.animationsControllers[index]
366 0 : .reverse()
367 0 : .then((value) => callback());
368 : } else {
369 2 : context.animationsControllers[index]
370 1 : .forward()
371 3 : .then((value) => callback());
372 : }
373 : }
374 :
375 0 : @override
376 : void onThanksButtonCallback() {
377 0 : if (this.onPositivButtonTap != null) {
378 0 : this.onPositivButtonTap();
379 : }
380 : }
381 : }
|