import 'package:aves/model/entry.dart'; import 'package:aves/services/image_op_events.dart'; import 'package:aves/theme/durations.dart'; import 'package:flushbar/flushbar.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:intl/intl.dart'; import 'package:percent_indicator/circular_percent_indicator.dart'; mixin FeedbackMixin { Flushbar _flushbar; Future dismissFeedback() => _flushbar?.dismiss(); void showFeedback(BuildContext context, String message) { _flushbar = Flushbar( message: message, margin: EdgeInsets.all(8), borderRadius: 8, borderColor: Colors.white30, borderWidth: 0.5, duration: Durations.opToastDisplay * timeDilation, flushbarPosition: FlushbarPosition.TOP, animationDuration: Durations.opToastAnimation, )..show(context); } // report overlay for multiple operations OverlayEntry _opReportOverlayEntry; void showOpReport({ @required BuildContext context, @required Set selection, @required Stream opStream, @required void Function(Set processed) onDone, }) { final processed = {}; // do not handle completion inside `StreamBuilder` // as it could be called multiple times Future onComplete() => _hideOpReportOverlay().then((_) => onDone(processed)); opStream.listen( processed.add, onError: (error) { debugPrint('_showOpReport error=$error'); onComplete(); }, 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( percent: percent, lineWidth: 16, radius: 160, backgroundColor: Colors.white24, progressColor: Theme.of(context).accentColor, 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; } }