Line data Source code
1 : part of 'main.dart';
2 :
3 : /// A helper to create a material app
4 : /// DO NOT use this on top of a VRouter, this will break the navigation
5 : /// instead use VRouter as the topmost MaterialApp
6 : /// DO use this in your widget instead of the traditional [MaterialApp]
7 : /// Note that if you ever use [MaterialApp] instead of [VMaterialApp],
8 : /// you risk breaking the entire navigation system.
9 : class VMaterialApp<T extends Object> extends StatelessWidget {
10 : final Widget child;
11 :
12 0 : const VMaterialApp({
13 : Key? key,
14 : required this.child,
15 : this.backButtonDispatcher,
16 : this.builder,
17 : this.title = '',
18 : this.onGenerateTitle,
19 : this.color,
20 : this.theme,
21 : this.darkTheme,
22 : this.highContrastTheme,
23 : this.highContrastDarkTheme,
24 : this.themeMode = ThemeMode.system,
25 : this.locale,
26 : this.localizationsDelegates,
27 : this.localeListResolutionCallback,
28 : this.localeResolutionCallback,
29 : this.supportedLocales = const <Locale>[Locale('en', 'US')],
30 : this.debugShowMaterialGrid = false,
31 : this.showPerformanceOverlay = false,
32 : this.checkerboardRasterCacheImages = false,
33 : this.checkerboardOffscreenLayers = false,
34 : this.showSemanticsDebugger = false,
35 : this.debugShowCheckedModeBanner = true,
36 : this.shortcuts,
37 : this.actions,
38 0 : }) : super(key: key);
39 :
40 0 : @override
41 : Widget build(BuildContext context) {
42 0 : return MaterialApp.router(
43 0 : routerDelegate: VRouterDelegateHelper<T>(child: child),
44 0 : routeInformationParser: VRouteInformationParser(),
45 0 : backButtonDispatcher: backButtonDispatcher,
46 0 : builder: builder,
47 0 : title: title ?? '',
48 0 : onGenerateTitle: onGenerateTitle,
49 0 : color: color,
50 0 : theme: theme,
51 0 : darkTheme: darkTheme,
52 0 : highContrastTheme: highContrastTheme,
53 0 : highContrastDarkTheme: highContrastDarkTheme,
54 0 : themeMode: themeMode,
55 0 : locale: locale,
56 0 : localizationsDelegates: localizationsDelegates,
57 0 : localeListResolutionCallback: localeListResolutionCallback,
58 0 : localeResolutionCallback: localeResolutionCallback,
59 0 : supportedLocales: supportedLocales ?? const <Locale>[Locale('en', 'US')],
60 0 : debugShowMaterialGrid: debugShowMaterialGrid ?? false,
61 0 : showPerformanceOverlay: showPerformanceOverlay ?? false,
62 0 : checkerboardRasterCacheImages: checkerboardRasterCacheImages ?? false,
63 0 : checkerboardOffscreenLayers: checkerboardOffscreenLayers ?? false,
64 0 : showSemanticsDebugger: showSemanticsDebugger ?? false,
65 0 : debugShowCheckedModeBanner: debugShowCheckedModeBanner ?? true,
66 0 : shortcuts: shortcuts,
67 0 : actions: actions,
68 : );
69 : }
70 :
71 : /// {@macro flutter.widgets.widgetsApp.backButtonDispatcher}
72 : final BackButtonDispatcher? backButtonDispatcher;
73 :
74 : /// {@macro flutter.widgets.widgetsApp.builder}
75 : ///
76 : /// Material specific features such as [showDialog] and [showMenu], and widgets
77 : /// such as [Tooltip], [PopupMenuButton], also require a [Navigator] to properly
78 : /// function.
79 : final TransitionBuilder? builder;
80 :
81 : /// {@macro flutter.widgets.widgetsApp.title}
82 : ///
83 : /// This value is passed unmodified to [WidgetsApp.title].
84 : final String? title;
85 :
86 : /// {@macro flutter.widgets.widgetsApp.onGenerateTitle}
87 : ///
88 : /// This value is passed unmodified to [WidgetsApp.onGenerateTitle].
89 : final GenerateAppTitle? onGenerateTitle;
90 :
91 : /// Default visual properties, like colors fonts and shapes, for this app's
92 : /// material widgets.
93 : ///
94 : /// A second [darkTheme] [ThemeData] value, which is used to provide a dark
95 : /// version of the user interface can also be specified. [themeMode] will
96 : /// control which theme will be used if a [darkTheme] is provided.
97 : ///
98 : /// The default value of this property is the value of [ThemeData.light()].
99 : ///
100 : /// See also:
101 : ///
102 : /// * [themeMode], which controls which theme to use.
103 : /// * [MediaQueryData.platformBrightness], which indicates the platform's
104 : /// desired brightness and is used to automatically toggle between [theme]
105 : /// and [darkTheme] in [MaterialApp].
106 : /// * [ThemeData.brightness], which indicates the [Brightness] of a theme's
107 : /// colors.
108 : final ThemeData? theme;
109 :
110 : /// The [ThemeData] to use when a 'dark mode' is requested by the system.
111 : ///
112 : /// Some host platforms allow the users to select a system-wide 'dark mode',
113 : /// or the application may want to offer the user the ability to choose a
114 : /// dark theme just for this application. This is theme that will be used for
115 : /// such cases. [themeMode] will control which theme will be used.
116 : ///
117 : /// This theme should have a [ThemeData.brightness] set to [Brightness.dark].
118 : ///
119 : /// Uses [theme] instead when null. Defaults to the value of
120 : /// [ThemeData.light()] when both [darkTheme] and [theme] are null.
121 : ///
122 : /// See also:
123 : ///
124 : /// * [themeMode], which controls which theme to use.
125 : /// * [MediaQueryData.platformBrightness], which indicates the platform's
126 : /// desired brightness and is used to automatically toggle between [theme]
127 : /// and [darkTheme] in [MaterialApp].
128 : /// * [ThemeData.brightness], which is typically set to the value of
129 : /// [MediaQueryData.platformBrightness].
130 : final ThemeData? darkTheme;
131 :
132 : /// The [ThemeData] to use when 'high contrast' is requested by the system.
133 : ///
134 : /// Some host platforms (for example, iOS) allow the users to increase
135 : /// contrast through an accessibility setting.
136 : ///
137 : /// Uses [theme] instead when null.
138 : ///
139 : /// See also:
140 : ///
141 : /// * [MediaQueryData.highContrast], which indicates the platform's
142 : /// desire to increase contrast.
143 : final ThemeData? highContrastTheme;
144 :
145 : /// The [ThemeData] to use when a 'dark mode' and 'high contrast' is requested
146 : /// by the system.
147 : ///
148 : /// Some host platforms (for example, iOS) allow the users to increase
149 : /// contrast through an accessibility setting.
150 : ///
151 : /// This theme should have a [ThemeData.brightness] set to [Brightness.dark].
152 : ///
153 : /// Uses [darkTheme] instead when null.
154 : ///
155 : /// See also:
156 : ///
157 : /// * [MediaQueryData.highContrast], which indicates the platform's
158 : /// desire to increase contrast.
159 : final ThemeData? highContrastDarkTheme;
160 :
161 : /// Determines which theme will be used by the application if both [theme]
162 : /// and [darkTheme] are provided.
163 : ///
164 : /// If set to [ThemeMode.system], the choice of which theme to use will
165 : /// be based on the user's system preferences. If the [MediaQuery.platformBrightnessOf]
166 : /// is [Brightness.light], [theme] will be used. If it is [Brightness.dark],
167 : /// [darkTheme] will be used (unless it is null, in which case [theme]
168 : /// will be used.
169 : ///
170 : /// If set to [ThemeMode.light] the [theme] will always be used,
171 : /// regardless of the user's system preference.
172 : ///
173 : /// If set to [ThemeMode.dark] the [darkTheme] will be used
174 : /// regardless of the user's system preference. If [darkTheme] is null
175 : /// then it will fallback to using [theme].
176 : ///
177 : /// The default value is [ThemeMode.system].
178 : ///
179 : /// See also:
180 : ///
181 : /// * [theme], which is used when a light mode is selected.
182 : /// * [darkTheme], which is used when a dark mode is selected.
183 : /// * [ThemeData.brightness], which indicates to various parts of the
184 : /// system what kind of theme is being used.
185 : final ThemeMode? themeMode;
186 :
187 : /// {@macro flutter.widgets.widgetsApp.color}
188 : final Color? color;
189 :
190 : /// {@macro flutter.widgets.widgetsApp.locale}
191 : final Locale? locale;
192 :
193 : /// {@macro flutter.widgets.widgetsApp.localizationsDelegates}
194 : ///
195 : /// Internationalized apps that require translations for one of the locales
196 : /// listed in [GlobalMaterialLocalizations] should specify this parameter
197 : /// and list the [supportedLocales] that the application can handle.
198 : ///
199 : /// ```dart
200 : /// import 'package:flutter_localizations/flutter_localizations.dart';
201 : /// MaterialApp(
202 : /// localizationsDelegates: [
203 : /// // ... app-specific localization delegate[s] here
204 : /// GlobalMaterialLocalizations.delegate,
205 : /// GlobalWidgetsLocalizations.delegate,
206 : /// ],
207 : /// supportedLocales: [
208 : /// const Locale('en', 'US'), // English
209 : /// const Locale('he', 'IL'), // Hebrew
210 : /// // ... other locales the app supports
211 : /// ],
212 : /// // ...
213 : /// )
214 : /// ```
215 : ///
216 : /// ## Adding localizations for a new locale
217 : ///
218 : /// The information that follows applies to the unusual case of an app
219 : /// adding translations for a language not already supported by
220 : /// [GlobalMaterialLocalizations].
221 : ///
222 : /// Delegates that produce [WidgetsLocalizations] and [MaterialLocalizations]
223 : /// are included automatically. Apps can provide their own versions of these
224 : /// localizations by creating implementations of
225 : /// [LocalizationsDelegate<WidgetsLocalizations>] or
226 : /// [LocalizationsDelegate<MaterialLocalizations>] whose load methods return
227 : /// custom versions of [WidgetsLocalizations] or [MaterialLocalizations].
228 : ///
229 : /// For example: to add support to [MaterialLocalizations] for a
230 : /// locale it doesn't already support, say `const Locale('foo', 'BR')`,
231 : /// one could just extend [DefaultMaterialLocalizations]:
232 : ///
233 : /// ```dart
234 : /// class FooLocalizations extends DefaultMaterialLocalizations {
235 : /// FooLocalizations(Locale locale) : super(locale);
236 : /// @override
237 : /// String get okButtonLabel {
238 : /// if (locale == const Locale('foo', 'BR'))
239 : /// return 'foo';
240 : /// return super.okButtonLabel;
241 : /// }
242 : /// }
243 : ///
244 : /// ```
245 : ///
246 : /// A `FooLocalizationsDelegate` is essentially just a method that constructs
247 : /// a `FooLocalizations` object. We return a [SynchronousFuture] here because
248 : /// no asynchronous work takes place upon "loading" the localizations object.
249 : ///
250 : /// ```dart
251 : /// class FooLocalizationsDelegate extends LocalizationsDelegate<MaterialLocalizations> {
252 : /// const FooLocalizationsDelegate();
253 : /// @override
254 : /// Future<FooLocalizations> load(Locale locale) {
255 : /// return SynchronousFuture(FooLocalizations(locale));
256 : /// }
257 : /// @override
258 : /// bool shouldReload(FooLocalizationsDelegate old) => false;
259 : /// }
260 : /// ```
261 : ///
262 : /// Constructing a [MaterialApp] with a `FooLocalizationsDelegate` overrides
263 : /// the automatically included delegate for [MaterialLocalizations] because
264 : /// only the first delegate of each [LocalizationsDelegate.type] is used and
265 : /// the automatically included delegates are added to the end of the app's
266 : /// [localizationsDelegates] list.
267 : ///
268 : /// ```dart
269 : /// MaterialApp(
270 : /// localizationsDelegates: [
271 : /// const FooLocalizationsDelegate(),
272 : /// ],
273 : /// // ...
274 : /// )
275 : /// ```
276 : /// See also:
277 : ///
278 : /// * [supportedLocales], which must be specified along with
279 : /// [localizationsDelegates].
280 : /// * [GlobalMaterialLocalizations], a [localizationsDelegates] value
281 : /// which provides material localizations for many languages.
282 : /// * The Flutter Internationalization Tutorial,
283 : /// <https://flutter.dev/tutorials/internationalization/>.
284 : final Iterable<LocalizationsDelegate<dynamic>>? localizationsDelegates;
285 :
286 : /// {@macro flutter.widgets.widgetsApp.localeListResolutionCallback}
287 : ///
288 : /// This callback is passed along to the [WidgetsApp] built by this widget.
289 : final LocaleListResolutionCallback? localeListResolutionCallback;
290 :
291 : /// {@macro flutter.widgets.LocaleResolutionCallback}
292 : ///
293 : /// This callback is passed along to the [WidgetsApp] built by this widget.
294 : final LocaleResolutionCallback? localeResolutionCallback;
295 :
296 : /// {@macro flutter.widgets.widgetsApp.supportedLocales}
297 : ///
298 : /// It is passed along unmodified to the [WidgetsApp] built by this widget.
299 : ///
300 : /// See also:
301 : ///
302 : /// * [localizationsDelegates], which must be specified for localized
303 : /// applications.
304 : /// * [GlobalMaterialLocalizations], a [localizationsDelegates] value
305 : /// which provides material localizations for many languages.
306 : /// * The Flutter Internationalization Tutorial,
307 : /// <https://flutter.dev/tutorials/internationalization/>.
308 : final Iterable<Locale>? supportedLocales;
309 :
310 : /// Turns on a performance overlay.
311 : ///
312 : /// See also:
313 : ///
314 : /// * <https://flutter.dev/debugging/#performanceoverlay>
315 : final bool? showPerformanceOverlay;
316 :
317 : /// Turns on checkerboarding of raster cache images.
318 : final bool? checkerboardRasterCacheImages;
319 :
320 : /// Turns on checkerboarding of layers rendered to offscreen bitmaps.
321 : final bool? checkerboardOffscreenLayers;
322 :
323 : /// Turns on an overlay that shows the accessibility information
324 : /// reported by the framework.
325 : final bool? showSemanticsDebugger;
326 :
327 : /// {@macro flutter.widgets.widgetsApp.debugShowCheckedModeBanner}
328 : final bool? debugShowCheckedModeBanner;
329 :
330 : /// {@macro flutter.widgets.widgetsApp.shortcuts}
331 : /// {@tool snippet}
332 : /// This example shows how to add a single shortcut for
333 : /// [LogicalKeyboardKey.select] to the default shortcuts without needing to
334 : /// add your own [Shortcuts] widget.
335 : ///
336 : /// Alternatively, you could insert a [Shortcuts] widget with just the mapping
337 : /// you want to add between the [WidgetsApp] and its child and get the same
338 : /// effect.
339 : ///
340 : /// ```dart
341 : /// Widget build(BuildContext context) {
342 : /// return WidgetsApp(
343 : /// shortcuts: <LogicalKeySet, Intent>{
344 : /// ... WidgetsApp.defaultShortcuts,
345 : /// LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),
346 : /// },
347 : /// color: const Color(0xFFFF0000),
348 : /// builder: (BuildContext context, Widget child) {
349 : /// return const Placeholder();
350 : /// },
351 : /// );
352 : /// }
353 : /// ```
354 : /// {@end-tool}
355 : /// {@macro flutter.widgets.widgetsApp.shortcuts.seeAlso}
356 : final Map<LogicalKeySet, Intent>? shortcuts;
357 :
358 : /// {@macro flutter.widgets.widgetsApp.actions}
359 : /// {@tool snippet}
360 : /// This example shows how to add a single action handling an
361 : /// [ActivateAction] to the default actions without needing to
362 : /// add your own [Actions] widget.
363 : ///
364 : /// Alternatively, you could insert a [Actions] widget with just the mapping
365 : /// you want to add between the [WidgetsApp] and its child and get the same
366 : /// effect.
367 : ///
368 : /// ```dart
369 : /// Widget build(BuildContext context) {
370 : /// return WidgetsApp(
371 : /// actions: <Type, Action<Intent>>{
372 : /// ... WidgetsApp.defaultActions,
373 : /// ActivateAction: CallbackAction(
374 : /// onInvoke: (Intent intent) {
375 : /// // Do something here...
376 : /// return null;
377 : /// },
378 : /// ),
379 : /// },
380 : /// color: const Color(0xFFFF0000),
381 : /// builder: (BuildContext context, Widget child) {
382 : /// return const Placeholder();
383 : /// },
384 : /// );
385 : /// }
386 : /// ```
387 : /// {@end-tool}
388 : /// {@macro flutter.widgets.widgetsApp.actions.seeAlso}
389 : final Map<Type, Action<Intent>>? actions;
390 :
391 : /// Turns on a [GridPaper] overlay that paints a baseline grid
392 : /// Material apps.
393 : ///
394 : /// Only available in checked mode.
395 : ///
396 : /// See also:
397 : ///
398 : /// * <https://material.io/design/layout/spacing-methods.html>
399 : final bool? debugShowMaterialGrid;
400 : }
401 :
402 : /// This is a helper to create a router in which we nest
403 : /// a [VRouterDelegateHelper]
404 : class VRouterHelper extends StatelessWidget {
405 : /// The pages that will be displayed in the [VRouterDelegateHelper]
406 : /// Navigator
407 : final List<Page> pages;
408 :
409 : /// The key of the [VRouterDelegateHelper] navigator
410 : final GlobalKey<NavigatorState>? navigatorKey;
411 :
412 : /// The observers of the [VRouterDelegateHelper] navigator
413 : final List<NavigatorObserver>? observers;
414 :
415 : /// The [BackButtonDispatcher] of the router
416 : final BackButtonDispatcher? backButtonDispatcher;
417 :
418 : /// The function that will be called when [Navigator.pop]
419 : /// is called in a page contained in this router
420 : final bool Function(Route<dynamic>, dynamic)? onPopPage;
421 :
422 : /// The function that will be called when a system pop
423 : /// (hardware back button in android) is called in a page
424 : /// contained in this router
425 : final Future<bool> Function()? onSystemPopPage;
426 :
427 3 : const VRouterHelper({
428 : Key? key,
429 : required this.pages,
430 : this.navigatorKey,
431 : this.observers,
432 : this.backButtonDispatcher,
433 : this.onPopPage,
434 : this.onSystemPopPage,
435 3 : }) : super(key: key);
436 :
437 3 : @override
438 : Widget build(BuildContext context) {
439 3 : return Router(
440 3 : backButtonDispatcher: backButtonDispatcher,
441 3 : routerDelegate: VRouterDelegateHelper(
442 3 : pages: pages,
443 3 : navigatorKey: navigatorKey,
444 3 : observers: observers,
445 3 : onPopPage: onPopPage,
446 3 : onSystemPopPage: onSystemPopPage,
447 : ),
448 : );
449 : }
450 : }
451 :
452 : /// A routerDelegate which automatically creates a Navigator
453 : /// See the details of each attribute to see what they can be used for
454 : class VRouterDelegateHelper<T extends Object> extends RouterDelegate<T>
455 : with ChangeNotifier {
456 : final GlobalKey<NavigatorState>? navigatorKey;
457 : final List<NavigatorObserver>? observers;
458 : final Widget? child;
459 : final List<Page>? pages;
460 : final bool Function(Route<dynamic>, dynamic)? onPopPage;
461 : final Future<bool> Function()? onSystemPopPage;
462 :
463 3 : VRouterDelegateHelper({
464 : this.child,
465 : this.pages,
466 : this.navigatorKey,
467 : this.observers,
468 : this.onPopPage,
469 : this.onSystemPopPage,
470 0 : }) : assert(pages != null || child != null);
471 :
472 3 : @override
473 : Widget build(BuildContext context) {
474 3 : if (pages != null) {
475 3 : return Navigator(
476 3 : key: navigatorKey,
477 3 : observers: observers ?? [],
478 3 : pages: pages!,
479 3 : onPopPage: onPopPage,
480 : );
481 : }
482 0 : if (child != null) {
483 0 : return child!;
484 : }
485 :
486 0 : return Center(
487 0 : child: CircularProgressIndicator(),
488 : );
489 : }
490 :
491 : @override
492 0 : Future<void> setNewRoutePath(configuration) async => null;
493 :
494 : @override
495 0 : Future<bool> popRoute() async {
496 0 : if (onSystemPopPage != null) {
497 0 : await onSystemPopPage!();
498 : return true;
499 : }
500 : return false;
501 : }
502 : }
|