import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import '../../../get.dart'; import '../router_report.dart'; class ConfigData { final ValueChanged? routingCallback; final Transition? defaultTransition; final VoidCallback? onInit; final VoidCallback? onReady; final VoidCallback? onDispose; final bool? enableLog; final LogWriterCallback? logWriterCallback; final SmartManagement smartManagement; final List binds; final Duration? transitionDuration; final bool? defaultGlobalState; final List? getPages; final GetPage? unknownRoute; final RouteInformationProvider? routeInformationProvider; final RouteInformationParser? routeInformationParser; final RouterDelegate? routerDelegate; final BackButtonDispatcher? backButtonDispatcher; final List? navigatorObservers; final GlobalKey? navigatorKey; final GlobalKey? scaffoldMessengerKey; final Map>? translationsKeys; final Translations? translations; final Locale? locale; final Locale? fallbackLocale; final String? initialRoute; final CustomTransition? customTransition; final Widget? home; final bool testMode; final Key? unikey; final ThemeData? theme; final ThemeData? darkTheme; final ThemeMode? themeMode; final bool defaultPopGesture; final bool defaultOpaqueRoute; final Duration defaultTransitionDuration; final Curve defaultTransitionCurve; final Curve defaultDialogTransitionCurve; final Duration defaultDialogTransitionDuration; final Routing routing; final Map parameters; final SnackBarQueue snackBarQueue = SnackBarQueue(); ConfigData({ required this.routingCallback, required this.defaultTransition, required this.onInit, required this.onReady, required this.onDispose, required this.enableLog, required this.logWriterCallback, required this.smartManagement, required this.binds, required this.transitionDuration, required this.defaultGlobalState, required this.getPages, required this.unknownRoute, required this.routeInformationProvider, required this.routeInformationParser, required this.routerDelegate, required this.backButtonDispatcher, required this.navigatorObservers, required this.navigatorKey, required this.scaffoldMessengerKey, required this.translationsKeys, required this.translations, required this.locale, required this.fallbackLocale, required this.initialRoute, required this.customTransition, required this.home, this.theme, this.darkTheme, this.themeMode, this.unikey, this.testMode = false, this.defaultOpaqueRoute = true, this.defaultTransitionDuration = const Duration(milliseconds: 300), this.defaultTransitionCurve = Curves.easeOutQuad, this.defaultDialogTransitionCurve = Curves.easeOutQuad, this.defaultDialogTransitionDuration = const Duration(milliseconds: 300), this.parameters = const {}, Routing? routing, bool? defaultPopGesture, }) : defaultPopGesture = defaultPopGesture ?? GetPlatform.isIOS, routing = routing ?? Routing(); ConfigData copyWith({ ValueChanged? routingCallback, Transition? defaultTransition, VoidCallback? onInit, VoidCallback? onReady, VoidCallback? onDispose, bool? enableLog, LogWriterCallback? logWriterCallback, SmartManagement? smartManagement, List? binds, Duration? transitionDuration, bool? defaultGlobalState, List? getPages, GetPage? unknownRoute, RouteInformationProvider? routeInformationProvider, RouteInformationParser? routeInformationParser, RouterDelegate? routerDelegate, BackButtonDispatcher? backButtonDispatcher, List? navigatorObservers, GlobalKey? navigatorKey, GlobalKey? scaffoldMessengerKey, Map>? translationsKeys, Translations? translations, Locale? locale, Locale? fallbackLocale, String? initialRoute, CustomTransition? customTransition, Widget? home, bool? testMode, Key? unikey, ThemeData? theme, ThemeData? darkTheme, ThemeMode? themeMode, bool? defaultPopGesture, bool? defaultOpaqueRoute, Duration? defaultTransitionDuration, Curve? defaultTransitionCurve, Curve? defaultDialogTransitionCurve, Duration? defaultDialogTransitionDuration, Routing? routing, Map? parameters, }) { return ConfigData( routingCallback: routingCallback ?? this.routingCallback, defaultTransition: defaultTransition ?? this.defaultTransition, onInit: onInit ?? this.onInit, onReady: onReady ?? this.onReady, onDispose: onDispose ?? this.onDispose, enableLog: enableLog ?? this.enableLog, logWriterCallback: logWriterCallback ?? this.logWriterCallback, smartManagement: smartManagement ?? this.smartManagement, binds: binds ?? this.binds, transitionDuration: transitionDuration ?? this.transitionDuration, defaultGlobalState: defaultGlobalState ?? this.defaultGlobalState, getPages: getPages ?? this.getPages, unknownRoute: unknownRoute ?? this.unknownRoute, routeInformationProvider: routeInformationProvider ?? this.routeInformationProvider, routeInformationParser: routeInformationParser ?? this.routeInformationParser, routerDelegate: routerDelegate ?? this.routerDelegate, backButtonDispatcher: backButtonDispatcher ?? this.backButtonDispatcher, navigatorObservers: navigatorObservers ?? this.navigatorObservers, navigatorKey: navigatorKey ?? this.navigatorKey, scaffoldMessengerKey: scaffoldMessengerKey ?? this.scaffoldMessengerKey, translationsKeys: translationsKeys ?? this.translationsKeys, translations: translations ?? this.translations, locale: locale ?? this.locale, fallbackLocale: fallbackLocale ?? this.fallbackLocale, initialRoute: initialRoute ?? this.initialRoute, customTransition: customTransition ?? this.customTransition, home: home ?? this.home, testMode: testMode ?? this.testMode, unikey: unikey ?? this.unikey, theme: theme ?? this.theme, darkTheme: darkTheme ?? this.darkTheme, themeMode: themeMode ?? this.themeMode, defaultPopGesture: defaultPopGesture ?? this.defaultPopGesture, defaultOpaqueRoute: defaultOpaqueRoute ?? this.defaultOpaqueRoute, defaultTransitionDuration: defaultTransitionDuration ?? this.defaultTransitionDuration, defaultTransitionCurve: defaultTransitionCurve ?? this.defaultTransitionCurve, defaultDialogTransitionCurve: defaultDialogTransitionCurve ?? this.defaultDialogTransitionCurve, defaultDialogTransitionDuration: defaultDialogTransitionDuration ?? this.defaultDialogTransitionDuration, routing: routing ?? this.routing, parameters: parameters ?? this.parameters, ); } @override bool operator ==(Object other) { if (identical(this, other)) return true; return other is ConfigData && other.routingCallback == routingCallback && other.defaultTransition == defaultTransition && other.onInit == onInit && other.onReady == onReady && other.onDispose == onDispose && other.enableLog == enableLog && other.logWriterCallback == logWriterCallback && other.smartManagement == smartManagement && listEquals(other.binds, binds) && other.transitionDuration == transitionDuration && other.defaultGlobalState == defaultGlobalState && listEquals(other.getPages, getPages) && other.unknownRoute == unknownRoute && other.routeInformationProvider == routeInformationProvider && other.routeInformationParser == routeInformationParser && other.routerDelegate == routerDelegate && other.backButtonDispatcher == backButtonDispatcher && listEquals(other.navigatorObservers, navigatorObservers) && other.navigatorKey == navigatorKey && other.scaffoldMessengerKey == scaffoldMessengerKey && mapEquals(other.translationsKeys, translationsKeys) && other.translations == translations && other.locale == locale && other.fallbackLocale == fallbackLocale && other.initialRoute == initialRoute && other.customTransition == customTransition && other.home == home && other.testMode == testMode && other.unikey == unikey && other.theme == theme && other.darkTheme == darkTheme && other.themeMode == themeMode && other.defaultPopGesture == defaultPopGesture && other.defaultOpaqueRoute == defaultOpaqueRoute && other.defaultTransitionDuration == defaultTransitionDuration && other.defaultTransitionCurve == defaultTransitionCurve && other.defaultDialogTransitionCurve == defaultDialogTransitionCurve && other.defaultDialogTransitionDuration == defaultDialogTransitionDuration && other.routing == routing && mapEquals(other.parameters, parameters); } @override int get hashCode { return routingCallback.hashCode ^ defaultTransition.hashCode ^ onInit.hashCode ^ onReady.hashCode ^ onDispose.hashCode ^ enableLog.hashCode ^ logWriterCallback.hashCode ^ smartManagement.hashCode ^ binds.hashCode ^ transitionDuration.hashCode ^ defaultGlobalState.hashCode ^ getPages.hashCode ^ unknownRoute.hashCode ^ routeInformationProvider.hashCode ^ routeInformationParser.hashCode ^ routerDelegate.hashCode ^ backButtonDispatcher.hashCode ^ navigatorObservers.hashCode ^ navigatorKey.hashCode ^ scaffoldMessengerKey.hashCode ^ translationsKeys.hashCode ^ translations.hashCode ^ locale.hashCode ^ fallbackLocale.hashCode ^ initialRoute.hashCode ^ customTransition.hashCode ^ home.hashCode ^ testMode.hashCode ^ unikey.hashCode ^ theme.hashCode ^ darkTheme.hashCode ^ themeMode.hashCode ^ defaultPopGesture.hashCode ^ defaultOpaqueRoute.hashCode ^ defaultTransitionDuration.hashCode ^ defaultTransitionCurve.hashCode ^ defaultDialogTransitionCurve.hashCode ^ defaultDialogTransitionDuration.hashCode ^ routing.hashCode ^ parameters.hashCode; } } class GetRoot extends StatefulWidget { const GetRoot({ super.key, required this.config, required this.child, }); final ConfigData config; final Widget child; @override State createState() => GetRootState(); static GetRootState of(BuildContext context) { // Handles the case where the input context is a navigator element. GetRootState? root; if (context is StatefulElement && context.state is GetRootState) { root = context.state as GetRootState; } root = context.findRootAncestorStateOfType() ?? root; assert(() { if (root == null) { throw FlutterError( 'GetRoot operation requested with a context that does not include a GetRoot.\n' 'The context used must be that of a ' 'widget that is a descendant of a GetRoot widget.', ); } return true; }()); return root!; } } class GetRootState extends State with WidgetsBindingObserver { static GetRootState? _controller; static GetRootState get controller { if (_controller == null) { throw Exception('GetRoot is not part of the three'); } else { return _controller!; } } late ConfigData config; @override void initState() { config = widget.config; GetRootState._controller = this; ambiguate(Engine.instance)!.addObserver(this); onInit(); super.initState(); } // @override // void didUpdateWidget(covariant GetRoot oldWidget) { // if (oldWidget.config != widget.config) { // config = widget.config; // } // super.didUpdateWidget(oldWidget); // } void onClose() { config.onDispose?.call(); Get.clearTranslations(); config.snackBarQueue.disposeControllers(); RouterReportManager.instance.clearRouteKeys(); RouterReportManager.dispose(); Get.resetInstance(clearRouteBindings: true); _controller = null; ambiguate(Engine.instance)!.removeObserver(this); } @override void dispose() { onClose(); super.dispose(); } void onInit() { // if (config.getPages == null && config.home == null) { // throw 'You need add pages or home'; // } if (config.routerDelegate == null) { final newDelegate = GetDelegate.createDelegate( pages: config.getPages ?? [ GetPage( name: cleanRouteName("/${config.home.runtimeType}"), page: () => config.home!, ), ], notFoundRoute: config.unknownRoute, navigatorKey: config.navigatorKey, navigatorObservers: (config.navigatorObservers == null ? [ GetObserver(config.routingCallback, Get.routing) ] : [ GetObserver(config.routingCallback, config.routing), ...config.navigatorObservers! ]), ); config = config.copyWith(routerDelegate: newDelegate); } if (config.routeInformationParser == null) { final newRouteInformationParser = GetInformationParser.createInformationParser( initialRoute: config.initialRoute ?? config.getPages?.first.name ?? cleanRouteName("/${config.home.runtimeType}"), ); config = config.copyWith(routeInformationParser: newRouteInformationParser); } if (config.locale != null) Get.locale = config.locale; if (config.fallbackLocale != null) { Get.fallbackLocale = config.fallbackLocale; } if (config.translations != null) { Get.addTranslations(config.translations!.keys); } else if (config.translationsKeys != null) { Get.addTranslations(config.translationsKeys!); } Get.smartManagement = config.smartManagement; config.onInit?.call(); Get.isLogEnable = config.enableLog ?? kDebugMode; Get.log = config.logWriterCallback ?? defaultLogWriterCallback; if (config.defaultTransition == null) { config = config.copyWith(defaultTransition: getThemeTransition()); } // defaultOpaqueRoute = config.opaqueRoute ?? true; // defaultPopGesture = config.popGesture ?? GetPlatform.isIOS; // defaultTransitionDuration = // config.transitionDuration ?? Duration(milliseconds: 300); Future(() => onReady()); } set parameters(Map newParameters) { // rootController.parameters = newParameters; config = config.copyWith(parameters: newParameters); } set testMode(bool isTest) { config = config.copyWith(testMode: isTest); // _getxController.testMode = isTest; } void onReady() { config.onReady?.call(); } Transition? getThemeTransition() { final platform = context.theme.platform; final matchingTransition = Get.theme.pageTransitionsTheme.builders[platform]; switch (matchingTransition) { case CupertinoPageTransitionsBuilder(): return Transition.cupertino; case ZoomPageTransitionsBuilder(): return Transition.zoom; case FadeUpwardsPageTransitionsBuilder(): return Transition.fade; case OpenUpwardsPageTransitionsBuilder(): return Transition.native; default: return null; } } @override void didChangeLocales(List? locales) { Get.asap(() { final locale = Get.deviceLocale; if (locale != null) { Get.updateLocale(locale); } }); } void setTheme(ThemeData value) { if (config.darkTheme == null) { config = config.copyWith(theme: value); } else { if (value.brightness == Brightness.light) { config = config.copyWith(theme: value); } else { config = config.copyWith(darkTheme: value); } } update(); } void setThemeMode(ThemeMode value) { config = config.copyWith(themeMode: value); update(); } void restartApp() { config = config.copyWith(unikey: UniqueKey()); update(); } void update() { context.visitAncestorElements((element) { element.markNeedsBuild(); return false; }); } GlobalKey get key => rootDelegate.navigatorKey; GetDelegate get rootDelegate => config.routerDelegate as GetDelegate; RouteInformationParser get informationParser => config.routeInformationParser!; GlobalKey? addKey(GlobalKey newKey) { rootDelegate.navigatorKey = newKey; return key; } Map keys = {}; GetDelegate? nestedKey(String? key) { if (key == null) { return rootDelegate; } keys.putIfAbsent( key, () => GetDelegate( showHashOnUrl: true, //debugLabel: 'Getx nested key: ${key.toString()}', pages: RouteDecoder.fromRoute(key).currentChildren ?? [], ), ); return keys[key]; } @override Widget build(BuildContext context) { return widget.child; } String cleanRouteName(String name) { name = name.replaceAll('() => ', ''); /// uncomment for URL styling. // name = name.paramCase!; if (!name.startsWith('/')) { name = '/$name'; } return Uri.tryParse(name)?.toString() ?? name; } }