improved file op report overlay

This commit is contained in:
Thibault Deckers 2021-01-27 12:43:14 +09:00
parent 79aefc3aa5
commit 797f8a8d07

View file

@ -35,11 +35,60 @@ mixin FeedbackMixin {
@required Stream<T> opStream, @required Stream<T> opStream,
@required void Function(Set<T> processed) onDone, @required void Function(Set<T> processed) onDone,
}) { }) {
final processed = <T>{}; _opReportOverlayEntry = OverlayEntry(
builder: (context) => ReportOverlay<T>(
opStream: opStream,
itemCount: selection.length,
onDone: (processed) {
_opReportOverlayEntry?.remove();
_opReportOverlayEntry = null;
onDone(processed);
},
),
);
Overlay.of(context).insert(_opReportOverlayEntry);
}
}
class ReportOverlay<T> extends StatefulWidget {
final Stream<T> opStream;
final int itemCount;
final void Function(Set<T> processed) onDone;
const ReportOverlay({
@required this.opStream,
@required this.itemCount,
@required this.onDone,
});
@override
_ReportOverlayState createState() => _ReportOverlayState<T>();
}
class _ReportOverlayState<T> extends State<ReportOverlay<T>> with SingleTickerProviderStateMixin {
final processed = <T>{};
AnimationController _animationController;
Animation<double> _animation;
Stream<T> 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` // do not handle completion inside `StreamBuilder`
// as it could be called multiple times // as it could be called multiple times
Future<void> onComplete() => _hideOpReportOverlay().then((_) => onDone(processed)); Future<void> onComplete() => _animationController.reverse().then((_) => widget.onDone(processed));
opStream.listen( opStream.listen(
processed.add, processed.add,
onError: (error) { onError: (error) {
@ -48,17 +97,34 @@ mixin FeedbackMixin {
}, },
onDone: onComplete, onDone: onComplete,
); );
}
_opReportOverlayEntry = OverlayEntry( @override
builder: (context) { void dispose() {
return AbsorbPointer( _animationController.dispose();
child: StreamBuilder<T>( super.dispose();
stream: opStream, }
builder: (context, snapshot) {
Widget child = SizedBox.shrink(); @override
if (!snapshot.hasError) { Widget build(BuildContext context) {
final percent = processed.length.toDouble() / selection.length; return AbsorbPointer(
child = CircularPercentIndicator( child: StreamBuilder<T>(
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, percent: percent,
lineWidth: 16, lineWidth: 16,
radius: 160, radius: 160,
@ -67,22 +133,11 @@ mixin FeedbackMixin {
animation: true, animation: true,
center: Text(NumberFormat.percentPattern().format(percent)), center: Text(NumberFormat.percentPattern().format(percent)),
animateFromLastPercent: true, animateFromLastPercent: true,
); ),
} ),
return AnimatedSwitcher( ),
duration: Durations.collectionOpOverlayAnimation, );
child: child, }),
);
}),
);
},
); );
Overlay.of(context).insert(_opReportOverlayEntry);
}
Future<void> _hideOpReportOverlay() async {
await Future.delayed(Durations.collectionOpOverlayAnimation * timeDilation);
_opReportOverlayEntry?.remove();
_opReportOverlayEntry = null;
} }
} }