201 lines
5.8 KiB
Dart
201 lines
5.8 KiB
Dart
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 package’s
|
||
/// [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,
|
||
);
|