Jonny Borges

prepare snackbar api to getx 5

... ... @@ -17,4 +17,3 @@ export 'src/routes/observers/route_observer.dart';
export 'src/routes/route_middleware.dart';
export 'src/routes/transitions_type.dart';
export 'src/snackbar/snack.dart';
export 'src/snackbar/snack_route.dart';
... ...
... ... @@ -11,243 +11,56 @@ import 'dialog/dialog_route.dart';
import 'root/parse_route.dart';
import 'root/root_controller.dart';
import 'routes/transitions_type.dart';
import 'snackbar/snackbar_controller.dart';
extension ExtensionSnackbar on GetInterface {
void rawSnackbar({
String? title,
String? message,
Widget? titleText,
Widget? messageText,
Widget? icon,
bool instantInit = true,
bool shouldIconPulse = true,
double? maxWidth,
EdgeInsets margin = const EdgeInsets.all(0.0),
EdgeInsets padding = const EdgeInsets.all(16),
double borderRadius = 0.0,
Color? borderColor,
double borderWidth = 1.0,
Color backgroundColor = const Color(0xFF303030),
Color? leftBarIndicatorColor,
List<BoxShadow>? boxShadows,
Gradient? backgroundGradient,
Widget? mainButton,
OnTap? onTap,
Duration duration = const Duration(seconds: 3),
bool isDismissible = true,
SnackDismissDirection dismissDirection = SnackDismissDirection.VERTICAL,
bool showProgressIndicator = false,
AnimationController? progressIndicatorController,
Color? progressIndicatorBackgroundColor,
Animation<Color>? progressIndicatorValueColor,
SnackPosition snackPosition = SnackPosition.BOTTOM,
SnackStyle snackStyle = SnackStyle.FLOATING,
Curve forwardAnimationCurve = Curves.easeOutCirc,
Curve reverseAnimationCurve = Curves.easeOutCirc,
Duration animationDuration = const Duration(seconds: 1),
SnackbarStatusCallback? snackbarStatus,
double? barBlur = 0.0,
double overlayBlur = 0.0,
Color? overlayColor,
Form? userInputForm,
}) async {
final getBar = GetBar(
snackbarStatus: snackbarStatus,
title: title,
message: message,
titleText: titleText,
messageText: messageText,
snackPosition: snackPosition,
borderRadius: borderRadius,
margin: margin,
duration: duration,
barBlur: barBlur,
backgroundColor: backgroundColor,
icon: icon,
shouldIconPulse: shouldIconPulse,
maxWidth: maxWidth,
padding: padding,
borderColor: borderColor,
borderWidth: borderWidth,
leftBarIndicatorColor: leftBarIndicatorColor,
boxShadows: boxShadows,
backgroundGradient: backgroundGradient,
mainButton: mainButton,
onTap: onTap,
isDismissible: isDismissible,
dismissDirection: dismissDirection,
showProgressIndicator: showProgressIndicator,
progressIndicatorController: progressIndicatorController,
progressIndicatorBackgroundColor: progressIndicatorBackgroundColor,
progressIndicatorValueColor: progressIndicatorValueColor,
snackStyle: snackStyle,
forwardAnimationCurve: forwardAnimationCurve,
reverseAnimationCurve: reverseAnimationCurve,
animationDuration: animationDuration,
overlayBlur: overlayBlur,
overlayColor: overlayColor,
userInputForm: userInputForm,
);
if (instantInit) {
getBar.show();
} else {
SchedulerBinding.instance!.addPostFrameCallback((_) {
getBar.show();
});
}
}
Future<T?>? showSnackbar<T>(GetBar snackbar) {
return key.currentState?.push(SnackRoute<T>(snack: snackbar));
}
void snackbar<T>(
String title,
String message, {
Color? colorText,
Duration? duration,
/// It replaces the Flutter Navigator, but needs no context.
/// You can to use navigator.push(YourRoute()) rather
/// Navigator.push(context, YourRoute());
NavigatorState? get navigator => GetNavigation(Get).key.currentState;
/// with instantInit = false you can put snackbar on initState
bool instantInit = true,
SnackPosition? snackPosition,
Widget? titleText,
Widget? messageText,
Widget? icon,
bool? shouldIconPulse,
double? maxWidth,
EdgeInsets? margin,
EdgeInsets? padding,
double? borderRadius,
Color? borderColor,
double? borderWidth,
extension ExtensionBottomSheet on GetInterface {
Future<T?> bottomSheet<T>(
Widget bottomsheet, {
Color? backgroundColor,
Color? leftBarIndicatorColor,
List<BoxShadow>? boxShadows,
Gradient? backgroundGradient,
TextButton? mainButton,
OnTap? onTap,
bool? isDismissible,
bool? showProgressIndicator,
SnackDismissDirection? dismissDirection,
AnimationController? progressIndicatorController,
Color? progressIndicatorBackgroundColor,
Animation<Color>? progressIndicatorValueColor,
SnackStyle? snackStyle,
Curve? forwardAnimationCurve,
Curve? reverseAnimationCurve,
Duration? animationDuration,
double? barBlur,
double? overlayBlur,
SnackbarStatusCallback? snackbarStatus,
Color? overlayColor,
Form? userInputForm,
}) async {
final getBar = GetBar(
snackbarStatus: snackbarStatus,
titleText: titleText ??
Text(
title,
style: TextStyle(
color: colorText ?? iconColor ?? Colors.black,
fontWeight: FontWeight.w800,
fontSize: 16,
),
),
messageText: messageText ??
Text(
message,
style: TextStyle(
color: colorText ?? iconColor ?? Colors.black,
fontWeight: FontWeight.w300,
fontSize: 14,
),
),
snackPosition: snackPosition ?? SnackPosition.TOP,
borderRadius: borderRadius ?? 15,
margin: margin ?? EdgeInsets.symmetric(horizontal: 10),
duration: duration ?? Duration(seconds: 3),
barBlur: barBlur ?? 7.0,
backgroundColor: backgroundColor ?? Colors.grey.withOpacity(0.2),
icon: icon,
shouldIconPulse: shouldIconPulse ?? true,
maxWidth: maxWidth,
padding: padding ?? EdgeInsets.all(16),
borderColor: borderColor,
borderWidth: borderWidth,
leftBarIndicatorColor: leftBarIndicatorColor,
boxShadows: boxShadows,
backgroundGradient: backgroundGradient,
mainButton: mainButton,
onTap: onTap,
isDismissible: isDismissible ?? true,
dismissDirection: dismissDirection ?? SnackDismissDirection.VERTICAL,
showProgressIndicator: showProgressIndicator ?? false,
progressIndicatorController: progressIndicatorController,
progressIndicatorBackgroundColor: progressIndicatorBackgroundColor,
progressIndicatorValueColor: progressIndicatorValueColor,
snackStyle: snackStyle ?? SnackStyle.FLOATING,
forwardAnimationCurve: forwardAnimationCurve ?? Curves.easeOutCirc,
reverseAnimationCurve: reverseAnimationCurve ?? Curves.easeOutCirc,
animationDuration: animationDuration ?? Duration(seconds: 1),
overlayBlur: overlayBlur ?? 0.0,
overlayColor: overlayColor ?? Colors.transparent,
userInputForm: userInputForm);
if (instantInit) {
showSnackbar<T>(getBar);
} else {
routing.isSnackbar = true;
SchedulerBinding.instance!.addPostFrameCallback((_) {
showSnackbar<T>(getBar);
});
}
}
}
extension OverlayExt on GetInterface {
Future<T> showOverlay<T>({
required Future<T> Function() asyncFunction,
Color opacityColor = Colors.black,
Widget? loadingWidget,
double opacity = .5,
}) async {
final navigatorState =
Navigator.of(Get.overlayContext!, rootNavigator: false);
final overlayState = navigatorState.overlay!;
final overlayEntryOpacity = OverlayEntry(builder: (context) {
return Opacity(
opacity: opacity,
child: Container(
color: opacityColor,
));
});
final overlayEntryLoader = OverlayEntry(builder: (context) {
return loadingWidget ??
Center(
child: Container(
height: 90,
width: 90,
child: Text('Loading...'),
));
});
overlayState.insert(overlayEntryOpacity);
overlayState.insert(overlayEntryLoader);
T data;
double? elevation,
bool persistent = true,
ShapeBorder? shape,
Clip? clipBehavior,
Color? barrierColor,
bool? ignoreSafeArea,
bool isScrollControlled = false,
bool useRootNavigator = false,
bool isDismissible = true,
bool enableDrag = true,
RouteSettings? settings,
Duration? enterBottomSheetDuration,
Duration? exitBottomSheetDuration,
}) {
return Navigator.of(overlayContext!, rootNavigator: useRootNavigator)
.push(GetModalBottomSheetRoute<T>(
builder: (_) => bottomsheet,
isPersistent: persistent,
// theme: Theme.of(key.currentContext, shadowThemeOnly: true),
theme: Theme.of(key.currentContext!),
isScrollControlled: isScrollControlled,
try {
data = await asyncFunction();
} on Exception catch (_) {
overlayEntryLoader.remove();
overlayEntryOpacity.remove();
rethrow;
}
barrierLabel: MaterialLocalizations.of(key.currentContext!)
.modalBarrierDismissLabel,
overlayEntryLoader.remove();
overlayEntryOpacity.remove();
return data;
backgroundColor: backgroundColor ?? Colors.transparent,
elevation: elevation,
shape: shape,
removeTop: ignoreSafeArea ?? true,
clipBehavior: clipBehavior,
isDismissible: isDismissible,
modalBarrierColor: barrierColor,
settings: settings,
enableDrag: enableDrag,
enterBottomSheetDuration:
enterBottomSheetDuration ?? const Duration(milliseconds: 250),
exitBottomSheetDuration:
exitBottomSheetDuration ?? const Duration(milliseconds: 200),
));
}
}
... ... @@ -378,7 +191,7 @@ extension ExtensionDialog on GetInterface {
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 8),
shape: RoundedRectangleBorder(
side: BorderSide(
color: buttonColor ?? theme.accentColor,
color: buttonColor ?? theme.colorScheme.secondary,
width: 2,
style: BorderStyle.solid),
borderRadius: BorderRadius.circular(100)),
... ... @@ -389,7 +202,8 @@ extension ExtensionDialog on GetInterface {
},
child: Text(
textCancel ?? "Cancel",
style: TextStyle(color: cancelTextColor ?? theme.accentColor),
style: TextStyle(
color: cancelTextColor ?? theme.colorScheme.secondary),
),
));
}
... ... @@ -397,113 +211,260 @@ extension ExtensionDialog on GetInterface {
if (confirm != null) {
actions.add(confirm);
} else {
if (leanConfirm) {
actions.add(TextButton(
style: TextButton.styleFrom(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
backgroundColor: buttonColor ?? theme.accentColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100)),
),
child: Text(
textConfirm ?? "Ok",
style:
TextStyle(color: confirmTextColor ?? theme.backgroundColor),
),
onPressed: () {
onConfirm?.call();
}));
}
if (leanConfirm) {
actions.add(TextButton(
style: TextButton.styleFrom(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
backgroundColor: buttonColor ?? theme.colorScheme.secondary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100)),
),
child: Text(
textConfirm ?? "Ok",
style:
TextStyle(color: confirmTextColor ?? theme.backgroundColor),
),
onPressed: () {
onConfirm?.call();
}));
}
}
Widget baseAlertDialog = AlertDialog(
titlePadding: titlePadding ?? EdgeInsets.all(8),
contentPadding: contentPadding ?? EdgeInsets.all(8),
backgroundColor: backgroundColor ?? theme.dialogBackgroundColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(radius))),
title: Text(title, textAlign: TextAlign.center, style: titleStyle),
content: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
content ??
Text(middleText,
textAlign: TextAlign.center, style: middleTextStyle),
SizedBox(height: 16),
ButtonTheme(
minWidth: 78.0,
height: 34.0,
child: Wrap(
alignment: WrapAlignment.center,
spacing: 8,
runSpacing: 8,
children: actions,
),
)
],
),
// actions: actions, // ?? <Widget>[cancelButton, confirmButton],
buttonPadding: EdgeInsets.zero,
);
return dialog<T>(
onWillPop != null
? WillPopScope(
onWillPop: onWillPop,
child: baseAlertDialog,
)
: baseAlertDialog,
barrierDismissible: barrierDismissible,
navigatorKey: navigatorKey,
);
}
}
extension ExtensionSnackbar on GetInterface {
void rawSnackbar({
String? title,
String? message,
Widget? titleText,
Widget? messageText,
Widget? icon,
bool instantInit = true,
bool shouldIconPulse = true,
double? maxWidth,
EdgeInsets margin = const EdgeInsets.all(0.0),
EdgeInsets padding = const EdgeInsets.all(16),
double borderRadius = 0.0,
Color? borderColor,
double borderWidth = 1.0,
Color backgroundColor = const Color(0xFF303030),
Color? leftBarIndicatorColor,
List<BoxShadow>? boxShadows,
Gradient? backgroundGradient,
Widget? mainButton,
OnTap? onTap,
Duration duration = const Duration(seconds: 3),
bool isDismissible = true,
SnackDismissDirection dismissDirection = SnackDismissDirection.VERTICAL,
bool showProgressIndicator = false,
AnimationController? progressIndicatorController,
Color? progressIndicatorBackgroundColor,
Animation<Color>? progressIndicatorValueColor,
SnackPosition snackPosition = SnackPosition.BOTTOM,
SnackStyle snackStyle = SnackStyle.FLOATING,
Curve forwardAnimationCurve = Curves.easeOutCirc,
Curve reverseAnimationCurve = Curves.easeOutCirc,
Duration animationDuration = const Duration(seconds: 1),
SnackbarStatusCallback? snackbarStatus,
double? barBlur = 0.0,
double overlayBlur = 0.0,
Color? overlayColor,
Form? userInputForm,
}) async {
final getBar = GetBar(
snackbarStatus: snackbarStatus,
title: title,
message: message,
titleText: titleText,
messageText: messageText,
snackPosition: snackPosition,
borderRadius: borderRadius,
margin: margin,
duration: duration,
barBlur: barBlur,
backgroundColor: backgroundColor,
icon: icon,
shouldIconPulse: shouldIconPulse,
maxWidth: maxWidth,
padding: padding,
borderColor: borderColor,
borderWidth: borderWidth,
leftBarIndicatorColor: leftBarIndicatorColor,
boxShadows: boxShadows,
backgroundGradient: backgroundGradient,
mainButton: mainButton,
onTap: onTap,
isDismissible: isDismissible,
dismissDirection: dismissDirection,
showProgressIndicator: showProgressIndicator,
progressIndicatorController: progressIndicatorController,
progressIndicatorBackgroundColor: progressIndicatorBackgroundColor,
progressIndicatorValueColor: progressIndicatorValueColor,
snackStyle: snackStyle,
forwardAnimationCurve: forwardAnimationCurve,
reverseAnimationCurve: reverseAnimationCurve,
animationDuration: animationDuration,
overlayBlur: overlayBlur,
overlayColor: overlayColor,
userInputForm: userInputForm,
);
if (instantInit) {
getBar.show();
} else {
SchedulerBinding.instance!.addPostFrameCallback((_) {
getBar.show();
});
}
}
Widget baseAlertDialog = AlertDialog(
titlePadding: titlePadding ?? EdgeInsets.all(8),
contentPadding: contentPadding ?? EdgeInsets.all(8),
backgroundColor: backgroundColor ?? theme.dialogBackgroundColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(radius))),
title: Text(title, textAlign: TextAlign.center, style: titleStyle),
content: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
content ??
Text(middleText,
textAlign: TextAlign.center, style: middleTextStyle),
SizedBox(height: 16),
ButtonTheme(
minWidth: 78.0,
height: 34.0,
child: Wrap(
alignment: WrapAlignment.center,
spacing: 8,
runSpacing: 8,
children: actions,
),
)
],
),
// actions: actions, // ?? <Widget>[cancelButton, confirmButton],
buttonPadding: EdgeInsets.zero,
);
return dialog<T>(
onWillPop != null
? WillPopScope(
onWillPop: onWillPop,
child: baseAlertDialog,
)
: baseAlertDialog,
barrierDismissible: barrierDismissible,
navigatorKey: navigatorKey,
);
Future<void> showSnackbar<T>(GetBar snackbar) {
return SnackbarController(snackbar).show();
}
}
extension ExtensionBottomSheet on GetInterface {
Future<T?> bottomSheet<T>(
Widget bottomsheet, {
Color? backgroundColor,
double? elevation,
bool persistent = true,
ShapeBorder? shape,
Clip? clipBehavior,
Color? barrierColor,
bool? ignoreSafeArea,
bool isScrollControlled = false,
bool useRootNavigator = false,
bool isDismissible = true,
bool enableDrag = true,
RouteSettings? settings,
Duration? enterBottomSheetDuration,
Duration? exitBottomSheetDuration,
}) {
return Navigator.of(overlayContext!, rootNavigator: useRootNavigator)
.push(GetModalBottomSheetRoute<T>(
builder: (_) => bottomsheet,
isPersistent: persistent,
// theme: Theme.of(key.currentContext, shadowThemeOnly: true),
theme: Theme.of(key.currentContext!),
isScrollControlled: isScrollControlled,
void snackbar<T>(
String title,
String message, {
Color? colorText,
Duration? duration,
barrierLabel: MaterialLocalizations.of(key.currentContext!)
.modalBarrierDismissLabel,
/// with instantInit = false you can put snackbar on initState
bool instantInit = true,
SnackPosition? snackPosition,
Widget? titleText,
Widget? messageText,
Widget? icon,
bool? shouldIconPulse,
double? maxWidth,
EdgeInsets? margin,
EdgeInsets? padding,
double? borderRadius,
Color? borderColor,
double? borderWidth,
Color? backgroundColor,
Color? leftBarIndicatorColor,
List<BoxShadow>? boxShadows,
Gradient? backgroundGradient,
TextButton? mainButton,
OnTap? onTap,
bool? isDismissible,
bool? showProgressIndicator,
SnackDismissDirection? dismissDirection,
AnimationController? progressIndicatorController,
Color? progressIndicatorBackgroundColor,
Animation<Color>? progressIndicatorValueColor,
SnackStyle? snackStyle,
Curve? forwardAnimationCurve,
Curve? reverseAnimationCurve,
Duration? animationDuration,
double? barBlur,
double? overlayBlur,
SnackbarStatusCallback? snackbarStatus,
Color? overlayColor,
Form? userInputForm,
}) async {
final getBar = GetBar(
snackbarStatus: snackbarStatus,
titleText: titleText ??
Text(
title,
style: TextStyle(
color: colorText ?? iconColor ?? Colors.black,
fontWeight: FontWeight.w800,
fontSize: 16,
),
),
messageText: messageText ??
Text(
message,
style: TextStyle(
color: colorText ?? iconColor ?? Colors.black,
fontWeight: FontWeight.w300,
fontSize: 14,
),
),
snackPosition: snackPosition ?? SnackPosition.TOP,
borderRadius: borderRadius ?? 15,
margin: margin ?? EdgeInsets.symmetric(horizontal: 10),
duration: duration ?? Duration(seconds: 3),
barBlur: barBlur ?? 7.0,
backgroundColor: backgroundColor ?? Colors.grey.withOpacity(0.2),
icon: icon,
shouldIconPulse: shouldIconPulse ?? true,
maxWidth: maxWidth,
padding: padding ?? EdgeInsets.all(16),
borderColor: borderColor,
borderWidth: borderWidth,
leftBarIndicatorColor: leftBarIndicatorColor,
boxShadows: boxShadows,
backgroundGradient: backgroundGradient,
mainButton: mainButton,
onTap: onTap,
isDismissible: isDismissible ?? true,
dismissDirection: dismissDirection ?? SnackDismissDirection.VERTICAL,
showProgressIndicator: showProgressIndicator ?? false,
progressIndicatorController: progressIndicatorController,
progressIndicatorBackgroundColor: progressIndicatorBackgroundColor,
progressIndicatorValueColor: progressIndicatorValueColor,
snackStyle: snackStyle ?? SnackStyle.FLOATING,
forwardAnimationCurve: forwardAnimationCurve ?? Curves.easeOutCirc,
reverseAnimationCurve: reverseAnimationCurve ?? Curves.easeOutCirc,
animationDuration: animationDuration ?? Duration(seconds: 1),
overlayBlur: overlayBlur ?? 0.0,
overlayColor: overlayColor ?? Colors.transparent,
userInputForm: userInputForm);
backgroundColor: backgroundColor ?? Colors.transparent,
elevation: elevation,
shape: shape,
removeTop: ignoreSafeArea ?? true,
clipBehavior: clipBehavior,
isDismissible: isDismissible,
modalBarrierColor: barrierColor,
settings: settings,
enableDrag: enableDrag,
enterBottomSheetDuration:
enterBottomSheetDuration ?? const Duration(milliseconds: 250),
exitBottomSheetDuration:
exitBottomSheetDuration ?? const Duration(milliseconds: 200),
));
if (instantInit) {
showSnackbar<T>(getBar);
} else {
//routing.isSnackbar = true;
SchedulerBinding.instance!.addPostFrameCallback((_) {
showSnackbar<T>(getBar);
});
}
}
}
... ... @@ -1134,8 +1095,9 @@ you can only use widgets and widget functions here''';
/// give name from previous route
String get previousRoute => routing.previous;
///TODO: made snackbar 2.0 trackeables
/// check if snackbar is open
bool? get isSnackbarOpen => routing.isSnackbar;
bool? get isSnackbarOpen => true; //routing.isSnackbar;
/// check if dialog is open
bool? get isDialogOpen => routing.isDialog;
... ... @@ -1341,7 +1303,48 @@ extension NavTwoExt on GetInterface {
}
}
/// It replaces the Flutter Navigator, but needs no context.
/// You can to use navigator.push(YourRoute()) rather
/// Navigator.push(context, YourRoute());
NavigatorState? get navigator => GetNavigation(Get).key.currentState;
extension OverlayExt on GetInterface {
Future<T> showOverlay<T>({
required Future<T> Function() asyncFunction,
Color opacityColor = Colors.black,
Widget? loadingWidget,
double opacity = .5,
}) async {
final navigatorState =
Navigator.of(Get.overlayContext!, rootNavigator: false);
final overlayState = navigatorState.overlay!;
final overlayEntryOpacity = OverlayEntry(builder: (context) {
return Opacity(
opacity: opacity,
child: Container(
color: opacityColor,
));
});
final overlayEntryLoader = OverlayEntry(builder: (context) {
return loadingWidget ??
Center(
child: Container(
height: 90,
width: 90,
child: Text('Loading...'),
));
});
overlayState.insert(overlayEntryOpacity);
overlayState.insert(overlayEntryLoader);
T data;
try {
data = await asyncFunction();
} on Exception catch (_) {
overlayEntryLoader.remove();
overlayEntryOpacity.remove();
rethrow;
}
overlayEntryLoader.remove();
overlayEntryOpacity.remove();
return data;
}
}
... ...
... ... @@ -10,6 +10,63 @@ import '../../get_navigation.dart';
import 'root_controller.dart';
class GetMaterialApp extends StatelessWidget {
final GlobalKey<NavigatorState>? navigatorKey;
final GlobalKey<ScaffoldMessengerState>? scaffoldMessengerKey;
final Widget? home;
final Map<String, WidgetBuilder>? routes;
final String? initialRoute;
final RouteFactory? onGenerateRoute;
final InitialRouteListFactory? onGenerateInitialRoutes;
final RouteFactory? onUnknownRoute;
final List<NavigatorObserver>? navigatorObservers;
final TransitionBuilder? builder;
final String title;
final GenerateAppTitle? onGenerateTitle;
final ThemeData? theme;
final ThemeData? darkTheme;
final ThemeMode themeMode;
final CustomTransition? customTransition;
final Color? color;
final Map<String, Map<String, String>>? translationsKeys;
final Translations? translations;
final TextDirection? textDirection;
final Locale? locale;
final Locale? fallbackLocale;
final Iterable<LocalizationsDelegate<dynamic>>? localizationsDelegates;
final LocaleListResolutionCallback? localeListResolutionCallback;
final LocaleResolutionCallback? localeResolutionCallback;
final Iterable<Locale> supportedLocales;
final bool showPerformanceOverlay;
final bool checkerboardRasterCacheImages;
final bool checkerboardOffscreenLayers;
final bool showSemanticsDebugger;
final bool debugShowCheckedModeBanner;
final Map<LogicalKeySet, Intent>? shortcuts;
final ScrollBehavior? scrollBehavior;
final ThemeData? highContrastTheme;
final ThemeData? highContrastDarkTheme;
final Map<Type, Action<Intent>>? actions;
final bool debugShowMaterialGrid;
final ValueChanged<Routing?>? routingCallback;
final Transition? defaultTransition;
final bool? opaqueRoute;
final VoidCallback? onInit;
final VoidCallback? onReady;
final VoidCallback? onDispose;
final bool? enableLog;
final LogWriterCallback? logWriterCallback;
final bool? popGesture;
final SmartManagement smartManagement;
final Bindings? initialBinding;
final Duration? transitionDuration;
final bool? defaultGlobalState;
final List<GetPage>? getPages;
final GetPage? unknownRoute;
final RouteInformationProvider? routeInformationProvider;
final RouteInformationParser<Object>? routeInformationParser;
final RouterDelegate<Object>? routerDelegate;
final BackButtonDispatcher? backButtonDispatcher;
const GetMaterialApp({
Key? key,
this.navigatorKey,
... ... @@ -72,63 +129,6 @@ class GetMaterialApp extends StatelessWidget {
backButtonDispatcher = null,
super(key: key);
final GlobalKey<NavigatorState>? navigatorKey;
final GlobalKey<ScaffoldMessengerState>? scaffoldMessengerKey;
final Widget? home;
final Map<String, WidgetBuilder>? routes;
final String? initialRoute;
final RouteFactory? onGenerateRoute;
final InitialRouteListFactory? onGenerateInitialRoutes;
final RouteFactory? onUnknownRoute;
final List<NavigatorObserver>? navigatorObservers;
final TransitionBuilder? builder;
final String title;
final GenerateAppTitle? onGenerateTitle;
final ThemeData? theme;
final ThemeData? darkTheme;
final ThemeMode themeMode;
final CustomTransition? customTransition;
final Color? color;
final Map<String, Map<String, String>>? translationsKeys;
final Translations? translations;
final TextDirection? textDirection;
final Locale? locale;
final Locale? fallbackLocale;
final Iterable<LocalizationsDelegate<dynamic>>? localizationsDelegates;
final LocaleListResolutionCallback? localeListResolutionCallback;
final LocaleResolutionCallback? localeResolutionCallback;
final Iterable<Locale> supportedLocales;
final bool showPerformanceOverlay;
final bool checkerboardRasterCacheImages;
final bool checkerboardOffscreenLayers;
final bool showSemanticsDebugger;
final bool debugShowCheckedModeBanner;
final Map<LogicalKeySet, Intent>? shortcuts;
final ScrollBehavior? scrollBehavior;
final ThemeData? highContrastTheme;
final ThemeData? highContrastDarkTheme;
final Map<Type, Action<Intent>>? actions;
final bool debugShowMaterialGrid;
final ValueChanged<Routing?>? routingCallback;
final Transition? defaultTransition;
final bool? opaqueRoute;
final VoidCallback? onInit;
final VoidCallback? onReady;
final VoidCallback? onDispose;
final bool? enableLog;
final LogWriterCallback? logWriterCallback;
final bool? popGesture;
final SmartManagement smartManagement;
final Bindings? initialBinding;
final Duration? transitionDuration;
final bool? defaultGlobalState;
final List<GetPage>? getPages;
final GetPage? unknownRoute;
final RouteInformationProvider? routeInformationProvider;
final RouteInformationParser<Object>? routeInformationParser;
final RouterDelegate<Object>? routerDelegate;
final BackButtonDispatcher? backButtonDispatcher;
GetMaterialApp.router({
Key? key,
this.routeInformationProvider,
... ... @@ -200,31 +200,6 @@ class GetMaterialApp extends StatelessWidget {
Get.routeInformationParser = routeInformationParser;
}
Route<dynamic> generator(RouteSettings settings) {
return PageRedirect(settings: settings, unknownRoute: unknownRoute).page();
}
List<Route<dynamic>> initialRoutesGenerate(String name) {
return [
PageRedirect(
settings: RouteSettings(name: name),
unknownRoute: unknownRoute,
).page()
];
}
Widget defaultBuilder(BuildContext context, Widget? child) {
return Directionality(
textDirection: textDirection ??
(rtlLanguages.contains(Get.locale?.languageCode)
? TextDirection.rtl
: TextDirection.ltr),
child: builder == null
? (child ?? Material())
: builder!(context, child ?? Material()),
);
}
@override
Widget build(BuildContext context) => GetBuilder<GetMaterialController>(
init: Get.rootController,
... ... @@ -270,7 +245,6 @@ class GetMaterialApp extends StatelessWidget {
? MaterialApp.router(
routerDelegate: routerDelegate!,
routeInformationParser: routeInformationParser!,
scaffoldMessengerKey: scaffoldMessengerKey,
backButtonDispatcher: backButtonDispatcher,
routeInformationProvider: routeInformationProvider,
key: _.unikey,
... ... @@ -283,6 +257,8 @@ class GetMaterialApp extends StatelessWidget {
_.darkTheme ?? darkTheme ?? theme ?? ThemeData.fallback(),
themeMode: _.themeMode ?? themeMode,
locale: Get.locale ?? locale,
scaffoldMessengerKey:
scaffoldMessengerKey ?? _.scaffoldMessengerKey,
localizationsDelegates: localizationsDelegates,
localeListResolutionCallback: localeListResolutionCallback,
localeResolutionCallback: localeResolutionCallback,
... ... @@ -301,7 +277,8 @@ class GetMaterialApp extends StatelessWidget {
navigatorKey: (navigatorKey == null
? Get.key
: Get.addKey(navigatorKey!)),
scaffoldMessengerKey: scaffoldMessengerKey,
scaffoldMessengerKey:
scaffoldMessengerKey ?? _.scaffoldMessengerKey,
home: home,
routes: routes ?? const <String, WidgetBuilder>{},
initialRoute: initialRoute,
... ... @@ -343,4 +320,29 @@ class GetMaterialApp extends StatelessWidget {
// actions: actions,
),
);
Widget defaultBuilder(BuildContext context, Widget? child) {
return Directionality(
textDirection: textDirection ??
(rtlLanguages.contains(Get.locale?.languageCode)
? TextDirection.rtl
: TextDirection.ltr),
child: builder == null
? (child ?? Material())
: builder!(context, child ?? Material()),
);
}
Route<dynamic> generator(RouteSettings settings) {
return PageRedirect(settings: settings, unknownRoute: unknownRoute).page();
}
List<Route<dynamic>> initialRoutesGenerate(String name) {
return [
PageRedirect(
settings: RouteSettings(name: name),
unknownRoute: unknownRoute,
).page()
];
}
}
... ...
import 'package:flutter/material.dart';
import '../../../get_state_manager/get_state_manager.dart';
import '../../../get_utils/get_utils.dart';
import '../routes/custom_transition.dart';
... ... @@ -12,6 +13,8 @@ class GetMaterialController extends GetxController {
ThemeData? darkTheme;
ThemeMode? themeMode;
final scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
bool defaultPopGesture = GetPlatform.isIOS;
bool defaultOpaqueRoute = true;
... ... @@ -31,6 +34,8 @@ class GetMaterialController extends GetxController {
var _key = GlobalKey<NavigatorState>(debugLabel: 'Key Created by default');
Map<dynamic, GlobalKey<NavigatorState>> keys = {};
GlobalKey<NavigatorState> get key => _key;
GlobalKey<NavigatorState>? addKey(GlobalKey<NavigatorState> newKey) {
... ... @@ -38,7 +43,10 @@ class GetMaterialController extends GetxController {
return key;
}
Map<dynamic, GlobalKey<NavigatorState>> keys = {};
void restartApp() {
unikey = UniqueKey();
update();
}
void setTheme(ThemeData value) {
if (darkTheme == null) {
... ... @@ -57,9 +65,4 @@ class GetMaterialController extends GetxController {
themeMode = value;
update();
}
void restartApp() {
unikey = UniqueKey();
update();
}
}
... ...
... ... @@ -5,37 +5,8 @@ import '../../../../instance_manager.dart';
import '../../../get_navigation.dart';
import '../../dialog/dialog_route.dart';
import '../../router_report.dart';
import '../../snackbar/snack_route.dart';
import '../default_route.dart';
class Routing {
String current;
String previous;
dynamic args;
String removed;
Route<dynamic>? route;
bool? isBack;
bool? isSnackbar;
bool? isBottomSheet;
bool? isDialog;
Routing({
this.current = '',
this.previous = '',
this.args,
this.removed = '',
this.route,
this.isBack,
this.isSnackbar,
this.isBottomSheet,
this.isDialog,
});
void update(void fn(Routing value)) {
fn(this);
}
}
/// Extracts the name of a route based on it's instance type
/// or null if not possible.
String? _extractRouteName(Route? route) {
... ... @@ -58,49 +29,70 @@ String? _extractRouteName(Route? route) {
return null;
}
/// This is basically a util for rules about 'what a route is'
class _RouteData {
final bool isGetPageRoute;
final bool isSnackbar;
final bool isBottomSheet;
final bool isDialog;
final String? name;
_RouteData({
required this.name,
required this.isGetPageRoute,
required this.isSnackbar,
required this.isBottomSheet,
required this.isDialog,
});
factory _RouteData.ofRoute(Route? route) {
return _RouteData(
name: _extractRouteName(route),
isGetPageRoute: route is GetPageRoute,
isSnackbar: route is SnackRoute,
isDialog: route is GetDialogRoute,
isBottomSheet: route is GetModalBottomSheetRoute,
);
}
}
class GetObserver extends NavigatorObserver {
final Function(Routing?)? routing;
final Routing? _routeSend;
GetObserver([this.routing, this._routeSend]);
final Routing? _routeSend;
@override
void didPop(Route route, Route? previousRoute) {
super.didPop(route, previousRoute);
final currentRoute = _RouteData.ofRoute(route);
final newRoute = _RouteData.ofRoute(previousRoute);
// if (currentRoute.isSnackbar) {
// // Get.log("CLOSE SNACKBAR ${currentRoute.name}");
// Get.log("CLOSE SNACKBAR");
// } else
if (currentRoute.isBottomSheet || currentRoute.isDialog) {
Get.log("CLOSE ${currentRoute.name}");
} else if (currentRoute.isGetPageRoute) {
Get.log("CLOSE TO ROUTE ${currentRoute.name}");
}
if (previousRoute != null) {
RouterReportManager.reportCurrentRoute(previousRoute);
}
// Here we use a 'inverse didPush set', meaning that we use
// previous route instead of 'route' because this is
// a 'inverse push'
_routeSend?.update((value) {
// Only PageRoute is allowed to change current value
if (previousRoute is PageRoute) {
value.current = _extractRouteName(previousRoute) ?? '';
value.previous = newRoute.name ?? '';
} else if (value.previous.isNotEmpty) {
value.current = value.previous;
}
value.args = previousRoute?.settings.arguments;
value.route = previousRoute;
value.isBack = true;
value.removed = '';
// value.isSnackbar = newRoute.isSnackbar;
value.isBottomSheet = newRoute.isBottomSheet;
value.isDialog = newRoute.isDialog;
});
// print('currentRoute.isDialog ${currentRoute.isDialog}');
routing?.call(_routeSend);
}
@override
void didPush(Route route, Route? previousRoute) {
super.didPush(route, previousRoute);
final newRoute = _RouteData.ofRoute(route);
if (newRoute.isSnackbar) {
// Get.log("OPEN SNACKBAR ${newRoute.name}");
Get.log("OPEN SNACKBAR");
} else if (newRoute.isBottomSheet || newRoute.isDialog) {
// if (newRoute.isSnackbar) {
// // Get.log("OPEN SNACKBAR ${newRoute.name}");
// Get.log("OPEN SNACKBAR");
// } else
if (newRoute.isBottomSheet || newRoute.isDialog) {
Get.log("OPEN ${newRoute.name}");
} else if (newRoute.isGetPageRoute) {
Get.log("GOING TO ROUTE ${newRoute.name}");
... ... @@ -121,7 +113,7 @@ class GetObserver extends NavigatorObserver {
value.route = route;
value.isBack = false;
value.removed = '';
value.isSnackbar = newRoute.isSnackbar ? true : value.isSnackbar ?? false;
// value.isSnackbar = newRoute.isSnackbar ? true : value.isSnackbar ?? false;
value.isBottomSheet =
newRoute.isBottomSheet ? true : value.isBottomSheet ?? false;
value.isDialog = newRoute.isDialog ? true : value.isDialog ?? false;
... ... @@ -133,46 +125,27 @@ class GetObserver extends NavigatorObserver {
}
@override
void didPop(Route route, Route? previousRoute) {
super.didPop(route, previousRoute);
void didRemove(Route route, Route? previousRoute) {
super.didRemove(route, previousRoute);
final routeName = _extractRouteName(route);
final currentRoute = _RouteData.ofRoute(route);
final newRoute = _RouteData.ofRoute(previousRoute);
if (currentRoute.isSnackbar) {
// Get.log("CLOSE SNACKBAR ${currentRoute.name}");
Get.log("CLOSE SNACKBAR");
} else if (currentRoute.isBottomSheet || currentRoute.isDialog) {
Get.log("CLOSE ${currentRoute.name}");
} else if (currentRoute.isGetPageRoute) {
Get.log("CLOSE TO ROUTE ${currentRoute.name}");
}
if (previousRoute != null) {
RouterReportManager.reportCurrentRoute(previousRoute);
}
Get.log("REMOVING ROUTE $routeName");
// Here we use a 'inverse didPush set', meaning that we use
// previous route instead of 'route' because this is
// a 'inverse push'
_routeSend?.update((value) {
// Only PageRoute is allowed to change current value
if (previousRoute is PageRoute) {
value.current = _extractRouteName(previousRoute) ?? '';
value.previous = newRoute.name ?? '';
} else if (value.previous.isNotEmpty) {
value.current = value.previous;
}
value.args = previousRoute?.settings.arguments;
value.route = previousRoute;
value.isBack = true;
value.removed = '';
value.isSnackbar = newRoute.isSnackbar;
value.isBottomSheet = newRoute.isBottomSheet;
value.isDialog = newRoute.isDialog;
value.isBack = false;
value.removed = routeName ?? '';
value.previous = routeName ?? '';
// value.isSnackbar = currentRoute.isSnackbar ? false : value.isSnackbar;
value.isBottomSheet =
currentRoute.isBottomSheet ? false : value.isBottomSheet;
value.isDialog = currentRoute.isDialog ? false : value.isDialog;
});
// print('currentRoute.isDialog ${currentRoute.isDialog}');
if (route is GetPageRoute) {
RouterReportManager.reportRouteWillDispose(route);
}
routing?.call(_routeSend);
}
... ... @@ -201,7 +174,7 @@ class GetObserver extends NavigatorObserver {
value.isBack = false;
value.removed = '';
value.previous = '$oldName';
value.isSnackbar = currentRoute.isSnackbar ? false : value.isSnackbar;
// value.isSnackbar = currentRoute.isSnackbar ? false : value.isSnackbar;
value.isBottomSheet =
currentRoute.isBottomSheet ? false : value.isBottomSheet;
value.isDialog = currentRoute.isDialog ? false : value.isDialog;
... ... @@ -212,29 +185,59 @@ class GetObserver extends NavigatorObserver {
routing?.call(_routeSend);
}
}
@override
void didRemove(Route route, Route? previousRoute) {
super.didRemove(route, previousRoute);
final routeName = _extractRouteName(route);
final currentRoute = _RouteData.ofRoute(route);
class Routing {
String current;
String previous;
dynamic args;
String removed;
Route<dynamic>? route;
bool? isBack;
// bool? isSnackbar;
bool? isBottomSheet;
bool? isDialog;
Get.log("REMOVING ROUTE $routeName");
Routing({
this.current = '',
this.previous = '',
this.args,
this.removed = '',
this.route,
this.isBack,
// this.isSnackbar,
this.isBottomSheet,
this.isDialog,
});
_routeSend?.update((value) {
value.route = previousRoute;
value.isBack = false;
value.removed = routeName ?? '';
value.previous = routeName ?? '';
value.isSnackbar = currentRoute.isSnackbar ? false : value.isSnackbar;
value.isBottomSheet =
currentRoute.isBottomSheet ? false : value.isBottomSheet;
value.isDialog = currentRoute.isDialog ? false : value.isDialog;
});
void update(void fn(Routing value)) {
fn(this);
}
}
if (route is GetPageRoute) {
RouterReportManager.reportRouteWillDispose(route);
}
routing?.call(_routeSend);
/// This is basically a util for rules about 'what a route is'
class _RouteData {
final bool isGetPageRoute;
//final bool isSnackbar;
final bool isBottomSheet;
final bool isDialog;
final String? name;
_RouteData({
required this.name,
required this.isGetPageRoute,
// required this.isSnackbar,
required this.isBottomSheet,
required this.isDialog,
});
factory _RouteData.ofRoute(Route? route) {
return _RouteData(
name: _extractRouteName(route),
isGetPageRoute: route is GetPageRoute,
// isSnackbar: route is SnackRoute,
isDialog: route is GetDialogRoute,
isBottomSheet: route is GetModalBottomSheetRoute,
);
}
}
... ...
import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import '../../../get_core/get_core.dart';
import '../../get_navigation.dart';
typedef SnackbarStatusCallback = void Function(SnackbarStatus? status);
typedef OnTap = void Function(GetBar snack);
typedef SnackbarStatusCallback = void Function(SnackbarStatus? status);
class GetBar<T extends Object> extends StatefulWidget {
GetBar({
Key? key,
this.title,
this.message,
this.titleText,
this.messageText,
this.icon,
this.shouldIconPulse = true,
this.maxWidth,
this.margin = const EdgeInsets.all(0.0),
this.padding = const EdgeInsets.all(16),
this.borderRadius = 0.0,
this.borderColor,
this.borderWidth = 1.0,
this.backgroundColor = const Color(0xFF303030),
this.leftBarIndicatorColor,
this.boxShadows,
this.backgroundGradient,
this.mainButton,
this.onTap,
this.duration,
this.isDismissible = true,
this.dismissDirection = SnackDismissDirection.VERTICAL,
this.showProgressIndicator = false,
this.progressIndicatorController,
this.progressIndicatorBackgroundColor,
this.progressIndicatorValueColor,
this.snackPosition = SnackPosition.BOTTOM,
this.snackStyle = SnackStyle.FLOATING,
this.forwardAnimationCurve = Curves.easeOutCirc,
this.reverseAnimationCurve = Curves.easeOutCirc,
this.animationDuration = const Duration(seconds: 1),
this.barBlur = 0.0,
this.overlayBlur = 0.0,
this.overlayColor = Colors.transparent,
this.userInputForm,
SnackbarStatusCallback? snackbarStatus,
}) : snackbarStatus = (snackbarStatus ?? (status) {}),
super(key: key);
/// A callback for you to listen to the different Snack status
final SnackbarStatusCallback snackbarStatus;
final SnackbarStatusCallback? snackbarStatus;
/// The title displayed to the user
final String? title;
... ... @@ -192,18 +154,78 @@ class GetBar<T extends Object> extends StatefulWidget {
/// Every other widget is ignored if this is not null.
final Form? userInputForm;
/// Show the snack. It's call [SnackbarStatus.OPENING] state
/// followed by [SnackbarStatus.OPEN]
Future<T?>? show<T>() async {
return Get.showSnackbar(this);
}
const GetBar({
Key? key,
this.title,
this.message,
this.titleText,
this.messageText,
this.icon,
this.shouldIconPulse = true,
this.maxWidth,
this.margin = const EdgeInsets.all(0.0),
this.padding = const EdgeInsets.all(16),
this.borderRadius = 0.0,
this.borderColor,
this.borderWidth = 1.0,
this.backgroundColor = const Color(0xFF303030),
this.leftBarIndicatorColor,
this.boxShadows,
this.backgroundGradient,
this.mainButton,
this.onTap,
this.duration,
this.isDismissible = true,
this.dismissDirection = SnackDismissDirection.VERTICAL,
this.showProgressIndicator = false,
this.progressIndicatorController,
this.progressIndicatorBackgroundColor,
this.progressIndicatorValueColor,
this.snackPosition = SnackPosition.BOTTOM,
this.snackStyle = SnackStyle.FLOATING,
this.forwardAnimationCurve = Curves.easeOutCirc,
this.reverseAnimationCurve = Curves.easeOutCirc,
this.animationDuration = const Duration(seconds: 1),
this.barBlur = 0.0,
this.overlayBlur = 0.0,
this.overlayColor = Colors.transparent,
this.userInputForm,
this.snackbarStatus,
}) : super(key: key);
@override
State createState() {
return _GetBarState<T>();
}
/// Show the snack. It's call [SnackbarStatus.OPENING] state
/// followed by [SnackbarStatus.OPEN]
Future<void> show<T>() async {
return Get.showSnackbar(this);
}
}
/// Indicates Status of snackbar
/// [SnackbarStatus.OPEN] Snack is fully open, [SnackbarStatus.CLOSED] Snackbar
/// has closed,
/// [SnackbarStatus.OPENING] Starts with the opening animation and ends
/// with the full
/// snackbar display, [SnackbarStatus.CLOSING] Starts with the closing animation
/// and ends
/// with the full snackbar dispose
enum SnackbarStatus { OPEN, CLOSED, OPENING, CLOSING }
/// Indicates the direction in which it is possible to dismiss
/// If vertical, dismiss up will be allowed if [SnackPosition.TOP]
/// If vertical, dismiss down will be allowed if [SnackPosition.BOTTOM]
enum SnackDismissDirection { HORIZONTAL, VERTICAL }
/// Indicates if snack is going to start at the [TOP] or at the [BOTTOM]
enum SnackPosition { TOP, BOTTOM }
/// Indicates if snack will be attached to the edge of the screen or not
enum SnackStyle { FLOATING, GROUNDED }
class _GetBarState<K extends Object> extends State<GetBar>
with TickerProviderStateMixin {
SnackbarStatus? currentStatus;
... ... @@ -223,6 +245,49 @@ class _GetBarState<K extends Object> extends State<GetBar>
FocusScopeNode? _focusNode;
late FocusAttachment _focusAttachment;
final Completer<Size> _boxHeightCompleter = Completer<Size>();
late VoidCallback _progressListener;
late CurvedAnimation _progressAnimation;
GlobalKey backgroundBoxKey = GlobalKey();
@override
Widget build(BuildContext context) {
return Align(
heightFactor: 1.0,
child: Material(
color: widget.snackStyle == SnackStyle.FLOATING
? Colors.transparent
: widget.backgroundColor,
child: SafeArea(
minimum: widget.snackPosition == SnackPosition.BOTTOM
? EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom)
: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
bottom: widget.snackPosition == SnackPosition.BOTTOM,
top: widget.snackPosition == SnackPosition.TOP,
left: false,
right: false,
child: _getSnack(),
),
),
);
}
@override
void dispose() {
_fadeController?.dispose();
widget.progressIndicatorController?.removeListener(_progressListener);
widget.progressIndicatorController?.dispose();
_focusAttachment.detach();
_focusNode!.dispose();
super.dispose();
}
@override
void initState() {
super.initState();
... ... @@ -250,20 +315,27 @@ Set either a message or messageText""");
_focusAttachment = _focusNode!.attach(context);
}
@override
void dispose() {
_fadeController?.dispose();
widget.progressIndicatorController?.removeListener(_progressListener);
widget.progressIndicatorController?.dispose();
_focusAttachment.detach();
_focusNode!.dispose();
super.dispose();
Widget _buildLeftBarIndicator() {
if (widget.leftBarIndicatorColor != null) {
return FutureBuilder<Size>(
future: _boxHeightCompleter.future,
builder: (buildContext, snapshot) {
if (snapshot.hasData) {
return Container(
color: widget.leftBarIndicatorColor,
width: 5.0,
height: snapshot.data!.height,
);
} else {
return _emptyWidget;
}
},
);
} else {
return _emptyWidget;
}
}
final Completer<Size> _boxHeightCompleter = Completer<Size>();
void _configureLeftBarFuture() {
SchedulerBinding.instance!.addPostFrameCallback(
(_) {
... ... @@ -277,6 +349,19 @@ Set either a message or messageText""");
);
}
void _configureProgressIndicatorAnimation() {
if (widget.showProgressIndicator &&
widget.progressIndicatorController != null) {
_progressListener = () {
setState(() {});
};
widget.progressIndicatorController!.addListener(_progressListener);
_progressAnimation = CurvedAnimation(
curve: Curves.linear, parent: widget.progressIndicatorController!);
}
}
void _configurePulseAnimation() {
_fadeController =
AnimationController(vsync: this, duration: _pulseAnimationDuration);
... ... @@ -299,92 +384,6 @@ Set either a message or messageText""");
_fadeController!.forward();
}
late VoidCallback _progressListener;
void _configureProgressIndicatorAnimation() {
if (widget.showProgressIndicator &&
widget.progressIndicatorController != null) {
_progressListener = () {
setState(() {});
};
widget.progressIndicatorController!.addListener(_progressListener);
_progressAnimation = CurvedAnimation(
curve: Curves.linear, parent: widget.progressIndicatorController!);
}
}
@override
Widget build(BuildContext context) {
return Align(
heightFactor: 1.0,
child: Material(
color: widget.snackStyle == SnackStyle.FLOATING
? Colors.transparent
: widget.backgroundColor,
child: SafeArea(
minimum: widget.snackPosition == SnackPosition.BOTTOM
? EdgeInsets.only(
// bottom: (GetUtils.isGreaterThan(
// MediaQuery.of(context).viewInsets.bottom,
// MediaQuery.of(context).padding.bottom)
// ? MediaQuery.of(context).viewInsets.bottom
// : MediaQuery.of(context).padding.bottom))
bottom: MediaQuery.of(context).viewInsets.bottom)
: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
bottom: widget.snackPosition == SnackPosition.BOTTOM,
top: widget.snackPosition == SnackPosition.TOP,
left: false,
right: false,
child: _getSnack(),
),
),
);
}
Widget _getSnack() {
Widget snack;
if (widget.userInputForm != null) {
snack = _generateInputSnack();
} else {
snack = _generateSnack();
}
return Stack(
children: [
FutureBuilder<Size>(
future: _boxHeightCompleter.future,
builder: (context, snapshot) {
if (snapshot.hasData) {
if (widget.barBlur == 0) {
return _emptyWidget;
}
return ClipRRect(
borderRadius: BorderRadius.circular(widget.borderRadius),
child: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: widget.barBlur!, sigmaY: widget.barBlur!),
child: Container(
height: snapshot.data!.height,
width: snapshot.data!.width,
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.circular(widget.borderRadius),
),
),
),
);
} else {
return _emptyWidget;
}
},
),
snack,
],
);
}
Widget _generateInputSnack() {
return Container(
key: backgroundBoxKey,
... ... @@ -412,9 +411,6 @@ Set either a message or messageText""");
);
}
late CurvedAnimation _progressAnimation;
GlobalKey backgroundBoxKey = GlobalKey();
Widget _generateSnack() {
return Container(
key: backgroundBoxKey,
... ... @@ -611,25 +607,11 @@ Set either a message or messageText""");
}
}
Widget _buildLeftBarIndicator() {
if (widget.leftBarIndicatorColor != null) {
return FutureBuilder<Size>(
future: _boxHeightCompleter.future,
builder: (buildContext, snapshot) {
if (snapshot.hasData) {
return Container(
color: widget.leftBarIndicatorColor,
width: 5.0,
height: snapshot.data!.height,
);
} else {
return _emptyWidget;
}
},
);
} else {
return _emptyWidget;
}
Text _getDefaultNotificationText() {
return Text(
widget.message ?? "",
style: TextStyle(fontSize: 14.0, color: Colors.white),
);
}
Widget? _getIcon() {
... ... @@ -645,6 +627,53 @@ Set either a message or messageText""");
}
}
Widget? _getMainActionButton() {
return widget.mainButton;
}
Widget _getSnack() {
Widget snack;
if (widget.userInputForm != null) {
snack = _generateInputSnack();
} else {
snack = _generateSnack();
}
return Stack(
children: [
FutureBuilder<Size>(
future: _boxHeightCompleter.future,
builder: (context, snapshot) {
if (snapshot.hasData) {
if (widget.barBlur == 0) {
return _emptyWidget;
}
return ClipRRect(
borderRadius: BorderRadius.circular(widget.borderRadius),
child: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: widget.barBlur!, sigmaY: widget.barBlur!),
child: Container(
height: snapshot.data!.height,
width: snapshot.data!.width,
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.circular(widget.borderRadius),
),
),
),
);
} else {
return _emptyWidget;
}
},
),
snack,
],
);
}
Widget _getTitleText() {
return widget.titleText ??
Text(
... ... @@ -653,36 +682,4 @@ Set either a message or messageText""");
fontSize: 16.0, color: Colors.white, fontWeight: FontWeight.bold),
);
}
Text _getDefaultNotificationText() {
return Text(
widget.message ?? "",
style: TextStyle(fontSize: 14.0, color: Colors.white),
);
}
Widget? _getMainActionButton() {
return widget.mainButton;
}
}
/// Indicates if snack is going to start at the [TOP] or at the [BOTTOM]
enum SnackPosition { TOP, BOTTOM }
/// Indicates if snack will be attached to the edge of the screen or not
enum SnackStyle { FLOATING, GROUNDED }
/// Indicates the direction in which it is possible to dismiss
/// If vertical, dismiss up will be allowed if [SnackPosition.TOP]
/// If vertical, dismiss down will be allowed if [SnackPosition.BOTTOM]
enum SnackDismissDirection { HORIZONTAL, VERTICAL }
/// Indicates Status of snackbar
/// [SnackbarStatus.OPEN] Snack is fully open, [SnackbarStatus.CLOSED] Snackbar
/// has closed,
/// [SnackbarStatus.OPENING] Starts with the opening animation and ends
/// with the full
/// snackbar display, [SnackbarStatus.CLOSING] Starts with the closing animation
/// and ends
/// with the full snackbar dispose
enum SnackbarStatus { OPEN, CLOSED, OPENING, CLOSING }
... ...
import 'dart:async';
import 'dart:ui';
import 'package:flutter/widgets.dart';
import '../../../get_core/get_core.dart';
import '../../get_navigation.dart';
import 'snack.dart';
class SnackRoute<T> extends OverlayRoute<T> {
late Animation<double> _filterBlurAnimation;
late Animation<Color?> _filterColorAnimation;
SnackRoute({
required this.snack,
RouteSettings? settings,
}) : super(settings: settings) {
_builder = Builder(builder: (_) {
return GestureDetector(
child: snack,
onTap: snack.onTap != null ? () => snack.onTap!(snack) : null,
);
});
_configureAlignment(snack.snackPosition);
_snackbarStatus = snack.snackbarStatus;
}
_configureAlignment(SnackPosition snackPosition) {
switch (snack.snackPosition) {
case SnackPosition.TOP:
{
_initialAlignment = Alignment(-1.0, -2.0);
_endAlignment = Alignment(-1.0, -1.0);
break;
}
case SnackPosition.BOTTOM:
{
_initialAlignment = Alignment(-1.0, 2.0);
_endAlignment = Alignment(-1.0, 1.0);
break;
}
}
}
GetBar snack;
Builder? _builder;
final Completer<T> _transitionCompleter = Completer<T>();
late SnackbarStatusCallback _snackbarStatus;
Alignment? _initialAlignment;
Alignment? _endAlignment;
bool _wasDismissedBySwipe = false;
bool _onTappedDismiss = false;
Timer? _timer;
bool get opaque => false;
@override
Iterable<OverlayEntry> createOverlayEntries() {
return <OverlayEntry>[
if (snack.overlayBlur > 0.0) ...[
OverlayEntry(
builder: (context) {
return GestureDetector(
onTap: () {
if (snack.isDismissible && !_onTappedDismiss) {
_onTappedDismiss = true;
Get.back();
}
},
child: AnimatedBuilder(
animation: _filterBlurAnimation,
builder: (context, child) {
return BackdropFilter(
filter: ImageFilter.blur(
sigmaX: _filterBlurAnimation.value,
sigmaY: _filterBlurAnimation.value),
child: Container(
constraints: BoxConstraints.expand(),
color: _filterColorAnimation.value,
),
);
},
),
);
},
maintainState: false,
opaque: opaque,
),
],
OverlayEntry(
builder: (context) {
final Widget annotatedChild = Semantics(
child: AlignTransition(
alignment: _animation!,
child: snack.isDismissible
? _getDismissibleSnack(_builder)
: _getSnack(),
),
focused: false,
container: true,
explicitChildNodes: true,
);
return annotatedChild;
},
maintainState: false,
opaque: opaque,
),
];
}
String dismissibleKeyGen = "";
Widget _getDismissibleSnack(Widget? child) {
return Dismissible(
direction: _getDismissDirection(),
resizeDuration: null,
confirmDismiss: (_) {
if (currentStatus == SnackbarStatus.OPENING ||
currentStatus == SnackbarStatus.CLOSING) {
return Future.value(false);
}
return Future.value(true);
},
key: Key(dismissibleKeyGen),
onDismissed: (_) {
dismissibleKeyGen += "1";
_cancelTimer();
_wasDismissedBySwipe = true;
if (isCurrent) {
navigator!.pop();
} else {
navigator!.removeRoute(this);
}
},
child: _getSnack(),
);
}
Widget _getSnack() {
return Container(
margin: snack.margin,
child: _builder,
);
}
DismissDirection _getDismissDirection() {
if (snack.dismissDirection == SnackDismissDirection.HORIZONTAL) {
return DismissDirection.horizontal;
} else {
if (snack.snackPosition == SnackPosition.TOP) {
return DismissDirection.up;
}
return DismissDirection.down;
}
}
@override
bool get finishedWhenPopped =>
_controller!.status == AnimationStatus.dismissed;
/// The animation that drives the route's transition and the previous route's
/// forward transition.
Animation<Alignment>? _animation;
/// The animation controller that the route uses to drive the transitions.
///
/// The animation itself is exposed by the [animation] property.
AnimationController? _controller;
/// Called to create the animation controller that will drive the transitions
/// to this route from the previous one, and back to the previous route
/// from this one.
AnimationController createAnimationController() {
assert(!_transitionCompleter.isCompleted,
'Cannot reuse a $runtimeType after disposing it.');
assert(snack.animationDuration >= Duration.zero);
return AnimationController(
duration: snack.animationDuration,
debugLabel: debugLabel,
vsync: navigator!,
);
}
/// Called to create the animation that exposes the current progress of
/// the transition controlled by the animation controller created by
/// `createAnimationController()`.
Animation<Alignment> createAnimation() {
assert(!_transitionCompleter.isCompleted,
'Cannot reuse a $runtimeType after disposing it.');
assert(_controller != null);
return AlignmentTween(begin: _initialAlignment, end: _endAlignment).animate(
CurvedAnimation(
parent: _controller!,
curve: snack.forwardAnimationCurve,
reverseCurve: snack.reverseAnimationCurve,
),
);
}
Animation<double> createBlurFilterAnimation() {
return Tween(begin: 0.0, end: snack.overlayBlur).animate(
CurvedAnimation(
parent: _controller!,
curve: Interval(
0.0,
0.35,
curve: Curves.easeInOutCirc,
),
),
);
}
Animation<Color?> createColorFilterAnimation() {
return ColorTween(begin: Color(0x00000000), end: snack.overlayColor)
.animate(
CurvedAnimation(
parent: _controller!,
curve: Interval(
0.0,
0.35,
curve: Curves.easeInOutCirc,
),
),
);
}
T? _result;
SnackbarStatus? currentStatus;
void _handleStatusChanged(AnimationStatus status) {
switch (status) {
case AnimationStatus.completed:
currentStatus = SnackbarStatus.OPEN;
_snackbarStatus(currentStatus);
if (overlayEntries.isNotEmpty) overlayEntries.first.opaque = opaque;
break;
case AnimationStatus.forward:
currentStatus = SnackbarStatus.OPENING;
_snackbarStatus(currentStatus);
break;
case AnimationStatus.reverse:
currentStatus = SnackbarStatus.CLOSING;
_snackbarStatus(currentStatus);
if (overlayEntries.isNotEmpty) overlayEntries.first.opaque = false;
break;
case AnimationStatus.dismissed:
assert(!overlayEntries.first.opaque);
// We might still be the current route if a subclass is controlling the
// the transition and hits the dismissed status. For example, the iOS
// back gesture drives this animation to the dismissed status before
// popping the navigator.
currentStatus = SnackbarStatus.CLOSED;
_snackbarStatus(currentStatus);
if (!isCurrent) {
navigator!.finalizeRoute(this);
// assert(overlayEntries.isEmpty);
}
break;
}
changedInternalState();
}
@override
void install() {
assert(!_transitionCompleter.isCompleted,
'Cannot install a $runtimeType after disposing it.');
_controller = createAnimationController();
assert(_controller != null,
'$runtimeType.createAnimationController() returned null.');
_filterBlurAnimation = createBlurFilterAnimation();
_filterColorAnimation = createColorFilterAnimation();
_animation = createAnimation();
assert(_animation != null, '$runtimeType.createAnimation() returned null.');
super.install();
}
@override
TickerFuture didPush() {
super.didPush();
assert(
_controller != null,
// ignore: lines_longer_than_80_chars
'$runtimeType.didPush called before calling install() or after calling dispose().',
);
assert(
!_transitionCompleter.isCompleted,
'Cannot reuse a $runtimeType after disposing it.',
);
_animation!.addStatusListener(_handleStatusChanged);
_configureTimer();
return _controller!.forward();
}
@override
void didReplace(Route<dynamic>? oldRoute) {
assert(
_controller != null,
// ignore: lines_longer_than_80_chars
'$runtimeType.didReplace called before calling install() or after calling dispose().',
);
assert(
!_transitionCompleter.isCompleted,
'Cannot reuse a $runtimeType after disposing it.',
);
if (oldRoute is SnackRoute) {
_controller!.value = oldRoute._controller!.value;
}
_animation!.addStatusListener(_handleStatusChanged);
super.didReplace(oldRoute);
}
@override
bool didPop(T? result) {
assert(
_controller != null,
// ignore: lines_longer_than_80_chars
'$runtimeType.didPop called before calling install() or after calling dispose().',
);
assert(
!_transitionCompleter.isCompleted,
'Cannot reuse a $runtimeType after disposing it.',
);
_result = result;
_cancelTimer();
if (_wasDismissedBySwipe) {
Timer(Duration(milliseconds: 200), () {
_controller!.reset();
});
_wasDismissedBySwipe = false;
} else {
_controller!.reverse();
}
return super.didPop(result);
}
void _configureTimer() {
if (snack.duration != null) {
if (_timer != null && _timer!.isActive) {
_timer!.cancel();
}
_timer = Timer(snack.duration!, () {
if (isCurrent) {
navigator!.pop();
} else if (isActive) {
navigator!.removeRoute(this);
}
});
} else {
if (_timer != null) {
_timer!.cancel();
}
}
}
void _cancelTimer() {
if (_timer != null && _timer!.isActive) {
_timer!.cancel();
}
}
/// Whether this route can perform a transition to the given route.
/// Subclasses can override this method to restrict the set of routes they
/// need to coordinate transitions with.
bool canTransitionTo(SnackRoute<dynamic> nextRoute) => true;
/// Whether this route can perform a transition from the given route.
///
/// Subclasses can override this method to restrict the set of routes they
/// need to coordinate transitions with.
bool canTransitionFrom(SnackRoute<dynamic> previousRoute) => true;
@override
void dispose() {
assert(!_transitionCompleter.isCompleted,
'Cannot dispose a $runtimeType twice.');
_controller?.dispose();
_transitionCompleter.complete(_result);
super.dispose();
}
/// A short description of this route useful for debugging.
String get debugLabel => '$runtimeType';
}
import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart';
import '../../../get.dart';
class SnackbarController {
late Animation<double> _filterBlurAnimation;
late Animation<Color?> _filterColorAnimation;
final GetBar<Object> snack;
final Completer _transitionCompleter = Completer();
late SnackbarStatusCallback? _snackbarStatus;
late final Alignment? _initialAlignment;
late final Alignment? _endAlignment;
bool _wasDismissedBySwipe = false;
bool _onTappedDismiss = false;
Timer? _timer;
/// The animation that drives the route's transition and the previous route's
/// forward transition.
late Animation<Alignment> _animation;
/// The animation controller that the route uses to drive the transitions.
///
/// The animation itself is exposed by the [animation] property.
late AnimationController _controller;
SnackbarStatus? _currentStatus;
final _overlayEntries = <OverlayEntry>[];
OverlayState? _overlayState;
SnackbarController(this.snack);
Future get future => _transitionCompleter.future;
bool get isSnackbarBeingShown => _currentStatus != SnackbarStatus.CLOSED;
Animation<double> createBlurFilterAnimation() {
return Tween(begin: 0.0, end: snack.overlayBlur).animate(
CurvedAnimation(
parent: _controller,
curve: const Interval(
0.0,
0.35,
curve: Curves.easeInOutCirc,
),
),
);
}
Animation<Color?> createColorOverlayColor() {
return ColorTween(begin: const Color(0x00000000), end: snack.overlayColor)
.animate(
CurvedAnimation(
parent: _controller,
curve: const Interval(
0.0,
0.35,
curve: Curves.easeInOutCirc,
),
),
);
}
void removeEntry() {
assert(
!_transitionCompleter.isCompleted,
'Cannot remove entry from a disposed snackbar',
);
_cancelTimer();
if (_wasDismissedBySwipe) {
Timer(const Duration(milliseconds: 200), () {
_controller.reset();
});
_wasDismissedBySwipe = false;
} else {
_controller.reverse();
}
}
void removeOverlay() {
for (var element in _overlayEntries) {
element.remove();
}
assert(!_transitionCompleter.isCompleted, 'Cannot remove overlay twice.');
_controller.dispose();
_overlayEntries.clear();
_transitionCompleter.complete();
}
Future<void> show() {
_configureOverlay();
return future;
}
void _cancelTimer() {
if (_timer != null && _timer!.isActive) {
_timer!.cancel();
}
}
void _configureAlignment(SnackPosition snackPosition) {
switch (snack.snackPosition) {
case SnackPosition.TOP:
{
_initialAlignment = const Alignment(-1.0, -2.0);
_endAlignment = const Alignment(-1.0, -1.0);
break;
}
case SnackPosition.BOTTOM:
{
_initialAlignment = const Alignment(-1.0, 2.0);
_endAlignment = const Alignment(-1.0, 1.0);
break;
}
}
}
void _configureOverlay() {
_overlayState = Overlay.of(Get.overlayContext!);
_overlayEntries.clear();
_overlayEntries.addAll(_createOverlayEntries(_getBodyWidget()));
_overlayState!.insertAll(_overlayEntries);
_configureSnackBarDisplay();
}
void _configureSnackBarDisplay() {
assert(!_transitionCompleter.isCompleted,
'Cannot configure a snackbar after disposing it.');
_controller = _createAnimationController();
_configureAlignment(snack.snackPosition);
_snackbarStatus = snack.snackbarStatus;
_filterBlurAnimation = createBlurFilterAnimation();
_filterColorAnimation = createColorOverlayColor();
_animation = _createAnimation();
_animation.addStatusListener(_handleStatusChanged);
_configureTimer();
_controller.forward();
}
void _configureTimer() {
if (snack.duration != null) {
if (_timer != null && _timer!.isActive) {
_timer!.cancel();
}
_timer = Timer(snack.duration!, removeEntry);
} else {
if (_timer != null) {
_timer!.cancel();
}
}
}
/// Called to create the animation that exposes the current progress of
/// the transition controlled by the animation controller created by
/// `createAnimationController()`.
Animation<Alignment> _createAnimation() {
assert(!_transitionCompleter.isCompleted,
'Cannot create a animation from a disposed snackbar');
return AlignmentTween(begin: _initialAlignment, end: _endAlignment).animate(
CurvedAnimation(
parent: _controller,
curve: snack.forwardAnimationCurve,
reverseCurve: snack.reverseAnimationCurve,
),
);
}
/// Called to create the animation controller that will drive the transitions
/// to this route from the previous one, and back to the previous route
/// from this one.
AnimationController _createAnimationController() {
assert(!_transitionCompleter.isCompleted,
'Cannot create a animationController from a disposed snackbar');
assert(snack.animationDuration >= Duration.zero);
return AnimationController(
duration: snack.animationDuration,
debugLabel: '$runtimeType',
vsync: navigator!,
);
}
Iterable<OverlayEntry> _createOverlayEntries(Widget child) {
return <OverlayEntry>[
if (snack.overlayBlur > 0.0) ...[
OverlayEntry(
builder: (context) => GestureDetector(
onTap: () {
if (snack.isDismissible && !_onTappedDismiss) {
_onTappedDismiss = true;
Get.back();
}
},
child: AnimatedBuilder(
animation: _filterBlurAnimation,
builder: (context, child) {
return BackdropFilter(
filter: ImageFilter.blur(
sigmaX: _filterBlurAnimation.value,
sigmaY: _filterBlurAnimation.value),
child: Container(
constraints: const BoxConstraints.expand(),
color: _filterColorAnimation.value,
),
);
},
),
),
maintainState: false,
opaque: false,
),
],
OverlayEntry(
builder: (context) => Semantics(
child: AlignTransition(
alignment: _animation,
child: snack.isDismissible
? _getDismissibleSnack(child)
: _getSnackbarContainer(child),
),
focused: false,
container: true,
explicitChildNodes: true,
),
maintainState: false,
opaque: false,
),
];
}
Widget _getBodyWidget() {
return Builder(builder: (_) {
return GestureDetector(
child: snack,
onTap: snack.onTap != null ? () => snack.onTap?.call(snack) : null,
);
});
}
DismissDirection _getDismissDirection() {
if (snack.dismissDirection == SnackDismissDirection.HORIZONTAL) {
return DismissDirection.horizontal;
} else {
if (snack.snackPosition == SnackPosition.TOP) {
return DismissDirection.up;
}
return DismissDirection.down;
}
}
Widget _getDismissibleSnack(Widget child) {
return Dismissible(
direction: _getDismissDirection(),
resizeDuration: null,
confirmDismiss: (_) {
if (_currentStatus == SnackbarStatus.OPENING ||
_currentStatus == SnackbarStatus.CLOSING) {
return Future.value(false);
}
return Future.value(true);
},
key: const Key('dismissible'),
onDismissed: (_) {
_cancelTimer();
_wasDismissedBySwipe = true;
removeEntry();
},
child: _getSnackbarContainer(child),
);
}
Widget _getSnackbarContainer(Widget child) {
return Container(
margin: snack.margin,
child: child,
);
}
void _handleStatusChanged(AnimationStatus status) {
switch (status) {
case AnimationStatus.completed:
_currentStatus = SnackbarStatus.OPEN;
_snackbarStatus?.call(_currentStatus);
if (_overlayEntries.isNotEmpty) _overlayEntries.first.opaque = false;
break;
case AnimationStatus.forward:
_currentStatus = SnackbarStatus.OPENING;
_snackbarStatus?.call(_currentStatus);
break;
case AnimationStatus.reverse:
_currentStatus = SnackbarStatus.CLOSING;
_snackbarStatus?.call(_currentStatus);
if (_overlayEntries.isNotEmpty) _overlayEntries.first.opaque = false;
break;
case AnimationStatus.dismissed:
assert(!_overlayEntries.first.opaque);
_currentStatus = SnackbarStatus.CLOSED;
_snackbarStatus?.call(_currentStatus);
removeOverlay();
break;
}
}
}
... ...