From 797f8a8d0753d03fae2d9c5b73dd1cc5994c37a1 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Wed, 27 Jan 2021 12:43:14 +0900 Subject: [PATCH] improved file op report overlay --- .../common/action_mixins/feedback.dart | 111 +++++++++++++----- 1 file changed, 83 insertions(+), 28 deletions(-) diff --git a/lib/widgets/common/action_mixins/feedback.dart b/lib/widgets/common/action_mixins/feedback.dart index 66156eae3..3a0120985 100644 --- a/lib/widgets/common/action_mixins/feedback.dart +++ b/lib/widgets/common/action_mixins/feedback.dart @@ -35,11 +35,60 @@ mixin FeedbackMixin { @required Stream opStream, @required void Function(Set processed) onDone, }) { - final processed = {}; + _opReportOverlayEntry = OverlayEntry( + builder: (context) => ReportOverlay( + opStream: opStream, + itemCount: selection.length, + onDone: (processed) { + _opReportOverlayEntry?.remove(); + _opReportOverlayEntry = null; + onDone(processed); + }, + ), + ); + Overlay.of(context).insert(_opReportOverlayEntry); + } +} + +class ReportOverlay extends StatefulWidget { + final Stream opStream; + final int itemCount; + final void Function(Set processed) onDone; + + const ReportOverlay({ + @required this.opStream, + @required this.itemCount, + @required this.onDone, + }); + + @override + _ReportOverlayState createState() => _ReportOverlayState(); +} + +class _ReportOverlayState extends State> with SingleTickerProviderStateMixin { + final processed = {}; + AnimationController _animationController; + Animation _animation; + + Stream get opStream => widget.opStream; + + @override + void initState() { + super.initState(); + + _animationController = AnimationController( + duration: Durations.collectionOpOverlayAnimation, + vsync: this, + ); + _animation = CurvedAnimation( + parent: _animationController, + curve: Curves.easeOutQuad, + ); + _animationController.forward(); // do not handle completion inside `StreamBuilder` // as it could be called multiple times - Future onComplete() => _hideOpReportOverlay().then((_) => onDone(processed)); + Future onComplete() => _animationController.reverse().then((_) => widget.onDone(processed)); opStream.listen( processed.add, onError: (error) { @@ -48,17 +97,34 @@ mixin FeedbackMixin { }, onDone: onComplete, ); + } - _opReportOverlayEntry = OverlayEntry( - builder: (context) { - return AbsorbPointer( - child: StreamBuilder( - stream: opStream, - builder: (context, snapshot) { - Widget child = SizedBox.shrink(); - if (!snapshot.hasError) { - final percent = processed.length.toDouble() / selection.length; - child = CircularPercentIndicator( + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AbsorbPointer( + child: StreamBuilder( + stream: opStream, + builder: (context, snapshot) { + final percent = processed.length.toDouble() / widget.itemCount; + return FadeTransition( + opacity: _animation, + child: Container( + decoration: BoxDecoration( + gradient: RadialGradient( + colors: [ + Colors.black, + Colors.black54, + ], + ), + ), + child: Center( + child: CircularPercentIndicator( percent: percent, lineWidth: 16, radius: 160, @@ -67,22 +133,11 @@ mixin FeedbackMixin { animation: true, center: Text(NumberFormat.percentPattern().format(percent)), animateFromLastPercent: true, - ); - } - return AnimatedSwitcher( - duration: Durations.collectionOpOverlayAnimation, - child: child, - ); - }), - ); - }, + ), + ), + ), + ); + }), ); - Overlay.of(context).insert(_opReportOverlayEntry); - } - - Future _hideOpReportOverlay() async { - await Future.delayed(Durations.collectionOpOverlayAnimation * timeDilation); - _opReportOverlayEntry?.remove(); - _opReportOverlayEntry = null; } }