From eab521edb97ed1761ec11deb1f65ee4821f730f8 Mon Sep 17 00:00:00 2001 From: Farkhod Sayfitdinov Date: Mon, 12 Jun 2023 15:57:14 +0500 Subject: [PATCH 1/2] added animationController to make smooth progress animation --- lib/src/circular_progress_builder.dart | 10 ++-- lib/src/linear_progress_builder.dart | 5 +- lib/src/progress_builder.dart | 77 ++++++++++++++------------ 3 files changed, 50 insertions(+), 42 deletions(-) diff --git a/lib/src/circular_progress_builder.dart b/lib/src/circular_progress_builder.dart index ad6ae7d..a3dd4a5 100644 --- a/lib/src/circular_progress_builder.dart +++ b/lib/src/circular_progress_builder.dart @@ -4,11 +4,9 @@ import 'action_controller.dart'; import 'progress_builder.dart'; class CircularProgressBuilder extends ProgressBuilder { - static Widget _progressBuilder(context, [double? value]) => - CircularProgressIndicator(value: value); + static Widget _progressBuilder(context, [double? value]) => CircularProgressIndicator(value: value); - static Widget _adaptiveProgressBuilder(context, [double? value]) => - CircularProgressIndicator.adaptive(value: value); + static Widget _adaptiveProgressBuilder(context, [double? value]) => CircularProgressIndicator.adaptive(value: value); const CircularProgressBuilder({ required ProgressChildWidgetBuilder builder, @@ -18,6 +16,7 @@ class CircularProgressBuilder extends ProgressBuilder { VoidCallback? onStart, VoidCallback? onSuccess, ActionController? controller, + int? animationDuration, Key? key, }) : super( action: action, @@ -28,6 +27,7 @@ class CircularProgressBuilder extends ProgressBuilder { onSuccess: onSuccess, progressBuilder: _progressBuilder, controller: controller, + animationDuration: animationDuration, key: key, ); @@ -39,6 +39,7 @@ class CircularProgressBuilder extends ProgressBuilder { VoidCallback? onStart, VoidCallback? onSuccess, ActionController? controller, + int? animationDuration, Key? key, }) : super( action: action, @@ -49,6 +50,7 @@ class CircularProgressBuilder extends ProgressBuilder { onSuccess: onSuccess, progressBuilder: _adaptiveProgressBuilder, controller: controller, + animationDuration: animationDuration, key: key, ); } diff --git a/lib/src/linear_progress_builder.dart b/lib/src/linear_progress_builder.dart index 15f5887..71cee76 100644 --- a/lib/src/linear_progress_builder.dart +++ b/lib/src/linear_progress_builder.dart @@ -4,8 +4,7 @@ import 'action_controller.dart'; import 'progress_builder.dart'; class LinearProgressBuilder extends ProgressBuilder { - static Widget _progressBuilder([context, double? value]) => - LinearProgressIndicator(value: value); + static Widget _progressBuilder([context, double? value]) => LinearProgressIndicator(value: value); const LinearProgressBuilder({ required ProgressChildWidgetBuilder builder, @@ -15,6 +14,7 @@ class LinearProgressBuilder extends ProgressBuilder { VoidCallback? onStart, VoidCallback? onSuccess, ActionController? controller, + int? animationDuration, Key? key, }) : super( action: action, @@ -25,6 +25,7 @@ class LinearProgressBuilder extends ProgressBuilder { onSuccess: onSuccess, progressBuilder: _progressBuilder, controller: controller, + animationDuration: animationDuration, key: key, ); } diff --git a/lib/src/progress_builder.dart b/lib/src/progress_builder.dart index 3df44b5..3eeb8c2 100644 --- a/lib/src/progress_builder.dart +++ b/lib/src/progress_builder.dart @@ -2,17 +2,14 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:progress_builder/progress_builder.dart'; -import 'action_controller.dart'; /// /// Builds a widget in the non-progress/loading state /// -typedef ProgressChildWidgetBuilder = Widget Function( - BuildContext context, void Function()? action, Object? error); +typedef ProgressChildWidgetBuilder = Widget Function(BuildContext context, void Function()? action, Object? error); /// Builds a progress indicator with [double progress] -typedef ProgressIndicatorWidgetBuilder = Widget Function(BuildContext context, - [double? progress]); +typedef ProgressIndicatorWidgetBuilder = Widget Function(BuildContext context, [double? progress]); /// The call back from action to notify about the progress typedef ProgressCallback = void Function(double? progress); @@ -55,6 +52,9 @@ class ProgressBuilder extends StatefulWidget { /// The stream to listen for triggering action externally final ActionController? controller; + /// The animation duration in milliseconds, defaults to 500 + final int? animationDuration; + /// Creates a ProgressBuilder. /// /// The [builder] builds the child, either in initial, done or error state (error != null). @@ -70,6 +70,7 @@ class ProgressBuilder extends StatefulWidget { this.onSuccess, this.onDone, this.onStart, + this.animationDuration, Key? key, }) : super(key: key); @@ -77,44 +78,49 @@ class ProgressBuilder extends StatefulWidget { _ProgressBuilderState createState() => _ProgressBuilderState(); } -class _ProgressBuilderState extends State { - double? _progress; +class _ProgressBuilderState extends State with SingleTickerProviderStateMixin { dynamic _error; - + bool _isInProgress = false; + AnimationController? _progressAnimationController; StreamSubscription? _subscription; + late final Duration _animationDuration; @override void initState() { - _subscription = (widget.controller ?? DefaultActionController.of(context)) - ?.stream - .listen((event) { - if (event == ActionType.start && mounted && _progress == null) { + super.initState(); + _animationDuration = Duration(milliseconds: widget.animationDuration ?? 500); + _progressAnimationController = AnimationController( + vsync: this, + duration: _animationDuration, + ); + + _subscription = (widget.controller ?? DefaultActionController.of(context))?.stream.listen((event) { + if (event == ActionType.start && mounted && !_isInProgress) { _action(); } }); - super.initState(); } @override void dispose() { + _progressAnimationController?.dispose(); _subscription?.cancel(); super.dispose(); } - void _progressCallback(double? progress) { - setState(() { - _progress = progress ?? -1; - }); - } - Future _action() async { - setState(() { - _progress = -1; - _error = null; - }); + _isInProgress = true; + _progressAnimationController?.value = 0.0; + _error = null; widget.onStart?.call(); try { - await widget.action?.call(_progressCallback); + await widget.action?.call((progress) { + _progressAnimationController?.animateTo( + progress ?? 0, + duration: _animationDuration, + curve: Curves.ease, + ); + }); widget.onSuccess?.call(); } catch (e) { _error = e; @@ -125,22 +131,21 @@ class _ProgressBuilderState extends State { } } finally { if (mounted) { - setState(() { - _progress = null; - }); + _isInProgress = false; } widget.onDone?.call(); } } @override - Widget build(BuildContext context) { - if (_progress != null) { - final progress = _progress! < 0 ? null : _progress; - return widget.progressBuilder.call(context, progress); - } else { - return widget.builder( - context, widget.action != null ? _action : null, _error); - } - } + Widget build(BuildContext context) => AnimatedBuilder( + animation: _progressAnimationController!, + builder: (context, child) { + if (_isInProgress) { + return widget.progressBuilder.call(context, _progressAnimationController!.value); + } else { + return widget.builder(context, widget.action != null ? _action : null, _error); + } + }, + ); } From 60ac361da785d76a67f1ccefc473cb6afaaae6f0 Mon Sep 17 00:00:00 2001 From: Farkhod Sayfitdinov Date: Mon, 12 Jun 2023 23:46:05 +0500 Subject: [PATCH 2/2] fix the progress should be completed when the action is done. --- lib/src/progress_builder.dart | 39 ++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/lib/src/progress_builder.dart b/lib/src/progress_builder.dart index 3eeb8c2..e3be8bf 100644 --- a/lib/src/progress_builder.dart +++ b/lib/src/progress_builder.dart @@ -81,7 +81,7 @@ class ProgressBuilder extends StatefulWidget { class _ProgressBuilderState extends State with SingleTickerProviderStateMixin { dynamic _error; bool _isInProgress = false; - AnimationController? _progressAnimationController; + late final AnimationController _progressAnimationController; StreamSubscription? _subscription; late final Duration _animationDuration; @@ -103,27 +103,33 @@ class _ProgressBuilderState extends State with SingleTickerProv @override void dispose() { - _progressAnimationController?.dispose(); + _progressAnimationController.dispose(); _subscription?.cancel(); super.dispose(); } Future _action() async { - _isInProgress = true; - _progressAnimationController?.value = 0.0; - _error = null; + setState(() { + _isInProgress = true; + _progressAnimationController.value = 0.0; + _error = null; + }); + widget.onStart?.call(); + try { await widget.action?.call((progress) { - _progressAnimationController?.animateTo( + _progressAnimationController.animateTo( progress ?? 0, duration: _animationDuration, curve: Curves.ease, ); }); + widget.onSuccess?.call(); } catch (e) { _error = e; + if (widget.onError != null) { widget.onError!(e); } else { @@ -131,18 +137,31 @@ class _ProgressBuilderState extends State with SingleTickerProv } } finally { if (mounted) { - _isInProgress = false; + await _progressAnimationController + .animateTo( + 1.0, + duration: _animationDuration, + curve: Curves.ease, + ) + .then((_) { + if (mounted) { + setState(() { + _isInProgress = false; + }); + } + }); + + widget.onDone?.call(); } - widget.onDone?.call(); } } @override Widget build(BuildContext context) => AnimatedBuilder( - animation: _progressAnimationController!, + animation: _progressAnimationController, builder: (context, child) { if (_isInProgress) { - return widget.progressBuilder.call(context, _progressAnimationController!.value); + return widget.progressBuilder.call(context, _progressAnimationController.value); } else { return widget.builder(context, widget.action != null ? _action : null, _error); }