aves/lib/widgets/common/action_delegates/feedback.dart
Thibault Deckers f32c3f1154 album: delete
2020-09-28 11:02:06 +09:00

88 lines
2.9 KiB
Dart

import 'package:aves/model/image_entry.dart';
import 'package:aves/services/image_file_service.dart';
import 'package:aves/utils/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<void> 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<T extends ImageOpEvent>({
@required BuildContext context,
@required List<ImageEntry> selection,
@required Stream<T> opStream,
@required void Function(Set<T> processed) onDone,
}) {
final processed = <T>{};
// do not handle completion inside `StreamBuilder`
// as it could be called multiple times
Future<void> 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<T>(
stream: opStream,
builder: (context, snapshot) {
Widget child = SizedBox.shrink();
if (!snapshot.hasError && snapshot.connectionState == ConnectionState.active) {
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<void> _hideOpReportOverlay() async {
await Future.delayed(Durations.collectionOpOverlayAnimation * timeDilation);
_opReportOverlayEntry.remove();
_opReportOverlayEntry = null;
}
}