import 'package:flutter/widgets.dart'; import 'package:vrouter/src/vlogs.dart'; import 'package:vrouter/src/vrouter_core.dart'; import 'package:vrouter/src/vrouter_scope.dart'; import 'package:vrouter/src/vrouter_vroute_elements.dart'; import 'mixin/vrouter_app_mixin.dart'; /// This widget handles most of the routing work /// It gives you access to the [routes] attribute where you can start /// building your routes using [VRouteElement]s /// /// Note that this widget also acts as a [WidgetsApp] so you can pass /// it every argument that you would expect in [WidgetsApp] class WidgetsVRouter extends VRouterApp { @override final List routes; @override final Widget Function(Animation animation, Animation secondaryAnimation, Widget child)? buildTransition; @override final Duration? transitionDuration; @override final Duration? reverseTransitionDuration; @override final VRouterMode mode; @override final List logs; @override Future beforeEnter(VRedirector vRedirector) => _beforeEnter(vRedirector); final Future Function(VRedirector vRedirector) _beforeEnter; @override Future beforeLeave( VRedirector vRedirector, void Function(Map historyState) saveHistoryState, ) => _beforeLeave(vRedirector, saveHistoryState); final Future Function( VRedirector vRedirector, void Function(Map historyState) saveHistoryState, ) _beforeLeave; @override void afterEnter(BuildContext context, String? from, String to) => _afterEnter(context, from, to); final void Function(BuildContext context, String? from, String to) _afterEnter; @override Future onPop(VRedirector vRedirector) => _onPop(vRedirector); final Future Function(VRedirector vRedirector) _onPop; @override Future onSystemPop(VRedirector vRedirector) => _onSystemPop(vRedirector); final Future Function(VRedirector vRedirector) _onSystemPop; @override final String initialUrl; @override final Key? appRouterKey; @override final GlobalKey? navigatorKey; WidgetsVRouter({ Key? key, required this.routes, Future Function(VRedirector vRedirector) beforeEnter = VoidVGuard.voidBeforeEnter, Future Function( VRedirector vRedirector, void Function(Map historyState) saveHistoryState, ) beforeLeave = VoidVGuard.voidBeforeLeave, void Function(BuildContext context, String? from, String to) afterEnter = VoidVGuard.voidAfterEnter, Future Function(VRedirector vRedirector) onPop = VoidVPopHandler.voidOnPop, Future Function(VRedirector vRedirector) onSystemPop = VoidVPopHandler.voidOnSystemPop, this.buildTransition, this.transitionDuration, this.reverseTransitionDuration, this.mode = VRouterMode.hash, this.initialUrl = '/', this.logs = VLogs.info, this.navigatorObservers = const [], this.builder, @Deprecated('Please use navigatorKey instead.\n This has been removed because it is redundant with navigatorKey.') this.appRouterKey, this.navigatorKey, // Bellow are the WidgetsApp parameters this.title = '', this.onGenerateTitle, this.textStyle, required this.color, this.locale, this.localizationsDelegates, this.localeListResolutionCallback, this.localeResolutionCallback, this.supportedLocales = const [Locale('en', 'US')], this.showPerformanceOverlay = false, this.checkerboardRasterCacheImages = false, this.checkerboardOffscreenLayers = false, this.showSemanticsDebugger = false, this.debugShowWidgetInspector = false, this.debugShowCheckedModeBanner = true, this.useInheritedMediaQuery = false, this.inspectorSelectButtonBuilder, this.shortcuts, this.actions, this.restorationScopeId, }) : _beforeEnter = beforeEnter, _beforeLeave = beforeLeave, _afterEnter = afterEnter, _onPop = onPop, _onSystemPop = onSystemPop, super(key: key); @override WidgetsVRouterState createState() => WidgetsVRouterState(); @override final List navigatorObservers; @override final Widget Function(BuildContext context, Widget child)? builder; /// {@macro flutter.widgets.widgetsApp.title} /// /// This value is passed unmodified to [WidgetsApp.title]. final String title; /// {@macro flutter.widgets.widgetsApp.onGenerateTitle} /// /// This value is passed unmodified to [WidgetsApp.onGenerateTitle]. final GenerateAppTitle? onGenerateTitle; /// The default text style for [Text] in the application. final TextStyle? textStyle; /// {@template flutter.widgets.widgetsApp.color} /// The primary color to use for the application in the operating system /// interface. /// /// For example, on Android this is the color used for the application in the /// application switcher. /// {@endtemplate} final Color color; /// {@template flutter.widgets.widgetsApp.locale} /// The initial locale for this app's [Localizations] widget is based /// on this value. /// /// If the 'locale' is null then the system's locale value is used. /// /// The value of [Localizations.locale] will equal this locale if /// it matches one of the [supportedLocales]. Otherwise it will be /// the first element of [supportedLocales]. /// {@endtemplate} /// /// See also: /// /// * [localeResolutionCallback], which can override the default /// [supportedLocales] matching algorithm. /// * [localizationsDelegates], which collectively define all of the localized /// resources used by this app. final Locale? locale; /// {@macro flutter.widgets.widgetsApp.localizationsDelegates} /// /// Internationalized apps that require translations for one of the locales /// listed in [GlobalMaterialLocalizations] should specify this parameter /// and list the [supportedLocales] that the application can handle. /// /// ```dart /// import 'package:flutter_localizations/flutter_localizations.dart'; /// MaterialApp( /// localizationsDelegates: [ /// // ... app-specific localization delegate[s] here /// GlobalMaterialLocalizations.delegate, /// GlobalWidgetsLocalizations.delegate, /// ], /// supportedLocales: [ /// const Locale('en', 'US'), // English /// const Locale('he', 'IL'), // Hebrew /// // ... other locales the app supports /// ], /// // ... /// ) /// ``` /// /// ## Adding localizations for a new locale /// /// The information that follows applies to the unusual case of an app /// adding translations for a language not already supported by /// [GlobalMaterialLocalizations]. /// /// Delegates that produce [WidgetsLocalizations] and [MaterialLocalizations] /// are included automatically. Apps can provide their own versions of these /// localizations by creating implementations of /// [LocalizationsDelegate] or /// [LocalizationsDelegate] whose load methods return /// custom versions of [WidgetsLocalizations] or [MaterialLocalizations]. /// /// For example: to add support to [MaterialLocalizations] for a /// locale it doesn't already support, say `const Locale('foo', 'BR')`, /// one could just extend [DefaultMaterialLocalizations]: /// /// ```dart /// class FooLocalizations extends DefaultMaterialLocalizations { /// FooLocalizations(Locale locale) : super(locale); /// @override /// String get okButtonLabel { /// if (locale == const Locale('foo', 'BR')) /// return 'foo'; /// return super.okButtonLabel; /// } /// } /// /// ``` /// /// A `FooLocalizationsDelegate` is essentially just a method that constructs /// a `FooLocalizations` object. We return a [SynchronousFuture] here because /// no asynchronous work takes place upon "loading" the localizations object. /// /// ```dart /// class FooLocalizationsDelegate extends LocalizationsDelegate { /// const FooLocalizationsDelegate(); /// @override /// Future load(Locale locale) { /// return SynchronousFuture(FooLocalizations(locale)); /// } /// @override /// bool shouldReload(FooLocalizationsDelegate old) => false; /// } /// ``` /// /// Constructing a [MaterialApp] with a `FooLocalizationsDelegate` overrides /// the automatically included delegate for [MaterialLocalizations] because /// only the first delegate of each [LocalizationsDelegate.type] is used and /// the automatically included delegates are added to the end of the app's /// [localizationsDelegates] list. /// /// ```dart /// MaterialApp( /// localizationsDelegates: [ /// const FooLocalizationsDelegate(), /// ], /// // ... /// ) /// ``` /// See also: /// /// * [supportedLocales], which must be specified along with /// [localizationsDelegates]. /// * [GlobalMaterialLocalizations], a [localizationsDelegates] value /// which provides material localizations for many languages. /// * The Flutter Internationalization Tutorial, /// . final Iterable>? localizationsDelegates; /// {@macro flutter.widgets.widgetsApp.localeListResolutionCallback} /// /// This callback is passed along to the [WidgetsApp] built by this widget. final LocaleListResolutionCallback? localeListResolutionCallback; /// {@macro flutter.widgets.LocaleResolutionCallback} /// /// This callback is passed along to the [WidgetsApp] built by this widget. final LocaleResolutionCallback? localeResolutionCallback; /// {@macro flutter.widgets.widgetsApp.supportedLocales} /// /// It is passed along unmodified to the [WidgetsApp] built by this widget. /// /// See also: /// /// * [localizationsDelegates], which must be specified for localized /// applications. /// * [GlobalMaterialLocalizations], a [localizationsDelegates] value /// which provides material localizations for many languages. /// * The Flutter Internationalization Tutorial, /// . final Iterable supportedLocales; /// Turns on a performance overlay. /// /// See also: /// /// * final bool showPerformanceOverlay; /// Turns on checkerboarding of raster cache images. final bool checkerboardRasterCacheImages; /// Turns on checkerboarding of layers rendered to offscreen bitmaps. final bool checkerboardOffscreenLayers; /// Turns on an overlay that shows the accessibility information /// reported by the framework. final bool showSemanticsDebugger; /// Turns on an overlay that enables inspecting the widget tree. /// /// The inspector is only available in checked mode as it depends on /// [RenderObject.debugDescribeChildren] which should not be called outside of /// checked mode. final bool debugShowWidgetInspector; /// {@template flutter.widgets.widgetsApp.debugShowCheckedModeBanner} /// Turns on a little "DEBUG" banner in checked mode to indicate /// that the app is in checked mode. This is on by default (in /// checked mode), to turn it off, set the constructor argument to /// false. In release mode this has no effect. /// /// To get this banner in your application if you're not using /// WidgetsApp, include a [CheckedModeBanner] widget in your app. /// /// This banner is intended to deter people from complaining that your /// app is slow when it's in checked mode. In checked mode, Flutter /// enables a large number of expensive diagnostics to aid in /// development, and so performance in checked mode is not /// representative of what will happen in release mode. /// {@endtemplate} final bool debugShowCheckedModeBanner; /// {@macro flutter.widgets.widgetsApp.useInheritedMediaQuery} final bool useInheritedMediaQuery; /// Builds the widget the [WidgetInspector] uses to switch between view and /// inspect modes. /// /// This lets [MaterialApp] to use a material button to toggle the inspector /// select mode without requiring [WidgetInspector] to depend on the /// material package. // final InspectorSelectButtonBuilder? inspectorSelectButtonBuilder; /// {@macro flutter.widgets.widgetsApp.shortcuts} /// {@tool snippet} /// This example shows how to add a single shortcut for /// [LogicalKeyboardKey.select] to the default shortcuts without needing to /// add your own [Shortcuts] widget. /// /// Alternatively, you could insert a [Shortcuts] widget with just the mapping /// you want to add between the [WidgetsApp] and its child and get the same /// effect. /// /// ```dart /// Widget build(BuildContext context) { /// return WidgetsApp( /// shortcuts: { /// ... WidgetsApp.defaultShortcuts, /// LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(), /// }, /// color: const Color(0xFFFF0000), /// builder: (BuildContext context, Widget child) { /// return const Placeholder(); /// }, /// ); /// } /// ``` /// {@end-tool} /// {@macro flutter.widgets.widgetsApp.shortcuts.seeAlso} final Map? shortcuts; /// {@macro flutter.widgets.widgetsApp.actions} /// {@tool snippet} /// This example shows how to add a single action handling an /// [ActivateAction] to the default actions without needing to /// add your own [Actions] widget. /// /// Alternatively, you could insert a [Actions] widget with just the mapping /// you want to add between the [WidgetsApp] and its child and get the same /// effect. /// /// ```dart /// Widget build(BuildContext context) { /// return WidgetsApp( /// actions: >{ /// ... WidgetsApp.defaultActions, /// ActivateAction: CallbackAction( /// onInvoke: (Intent intent) { /// // Do something here... /// return null; /// }, /// ), /// }, /// color: const Color(0xFFFF0000), /// builder: (BuildContext context, Widget child) { /// return const Placeholder(); /// }, /// ); /// } /// ``` /// {@end-tool} /// {@macro flutter.widgets.widgetsApp.actions.seeAlso} final Map>? actions; /// {@template flutter.widgets.widgetsApp.restorationScopeId} /// The identifier to use for state restoration of this app. /// /// Providing a restoration ID inserts a [RootRestorationScope] into the /// widget hierarchy, which enables state restoration for descendant widgets. /// /// Providing a restoration ID also enables the [Navigator] built by the /// [WidgetsApp] to restore its state (i.e. to restore the history stack of /// active [Route]s). See the documentation on [Navigator] for more details /// around state restoration of [Route]s. /// /// See also: /// /// * [RestorationManager], which explains how state restoration works in /// Flutter. /// {@endtemplate} final String? restorationScopeId; static VRouterSailor of(BuildContext context) { VRouterSailor? vRouterData; // First try to get a local MaterialVRouterData vRouterData = context.dependOnInheritedWidgetOfExactType(); if (vRouterData != null) { return vRouterData; } // Else try to get the root MaterialVRouterData vRouterData = context.dependOnInheritedWidgetOfExactType(); if (vRouterData != null) { return vRouterData; } if (vRouterData == null) { throw FlutterError('MaterialVRouter.of(context) was called with a context which does not contain a MaterialVRouter.\n' 'The context used to retrieve MaterialVRouter must be that of a widget that ' 'is a descendant of a MaterialVRouter widget.'); } return vRouterData; } @override List buildRoutes() => routes; @override void afterUpdate(BuildContext context, String? from, String to) {} @override Future beforeUpdate(VRedirector vRedirector) async {} @override void afterLeave(BuildContext context, String? from, String to) {} } class WidgetsVRouterState extends State with VRouterAppStateMixin implements VRouterSailor { @override Widget build(BuildContext context) { return VRouterScope( vRouterMode: widget.mode, child: WidgetsApp.router( backButtonDispatcher: VBackButtonDispatcher(), routeInformationParser: VRouteInformationParser(), routerDelegate: vRouterDelegate, key: widget.appRouterKey, title: widget.title, onGenerateTitle: widget.onGenerateTitle, textStyle: widget.textStyle, color: widget.color, locale: widget.locale, localizationsDelegates: widget.localizationsDelegates, localeListResolutionCallback: widget.localeListResolutionCallback, localeResolutionCallback: widget.localeResolutionCallback, supportedLocales: widget.supportedLocales, showPerformanceOverlay: widget.showPerformanceOverlay, // checkerboardRasterCacheImages: widget.checkerboardRasterCacheImages, // checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers, showSemanticsDebugger: widget.showSemanticsDebugger, debugShowWidgetInspector: widget.debugShowWidgetInspector, debugShowCheckedModeBanner: widget.debugShowCheckedModeBanner, useInheritedMediaQuery: widget.useInheritedMediaQuery, // inspectorSelectButtonBuilder: widget.inspectorSelectButtonBuilder, shortcuts: widget.shortcuts, actions: widget.actions, restorationScopeId: widget.restorationScopeId, ), ); } }