tasq/lib/utils/snackbar.dart

201 lines
5.8 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:awesome_snackbar_content/awesome_snackbar_content.dart';
import 'package:flutter/foundation.dart';
/// A global messenger key used to show snackbars from contexts without a
/// Scaffold (e.g. dialogs, background callbacks, or tests).
final GlobalKey<ScaffoldMessengerState> scaffoldMessengerKey =
GlobalKey<ScaffoldMessengerState>();
/// Helper wrappers around `awesome_snackbar_content` so that callers
/// can show snackbars with a consistent look and only specify a message and
/// semantic type.
/// Enumeration used by callers; the mapping to the packages
/// [ContentType] is internal.
enum SnackType { success, info, warning, error }
ContentType _mapSnackType(SnackType t) {
switch (t) {
case SnackType.success:
return ContentType.success;
case SnackType.info:
return ContentType.help;
case SnackType.warning:
return ContentType.warning;
case SnackType.error:
return ContentType.failure;
}
}
/// Core function used by all of the convenience helpers below.
void showAwesomeSnackBar(
BuildContext context, {
required String title,
required String message,
required SnackType snackType,
bool retry = true,
}) {
// Add margin and padding so even very short messages feel substantial.
final snackBar = SnackBar(
elevation: 0,
behavior: SnackBarBehavior.floating,
// give floating snackbar some breathing room
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
padding: EdgeInsets.zero,
backgroundColor: Colors.transparent,
content: Container(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 8),
child: AwesomeSnackbarContent(
title: title,
message: message,
contentType: _mapSnackType(snackType),
),
),
);
// Prefer a local ScaffoldMessenger when available, but prefer the global
// `scaffoldMessengerKey` if it's already ready. This increases reliability
// when callers are inside dialogs or other overlays.
final globalMessenger = scaffoldMessengerKey.currentState;
final localMessenger = ScaffoldMessenger.maybeOf(context);
final messenger = localMessenger ?? globalMessenger;
if (messenger == null) {
if (kDebugMode) {
debugPrint(
'showAwesomeSnackBar: no messenger available; scheduling fallback',
);
}
// If the scaffold messenger is not available yet, schedule a post-frame
// callback that uses the global messenger (if it becomes available).
if (retry) {
WidgetsBinding.instance.addPostFrameCallback((_) {
final gm = scaffoldMessengerKey.currentState;
if (gm != null) {
try {
gm
..hideCurrentSnackBar()
..showSnackBar(snackBar);
if (kDebugMode) {
debugPrint(
'showAwesomeSnackBar: shown via global messenger (post-frame)',
);
}
} catch (e) {
if (kDebugMode) {
debugPrint('showAwesomeSnackBar: post-frame show failed: $e');
}
}
} else if (kDebugMode) {
debugPrint(
'showAwesomeSnackBar: global messenger still null after frame',
);
}
});
}
return;
}
try {
messenger
..hideCurrentSnackBar()
..showSnackBar(snackBar);
if (kDebugMode) {
debugPrint(
'showAwesomeSnackBar: shown via ${identical(messenger, globalMessenger) ? 'global' : 'local'} messenger',
);
}
} catch (e) {
if (kDebugMode) debugPrint('showAwesomeSnackBar: show failed: $e');
}
}
void showSuccessSnackBar(BuildContext context, String message) {
showSuccessSnackBarGlobal(message);
}
void showErrorSnackBar(BuildContext context, String message) {
showErrorSnackBarGlobal(message);
}
void showInfoSnackBar(BuildContext context, String message) {
showInfoSnackBarGlobal(message);
}
void showWarningSnackBar(BuildContext context, String message) {
showWarningSnackBarGlobal(message);
}
/// Global helpers that use the app-level `scaffoldMessengerKey` directly.
/// Use these from places where a valid `BuildContext` with a Scaffold may
/// not be available (e.g. dialog builders or background callbacks).
void showAwesomeSnackBarGlobal({
required String title,
required String message,
required SnackType snackType,
}) {
final gm = scaffoldMessengerKey.currentState;
if (gm == null) {
if (kDebugMode) {
debugPrint('showAwesomeSnackBarGlobal: global messenger null');
}
return;
}
final snackBar = SnackBar(
elevation: 0,
behavior: SnackBarBehavior.floating,
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
padding: EdgeInsets.zero,
backgroundColor: Colors.transparent,
content: Container(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 8),
child: AwesomeSnackbarContent(
title: title,
message: message,
contentType: _mapSnackType(snackType),
),
),
);
try {
gm
..hideCurrentSnackBar()
..showSnackBar(snackBar);
if (kDebugMode) {
debugPrint('showAwesomeSnackBarGlobal: shown');
}
} catch (e) {
if (kDebugMode) {
debugPrint('showAwesomeSnackBarGlobal: show failed: $e');
}
}
}
void showSuccessSnackBarGlobal(String message) => showAwesomeSnackBarGlobal(
title: 'Success',
message: message,
snackType: SnackType.success,
);
void showErrorSnackBarGlobal(String message) => showAwesomeSnackBarGlobal(
title: 'Error',
message: message,
snackType: SnackType.error,
);
void showInfoSnackBarGlobal(String message) => showAwesomeSnackBarGlobal(
title: 'Info',
message: message,
snackType: SnackType.info,
);
void showWarningSnackBarGlobal(String message) => showAwesomeSnackBarGlobal(
title: 'Warning',
message: message,
snackType: SnackType.warning,
);