Final commit.
This commit is contained in:
@ -15,7 +15,7 @@ class OperationBloc extends Bloc<OperationEvent, OperationState> {
|
||||
/// Minimum time the overlay stays visible, even if the underlying request
|
||||
/// completes faster. Prevents a "did anything happen?" UX where a sub-100 ms
|
||||
/// roundtrip flashes the overlay for one frame.
|
||||
static const Duration _minimumDisplayDuration = Duration(milliseconds: 350);
|
||||
static const Duration _minimumDisplayDuration = Duration(milliseconds: 500);
|
||||
|
||||
OperationBloc() : super(OperationIdle()) {
|
||||
on<StartOperation>(_startOperation);
|
||||
@ -40,6 +40,13 @@ class OperationBloc extends Bloc<OperationEvent, OperationState> {
|
||||
) async {
|
||||
_inFlightCount = (_inFlightCount - 1).clamp(0, 1 << 30);
|
||||
|
||||
// Spinner-Mindestdauer einhalten, BEVOR wir ihn schließen — auch wenn eine
|
||||
// Erfolgsmeldung folgt. Sonst blitzt der Spinner bei schnellen Requests nur
|
||||
// einen Frame lang auf (genau der Grund für den vorherigen „kein Spinner").
|
||||
if (_inFlightCount == 0) {
|
||||
await _awaitMinimumOverlayDuration();
|
||||
}
|
||||
|
||||
if (event.message != null) {
|
||||
emit(OperationFinished(message: event.message));
|
||||
await Future.delayed(const Duration(seconds: 5));
|
||||
@ -48,7 +55,6 @@ class OperationBloc extends Bloc<OperationEvent, OperationState> {
|
||||
if (_inFlightCount > 0) {
|
||||
emit(OperationInProgress());
|
||||
} else {
|
||||
await _awaitMinimumOverlayDuration();
|
||||
_overlayStartedAt = null;
|
||||
emit(OperationIdle());
|
||||
}
|
||||
@ -59,6 +65,13 @@ class OperationBloc extends Bloc<OperationEvent, OperationState> {
|
||||
Emitter<OperationState> emit,
|
||||
) async {
|
||||
_inFlightCount = (_inFlightCount - 1).clamp(0, 1 << 30);
|
||||
|
||||
// Auch im Fehlerfall den Spinner mindestens kurz zeigen, bevor die
|
||||
// Fehler-SnackBar erscheint.
|
||||
if (_inFlightCount == 0) {
|
||||
await _awaitMinimumOverlayDuration();
|
||||
}
|
||||
|
||||
emit(OperationFailed(message: event.message));
|
||||
await Future.delayed(const Duration(seconds: 5));
|
||||
|
||||
|
||||
@ -48,18 +48,30 @@ class OperationViewEnforcer extends StatelessWidget {
|
||||
color: Colors.black54,
|
||||
),
|
||||
Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const CircularProgressIndicator(),
|
||||
if (progressMessage != null) ...[
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
progressMessage,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
// Material liefert einen DefaultTextStyle — sonst rendert
|
||||
// der Text hier (über dem Navigator, ohne Scaffold) mit
|
||||
// der gelb-unterstrichenen Fallback-Darstellung.
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const CircularProgressIndicator(),
|
||||
if (progressMessage != null) ...[
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
progressMessage,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user