fix(feedback-dialogs): add queue system to prevent dialog stacking
This commit is contained in:
@@ -1,17 +1,39 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
|
||||
enum _FeedbackKind { error, success, info }
|
||||
|
||||
final _queue = Queue<_FeedbackRequest>();
|
||||
bool _isShowing = false;
|
||||
|
||||
class _FeedbackRequest {
|
||||
final BuildContext context;
|
||||
final String messageKey;
|
||||
final Map<String, dynamic>? args;
|
||||
final _FeedbackKind kind;
|
||||
final Duration autoDismiss;
|
||||
final Completer<void> completer;
|
||||
|
||||
_FeedbackRequest({
|
||||
required this.context,
|
||||
required this.messageKey,
|
||||
required this.args,
|
||||
required this.kind,
|
||||
required this.autoDismiss,
|
||||
required this.completer,
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> showErrorDialog(
|
||||
BuildContext context,
|
||||
String messageKey, {
|
||||
Map<String, dynamic>? args,
|
||||
Duration autoDismiss = const Duration(seconds: 3),
|
||||
}) {
|
||||
return _showPillFeedback(
|
||||
return _enqueue(
|
||||
context,
|
||||
messageKey: messageKey,
|
||||
args: args,
|
||||
@@ -26,7 +48,7 @@ Future<void> showSuccessDialog(
|
||||
Map<String, dynamic>? args,
|
||||
Duration autoDismiss = const Duration(seconds: 3),
|
||||
}) {
|
||||
return _showPillFeedback(
|
||||
return _enqueue(
|
||||
context,
|
||||
messageKey: messageKey,
|
||||
args: args,
|
||||
@@ -41,7 +63,7 @@ Future<void> showInfoDialog(
|
||||
Map<String, dynamic>? args,
|
||||
Duration autoDismiss = const Duration(seconds: 3),
|
||||
}) {
|
||||
return _showPillFeedback(
|
||||
return _enqueue(
|
||||
context,
|
||||
messageKey: messageKey,
|
||||
args: args,
|
||||
@@ -50,6 +72,50 @@ Future<void> showInfoDialog(
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _enqueue(
|
||||
BuildContext context, {
|
||||
required String messageKey,
|
||||
required Map<String, dynamic>? args,
|
||||
required _FeedbackKind kind,
|
||||
required Duration autoDismiss,
|
||||
}) {
|
||||
final completer = Completer<void>();
|
||||
_queue.add(_FeedbackRequest(
|
||||
context: context,
|
||||
messageKey: messageKey,
|
||||
args: args,
|
||||
kind: kind,
|
||||
autoDismiss: autoDismiss,
|
||||
completer: completer,
|
||||
));
|
||||
_processQueue();
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
Future<void> _processQueue() async {
|
||||
if (_isShowing || _queue.isEmpty) return;
|
||||
|
||||
while (_queue.isNotEmpty && !_queue.first.context.mounted) {
|
||||
_queue.removeFirst().completer.complete();
|
||||
}
|
||||
if (_queue.isEmpty) return;
|
||||
|
||||
_isShowing = true;
|
||||
final request = _queue.removeFirst();
|
||||
|
||||
await _showPillFeedback(
|
||||
request.context,
|
||||
messageKey: request.messageKey,
|
||||
args: request.args,
|
||||
kind: request.kind,
|
||||
autoDismiss: request.autoDismiss,
|
||||
);
|
||||
|
||||
request.completer.complete();
|
||||
_isShowing = false;
|
||||
_processQueue();
|
||||
}
|
||||
|
||||
Future<void> _showPillFeedback(
|
||||
BuildContext context, {
|
||||
required String messageKey,
|
||||
@@ -57,6 +123,7 @@ Future<void> _showPillFeedback(
|
||||
required _FeedbackKind kind,
|
||||
required Duration autoDismiss,
|
||||
}) async {
|
||||
if (!context.mounted) return;
|
||||
final overlay = Overlay.of(context);
|
||||
final resolved = context.translate(messageKey, args: args);
|
||||
|
||||
@@ -78,6 +145,7 @@ Future<void> _showPillFeedback(
|
||||
),
|
||||
};
|
||||
|
||||
final completer = Completer<void>();
|
||||
late final OverlayEntry entry;
|
||||
late final AnimationController controller;
|
||||
|
||||
@@ -93,9 +161,11 @@ Future<void> _showPillFeedback(
|
||||
);
|
||||
|
||||
void dismiss() {
|
||||
if (completer.isCompleted) return;
|
||||
controller.reverse().then((_) {
|
||||
entry.remove();
|
||||
controller.dispose();
|
||||
completer.complete();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -192,8 +262,7 @@ Future<void> _showPillFeedback(
|
||||
controller.forward();
|
||||
|
||||
await Future.delayed(autoDismiss);
|
||||
if (controller.isAnimating ||
|
||||
controller.status == AnimationStatus.completed) {
|
||||
dismiss();
|
||||
}
|
||||
dismiss();
|
||||
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user