Final commit.

This commit is contained in:
Dennis Nemec
2026-06-01 17:12:28 +02:00
parent 3ecbc82885
commit a9bf8ecdd1
385 changed files with 29081 additions and 12089 deletions

View File

@ -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));

View File

@ -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,
),
),
],
],
],
),
),
),
],