Jonny Borges
Committed by GitHub

Merge pull request #2087 from Bdaya-Dev/master

Minor improvements with small breaking changes
@@ -75,9 +75,7 @@ class HomeView extends GetView<HomeController> { @@ -75,9 +75,7 @@ class HomeView extends GetView<HomeController> {
75 shape: StadiumBorder(), 75 shape: StadiumBorder(),
76 ), 76 ),
77 onPressed: () async { 77 onPressed: () async {
78 - final data =  
79 - await Get.rootDelegate.toNamed('/home/country');  
80 - print('DATA: $data'); 78 + await Get.rootDelegate.toNamed('/home/country');
81 }, 79 },
82 child: Text( 80 child: Text(
83 'fetch_country'.tr, 81 'fetch_country'.tr,
  1 +import 'dart:async';
  2 +
  3 +import 'package:get/get.dart';
  4 +import 'package:async/async.dart';
  5 +
  6 +class SplashService extends GetxService {
  7 + final welcomeStr = ['GetX', 'Rules!'];
  8 + final activeStr = 0.obs;
  9 +
  10 + final memo = AsyncMemoizer<void>();
  11 + Future<void> init() {
  12 + return memo.runOnce(_initFunction);
  13 + }
  14 +
  15 + void _changeActiveString() {
  16 + activeStr.value = (activeStr.value + 1) % welcomeStr.length;
  17 + }
  18 +
  19 + Future<void> _initFunction() async {
  20 + final t = Timer.periodic(
  21 + Duration(milliseconds: 500),
  22 + (t) => _changeActiveString(),
  23 + );
  24 + //simulate some long running operation
  25 + await Future.delayed(Duration(seconds: 5));
  26 + //cancel the timer once we are done
  27 + t.cancel();
  28 + }
  29 +}
  1 +import 'package:flutter/material.dart';
  2 +
  3 +import 'package:get/get.dart';
  4 +
  5 +import '../controllers/splash_service.dart';
  6 +
  7 +class SplashView extends GetView<SplashService> {
  8 + @override
  9 + Widget build(BuildContext context) {
  10 + return Scaffold(
  11 + body: Center(
  12 + child: Column(
  13 + mainAxisSize: MainAxisSize.min,
  14 + children: [
  15 + Obx(
  16 + () => Text(
  17 + controller.welcomeStr[controller.activeStr.value],
  18 + style: TextStyle(fontSize: 20),
  19 + ),
  20 + ),
  21 + CircularProgressIndicator(),
  22 + ],
  23 + ),
  24 + ),
  25 + );
  26 + }
  27 +}
  1 +import 'package:example_nav2/app/modules/splash/controllers/splash_service.dart';
  2 +import 'package:example_nav2/app/modules/splash/views/splash_view.dart';
1 import 'package:flutter/material.dart'; 3 import 'package:flutter/material.dart';
2 import 'package:get/get.dart'; 4 import 'package:get/get.dart';
3 5
@@ -10,10 +12,23 @@ void main() { @@ -10,10 +12,23 @@ void main() {
10 title: "Application", 12 title: "Application",
11 initialBinding: BindingsBuilder( 13 initialBinding: BindingsBuilder(
12 () { 14 () {
  15 + Get.put(SplashService());
13 Get.put(AuthService()); 16 Get.put(AuthService());
14 }, 17 },
15 ), 18 ),
16 getPages: AppPages.routes, 19 getPages: AppPages.routes,
  20 + builder: (context, child) {
  21 + return FutureBuilder<void>(
  22 + key: ValueKey('initFuture'),
  23 + future: Get.find<SplashService>().init(),
  24 + builder: (context, snapshot) {
  25 + if (snapshot.connectionState == ConnectionState.done) {
  26 + return child ?? SizedBox.shrink();
  27 + }
  28 + return SplashView();
  29 + },
  30 + );
  31 + },
17 // routeInformationParser: GetInformationParser( 32 // routeInformationParser: GetInformationParser(
18 // // initialRoute: Routes.HOME, 33 // // initialRoute: Routes.HOME,
19 // ), 34 // ),
@@ -2,8 +2,6 @@ @@ -2,8 +2,6 @@
2 // Generated file. Do not edit. 2 // Generated file. Do not edit.
3 // 3 //
4 4
5 -// clang-format off  
6 -  
7 #include "generated_plugin_registrant.h" 5 #include "generated_plugin_registrant.h"
8 6
9 7
@@ -2,8 +2,6 @@ @@ -2,8 +2,6 @@
2 // Generated file. Do not edit. 2 // Generated file. Do not edit.
3 // 3 //
4 4
5 -// clang-format off  
6 -  
7 #ifndef GENERATED_PLUGIN_REGISTRANT_ 5 #ifndef GENERATED_PLUGIN_REGISTRANT_
8 #define GENERATED_PLUGIN_REGISTRANT_ 6 #define GENERATED_PLUGIN_REGISTRANT_
9 7
@@ -162,29 +162,24 @@ class GetInstance { @@ -162,29 +162,24 @@ class GetInstance {
162 }) { 162 }) {
163 final key = _getKey(S, name); 163 final key = _getKey(S, name);
164 164
  165 + _InstanceBuilderFactory<S>? dep;
165 if (_singl.containsKey(key)) { 166 if (_singl.containsKey(key)) {
166 - final dep = _singl[key];  
167 - if (dep != null && dep.isDirty) {  
168 - _singl[key] = _InstanceBuilderFactory<S>(  
169 - isSingleton,  
170 - builder,  
171 - permanent,  
172 - false,  
173 - fenix,  
174 - name,  
175 - lateRemove: dep as _InstanceBuilderFactory<S>,  
176 - ); 167 + final _dep = _singl[key];
  168 + if (_dep == null || !_dep.isDirty) {
  169 + return;
  170 + } else {
  171 + dep = _dep as _InstanceBuilderFactory<S>;
177 } 172 }
178 - } else {  
179 - _singl[key] = _InstanceBuilderFactory<S>(  
180 - isSingleton,  
181 - builder,  
182 - permanent,  
183 - false,  
184 - fenix,  
185 - name,  
186 - );  
187 } 173 }
  174 + _singl[key] = _InstanceBuilderFactory<S>(
  175 + isSingleton: isSingleton,
  176 + builderFunc: builder,
  177 + permanent: permanent,
  178 + isInit: false,
  179 + fenix: fenix,
  180 + tag: name,
  181 + lateRemove: dep,
  182 + );
188 } 183 }
189 184
190 /// Initializes the dependencies for a Class Instance [S] (or tag), 185 /// Initializes the dependencies for a Class Instance [S] (or tag),
@@ -519,14 +514,14 @@ class _InstanceBuilderFactory<S> { @@ -519,14 +514,14 @@ class _InstanceBuilderFactory<S> {
519 514
520 String? tag; 515 String? tag;
521 516
522 - _InstanceBuilderFactory(  
523 - this.isSingleton,  
524 - this.builderFunc,  
525 - this.permanent,  
526 - this.isInit,  
527 - this.fenix,  
528 - this.tag, {  
529 - this.lateRemove, 517 + _InstanceBuilderFactory({
  518 + required this.isSingleton,
  519 + required this.builderFunc,
  520 + required this.permanent,
  521 + required this.isInit,
  522 + required this.fenix,
  523 + required this.tag,
  524 + required this.lateRemove,
530 }); 525 });
531 526
532 void _showInitLog() { 527 void _showInitLog() {
@@ -4,6 +4,7 @@ export 'src/bottomsheet/bottomsheet.dart'; @@ -4,6 +4,7 @@ export 'src/bottomsheet/bottomsheet.dart';
4 export 'src/extension_navigation.dart'; 4 export 'src/extension_navigation.dart';
5 export 'src/nav2/get_information_parser.dart'; 5 export 'src/nav2/get_information_parser.dart';
6 export 'src/nav2/get_nav_config.dart'; 6 export 'src/nav2/get_nav_config.dart';
  7 +export 'src/nav2/get_navigator.dart';
7 export 'src/nav2/get_router_delegate.dart'; 8 export 'src/nav2/get_router_delegate.dart';
8 export 'src/nav2/router_outlet.dart'; 9 export 'src/nav2/router_outlet.dart';
9 export 'src/root/get_cupertino_app.dart'; 10 export 'src/root/get_cupertino_app.dart';
@@ -56,5 +56,5 @@ class GetNavConfig extends RouteInformation { @@ -56,5 +56,5 @@ class GetNavConfig extends RouteInformation {
56 56
57 @override 57 @override
58 String toString() => ''' 58 String toString() => '''
59 -======GetNavConfig=====\ncurrentTreeBranch: $currentTreeBranch\ncurrentPage: $currentPage\n======GetNavConfig====='''; 59 +======GetNavConfig=====\nlocation: $location\ncurrentTreeBranch: $currentTreeBranch\n======GetNavConfig=====''';
60 } 60 }
  1 +import 'package:flutter/widgets.dart';
  2 +import '../routes/default_route.dart';
  3 +import '../routes/get_route.dart';
  4 +
  5 +class GetNavigator extends Navigator {
  6 + GetNavigator.onGenerateRoute({
  7 + GlobalKey<NavigatorState>? key,
  8 + bool Function(Route<dynamic>, dynamic)? onPopPage,
  9 + required List<GetPage> pages,
  10 + List<NavigatorObserver>? observers,
  11 + bool reportsRouteUpdateToEngine = false,
  12 + TransitionDelegate? transitionDelegate,
  13 + String? initialRoute,
  14 + }) : super(
  15 + //keys should be optional
  16 + key: key,
  17 + initialRoute: initialRoute,
  18 + onPopPage: onPopPage ??
  19 + (route, result) {
  20 + final didPop = route.didPop(result);
  21 + if (!didPop) {
  22 + return false;
  23 + }
  24 + return true;
  25 + },
  26 + onGenerateRoute: (settings) {
  27 + final selectedPageList =
  28 + pages.where((element) => element.name == settings.name);
  29 + if (selectedPageList.isNotEmpty) {
  30 + final selectedPage = selectedPageList.first;
  31 + return GetPageRoute(
  32 + page: selectedPage.page,
  33 + settings: settings,
  34 + );
  35 + }
  36 + },
  37 + reportsRouteUpdateToEngine: reportsRouteUpdateToEngine,
  38 + pages: pages,
  39 + observers: [
  40 + // GetObserver(),
  41 + ...?observers,
  42 + ],
  43 + transitionDelegate:
  44 + transitionDelegate ?? const DefaultTransitionDelegate<dynamic>(),
  45 + );
  46 +
  47 + GetNavigator({
  48 + GlobalKey<NavigatorState>? key,
  49 + bool Function(Route<dynamic>, dynamic)? onPopPage,
  50 + required List<GetPage> pages,
  51 + List<NavigatorObserver>? observers,
  52 + bool reportsRouteUpdateToEngine = false,
  53 + TransitionDelegate? transitionDelegate,
  54 + String? initialRoute,
  55 + }) : super(
  56 + //keys should be optional
  57 + key: key,
  58 + initialRoute: initialRoute,
  59 + onPopPage: onPopPage ??
  60 + (route, result) {
  61 + final didPop = route.didPop(result);
  62 + if (!didPop) {
  63 + return false;
  64 + }
  65 + return true;
  66 + },
  67 + reportsRouteUpdateToEngine: reportsRouteUpdateToEngine,
  68 + pages: pages,
  69 + observers: [
  70 + // GetObserver(),
  71 + ...?observers,
  72 + ],
  73 + transitionDelegate:
  74 + transitionDelegate ?? const DefaultTransitionDelegate<dynamic>(),
  75 + );
  76 +}
@@ -2,9 +2,50 @@ import 'dart:async'; @@ -2,9 +2,50 @@ import 'dart:async';
2 2
3 import 'package:flutter/foundation.dart'; 3 import 'package:flutter/foundation.dart';
4 import 'package:flutter/material.dart'; 4 import 'package:flutter/material.dart';
5 -  
6 import '../../../get.dart'; 5 import '../../../get.dart';
7 import '../../../get_state_manager/src/simple/list_notifier.dart'; 6 import '../../../get_state_manager/src/simple/list_notifier.dart';
  7 +import 'get_navigator.dart';
  8 +
  9 +/// Enables the user to customize the intended pop behavior
  10 +///
  11 +/// Goes to either the previous history entry or the previous page entry
  12 +///
  13 +/// e.g. if the user navigates to these pages
  14 +/// 1) /home
  15 +/// 2) /home/products/1234
  16 +///
  17 +/// when popping on [History] mode, it will emulate a browser back button.
  18 +///
  19 +/// so the new history stack will be:
  20 +/// 1) /home
  21 +///
  22 +/// when popping on [Page] mode, it will only remove the last part of the route
  23 +/// so the new history stack will be:
  24 +/// 1) /home
  25 +/// 2) /home/products
  26 +///
  27 +/// another pop will change the history stack to:
  28 +/// 1) /home
  29 +enum PopMode {
  30 + History,
  31 + Page,
  32 +}
  33 +
  34 +/// Enables the user to customize the behavior when pushing multiple routes that
  35 +/// shouldn't be duplicates
  36 +enum PreventDuplicateHandlingMode {
  37 + /// Removes the history entries until it reaches the old route
  38 + PopUntilOriginalRoute,
  39 +
  40 + /// Simply don't push the new route
  41 + DoNothing,
  42 +
  43 + /// Recommended - Moves the old route entry to the front
  44 + ///
  45 + /// With this mode, you guarantee there will be only one
  46 + /// route entry for each location
  47 + ReorderRoutes
  48 +}
8 49
9 class GetDelegate extends RouterDelegate<GetNavConfig> 50 class GetDelegate extends RouterDelegate<GetNavConfig>
10 with ListNotifierSingleMixin { 51 with ListNotifierSingleMixin {
@@ -17,7 +58,10 @@ class GetDelegate extends RouterDelegate<GetNavConfig> @@ -17,7 +58,10 @@ class GetDelegate extends RouterDelegate<GetNavConfig>
17 final List<NavigatorObserver>? navigatorObservers; 58 final List<NavigatorObserver>? navigatorObservers;
18 final TransitionDelegate<dynamic>? transitionDelegate; 59 final TransitionDelegate<dynamic>? transitionDelegate;
19 60
20 - final _allCompleters = <GetPage, Completer>{}; 61 + final Iterable<GetPage> Function(GetNavConfig currentNavStack)?
  62 + pickPagesForRootNavigator;
  63 +
  64 + GlobalKey<NavigatorState> get navigatorKey => Get.key;
21 65
22 GetDelegate({ 66 GetDelegate({
23 GetPage? notFoundRoute, 67 GetPage? notFoundRoute,
@@ -26,6 +70,7 @@ class GetDelegate extends RouterDelegate<GetNavConfig> @@ -26,6 +70,7 @@ class GetDelegate extends RouterDelegate<GetNavConfig>
26 this.backButtonPopMode = PopMode.History, 70 this.backButtonPopMode = PopMode.History,
27 this.preventDuplicateHandlingMode = 71 this.preventDuplicateHandlingMode =
28 PreventDuplicateHandlingMode.ReorderRoutes, 72 PreventDuplicateHandlingMode.ReorderRoutes,
  73 + this.pickPagesForRootNavigator,
29 }) : notFoundRoute = notFoundRoute ?? 74 }) : notFoundRoute = notFoundRoute ??
30 GetPage( 75 GetPage(
31 name: '/404', 76 name: '/404',
@@ -36,191 +81,232 @@ class GetDelegate extends RouterDelegate<GetNavConfig> @@ -36,191 +81,232 @@ class GetDelegate extends RouterDelegate<GetNavConfig>
36 Get.log('GetDelegate is created !'); 81 Get.log('GetDelegate is created !');
37 } 82 }
38 83
39 - @override  
40 - GetNavConfig? get currentConfiguration {  
41 - if (history.isEmpty) return null;  
42 - final route = history.last;  
43 - return route; 84 + Future<GetNavConfig?> runMiddleware(GetNavConfig config) async {
  85 + final middlewares = config.currentTreeBranch.last.middlewares;
  86 + if (middlewares == null) {
  87 + return config;
  88 + }
  89 + var iterator = config;
  90 + for (var item in middlewares) {
  91 + var redirectRes = await item.redirectDelegate(iterator);
  92 + if (redirectRes == null) return null;
  93 + iterator = redirectRes;
  94 + }
  95 + return iterator;
44 } 96 }
45 97
46 - GlobalKey<NavigatorState> get navigatorKey => Get.key; 98 + Future<void> _unsafeHistoryAdd(GetNavConfig config) async {
  99 + final res = await runMiddleware(config);
  100 + if (res == null) return;
  101 + history.add(res);
  102 + }
47 103
48 - Map<String, String> get parameters {  
49 - return currentConfiguration?.currentPage?.parameters ?? {}; 104 + Future<void> _unsafeHistoryRemove(GetNavConfig config) async {
  105 + var index = history.indexOf(config);
  106 + if (index >= 0) await _unsafeHistoryRemoveAt(index);
  107 + }
  108 +
  109 + Future<GetNavConfig?> _unsafeHistoryRemoveAt(int index) async {
  110 + if (index == history.length - 1 && history.length > 1) {
  111 + //removing WILL update the current route
  112 + final toCheck = history[history.length - 2];
  113 + final resMiddleware = await runMiddleware(toCheck);
  114 + if (resMiddleware == null) return null;
  115 + history[history.length - 2] = resMiddleware;
  116 + }
  117 + return history.removeAt(index);
50 } 118 }
51 119
52 T arguments<T>() { 120 T arguments<T>() {
53 return currentConfiguration?.currentPage?.arguments as T; 121 return currentConfiguration?.currentPage?.arguments as T;
54 } 122 }
55 123
56 - /// Removes routes according to [PopMode]  
57 - /// until it reaches the specifc [fullRoute],  
58 - /// DOES NOT remove the [fullRoute]  
59 - Future<void> backUntil(  
60 - String fullRoute, {  
61 - PopMode popMode = PopMode.Page, 124 + Map<String, String> get parameters {
  125 + return currentConfiguration?.currentPage?.parameters ?? {};
  126 + }
  127 +
  128 + /// Adds a new history entry and waits for the result
  129 + Future<void> pushHistory(
  130 + GetNavConfig config, {
  131 + bool rebuildStack = true,
62 }) async { 132 }) async {
63 - // remove history or page entries until you meet route  
64 - var iterator = currentConfiguration;  
65 - while (_canPop(popMode) &&  
66 - iterator != null &&  
67 - iterator.location != fullRoute) {  
68 - await _pop(popMode);  
69 - // replace iterator  
70 - iterator = currentConfiguration; 133 + //this changes the currentConfiguration
  134 + await _pushHistory(config);
  135 + if (rebuildStack) {
  136 + refresh();
71 } 137 }
72 - refresh();  
73 } 138 }
74 139
75 - @override  
76 - Widget build(BuildContext context) {  
77 - final pages = getVisualPages();  
78 - if (pages.length == 0) return SizedBox.shrink();  
79 - final extraObservers = navigatorObservers;  
80 - return GetNavigator(  
81 - key: navigatorKey,  
82 - onPopPage: _onPopVisualRoute,  
83 - pages: pages,  
84 - observers: [  
85 - GetObserver(),  
86 - if (extraObservers != null) ...extraObservers,  
87 - ],  
88 - transitionDelegate:  
89 - transitionDelegate ?? const DefaultTransitionDelegate<dynamic>(),  
90 - ); 140 + Future<void> _removeHistoryEntry(GetNavConfig entry) async {
  141 + await _unsafeHistoryRemove(entry);
  142 + }
  143 +
  144 + Future<void> _pushHistory(GetNavConfig config) async {
  145 + if (config.currentPage!.preventDuplicates) {
  146 + final originalEntryIndex =
  147 + history.indexWhere((element) => element.location == config.location);
  148 + if (originalEntryIndex >= 0) {
  149 + switch (preventDuplicateHandlingMode) {
  150 + case PreventDuplicateHandlingMode.PopUntilOriginalRoute:
  151 + await backUntil(config.location!, popMode: PopMode.Page);
  152 + break;
  153 + case PreventDuplicateHandlingMode.ReorderRoutes:
  154 + await _unsafeHistoryRemoveAt(originalEntryIndex);
  155 + await _unsafeHistoryAdd(config);
  156 + break;
  157 + case PreventDuplicateHandlingMode.DoNothing:
  158 + default:
  159 + break;
  160 + }
  161 + return;
  162 + }
  163 + }
  164 + await _unsafeHistoryAdd(config);
  165 + }
  166 +
  167 + Future<GetNavConfig?> _popHistory() async {
  168 + if (!_canPopHistory()) return null;
  169 + return await _doPopHistory();
  170 + }
  171 +
  172 + Future<GetNavConfig?> _doPopHistory() async {
  173 + return await _unsafeHistoryRemoveAt(history.length - 1);
  174 + }
  175 +
  176 + Future<GetNavConfig?> _popPage() async {
  177 + if (!_canPopPage()) return null;
  178 + return await _doPopPage();
  179 + }
  180 +
  181 + Future<GetNavConfig?> _pop(PopMode mode) async {
  182 + switch (mode) {
  183 + case PopMode.History:
  184 + return await _popHistory();
  185 + case PopMode.Page:
  186 + return await _popPage();
  187 + default:
  188 + return null;
  189 + }
  190 + }
  191 +
  192 + // returns the popped page
  193 + Future<GetNavConfig?> _doPopPage() async {
  194 + final currentBranch = currentConfiguration?.currentTreeBranch;
  195 + if (currentBranch != null && currentBranch.length > 1) {
  196 + //remove last part only
  197 + final remaining = currentBranch.take(currentBranch.length - 1);
  198 + final prevHistoryEntry =
  199 + history.length > 1 ? history[history.length - 2] : null;
  200 +
  201 + //check if current route is the same as the previous route
  202 + if (prevHistoryEntry != null) {
  203 + //if so, pop the entire history entry
  204 + final newLocation = remaining.last.name;
  205 + final prevLocation = prevHistoryEntry.location;
  206 + if (newLocation == prevLocation) {
  207 + //pop the entire history entry
  208 + return await _popHistory();
  209 + }
  210 + }
  211 +
  212 + //create a new route with the remaining tree branch
  213 + final res = await _popHistory();
  214 + await _pushHistory(
  215 + GetNavConfig(
  216 + currentTreeBranch: remaining.toList(),
  217 + location: remaining.last.name,
  218 + state: null, //TOOD: persist state??
  219 + ),
  220 + );
  221 + return res;
  222 + } else {
  223 + //remove entire entry
  224 + return await _popHistory();
  225 + }
  226 + }
  227 +
  228 + Future<GetNavConfig?> popHistory() async {
  229 + return await _popHistory();
91 } 230 }
92 231
93 - // void _unsafeHistoryClear() {  
94 - // history.clear();  
95 - // } 232 + bool _canPopHistory() {
  233 + return history.length > 1;
  234 + }
96 235
97 Future<bool> canPopHistory() { 236 Future<bool> canPopHistory() {
98 return SynchronousFuture(_canPopHistory()); 237 return SynchronousFuture(_canPopHistory());
99 } 238 }
100 239
  240 + bool _canPopPage() {
  241 + final currentTreeBranch = currentConfiguration?.currentTreeBranch;
  242 + if (currentTreeBranch == null) return false;
  243 + return currentTreeBranch.length > 1 ? true : _canPopHistory();
  244 + }
  245 +
101 Future<bool> canPopPage() { 246 Future<bool> canPopPage() {
102 return SynchronousFuture(_canPopPage()); 247 return SynchronousFuture(_canPopPage());
103 } 248 }
104 249
  250 + bool _canPop(PopMode mode) {
  251 + switch (mode) {
  252 + case PopMode.History:
  253 + return _canPopHistory();
  254 + case PopMode.Page:
  255 + default:
  256 + return _canPopPage();
  257 + }
  258 + }
  259 +
105 /// gets the visual pages from the current history entry 260 /// gets the visual pages from the current history entry
106 /// 261 ///
107 - /// visual pages must have [participatesInRootNavigator] set to true  
108 - List<GetPage> getVisualPages() {  
109 - final currentHistory = currentConfiguration;  
110 - if (currentHistory == null) return <GetPage>[];  
111 - 262 + /// visual pages must have [GetPage.participatesInRootNavigator] set to true
  263 + Iterable<GetPage> getVisualPages(GetNavConfig currentHistory) {
112 final res = currentHistory.currentTreeBranch 264 final res = currentHistory.currentTreeBranch
113 .where((r) => r.participatesInRootNavigator != null); 265 .where((r) => r.participatesInRootNavigator != null);
114 if (res.length == 0) { 266 if (res.length == 0) {
115 //default behavoir, all routes participate in root navigator 267 //default behavoir, all routes participate in root navigator
116 - return history.map((e) => e.currentPage!).toList(); 268 + return history.map((e) => e.currentPage!);
117 } else { 269 } else {
118 //user specified at least one participatesInRootNavigator 270 //user specified at least one participatesInRootNavigator
119 return res 271 return res
120 - .where((element) => element.participatesInRootNavigator == true)  
121 - .toList(); 272 + .where((element) => element.participatesInRootNavigator == true);
122 } 273 }
123 } 274 }
124 275
125 - // GetPageRoute getPageRoute(RouteSettings? settings) {  
126 - // return PageRedirect(settings ?? RouteSettings(name: '/404'), _notFound())  
127 - // .page();  
128 - // } 276 + @override
  277 + Widget build(BuildContext context) {
  278 + final currentHistory = currentConfiguration;
  279 + final pages = currentHistory == null
  280 + ? <GetPage>[]
  281 + : pickPagesForRootNavigator?.call(currentHistory) ??
  282 + getVisualPages(currentHistory);
  283 + if (pages.length == 0) return SizedBox.shrink();
  284 + return GetNavigator(
  285 + key: navigatorKey,
  286 + onPopPage: _onPopVisualRoute,
  287 + pages: pages.toList(),
  288 + observers: [
  289 + GetObserver(),
  290 + ...?navigatorObservers,
  291 + ],
  292 + transitionDelegate:
  293 + transitionDelegate ?? const DefaultTransitionDelegate<dynamic>(),
  294 + );
  295 + }
129 296
130 - Future<bool> handlePopupRoutes({  
131 - Object? result,  
132 - }) async {  
133 - Route? currentRoute;  
134 - navigatorKey.currentState!.popUntil((route) {  
135 - currentRoute = route;  
136 - return true;  
137 - });  
138 - if (currentRoute is PopupRoute) {  
139 - return await navigatorKey.currentState!.maybePop(result);  
140 - }  
141 - return false;  
142 - }  
143 -  
144 - Future<T?>? offAndToNamed<T>(  
145 - String page, {  
146 - dynamic arguments,  
147 - int? id,  
148 - dynamic result,  
149 - Map<String, String>? parameters,  
150 - PopMode popMode = PopMode.History,  
151 - }) async {  
152 - if (parameters != null) {  
153 - final uri = Uri(path: page, queryParameters: parameters);  
154 - page = uri.toString();  
155 - }  
156 -  
157 - await popRoute(result: result);  
158 - return toNamed(page, arguments: arguments, parameters: parameters);  
159 - }  
160 -  
161 - Future<T> offNamed<T>(  
162 - String page, {  
163 - dynamic arguments,  
164 - Map<String, String>? parameters,  
165 - }) async {  
166 - history.removeLast();  
167 - return toNamed<T>(page, arguments: arguments, parameters: parameters);  
168 - }  
169 -  
170 - Future<GetNavConfig?> popHistory() async {  
171 - return await _popHistory();  
172 - }  
173 -  
174 - // returns the popped page  
175 @override 297 @override
176 - Future<bool> popRoute({  
177 - Object? result,  
178 - PopMode popMode = PopMode.Page,  
179 - }) async {  
180 - //Returning false will cause the entire app to be popped.  
181 - final wasPopup = await handlePopupRoutes(result: result);  
182 - if (wasPopup) return true;  
183 - final _popped = await _pop(popMode);  
184 - refresh();  
185 - if (_popped != null) {  
186 - //emulate the old pop with result  
187 - return true;  
188 - }  
189 - return false;  
190 - }  
191 -  
192 - /// Adds a new history entry and waits for the result  
193 - Future<void> pushHistory(  
194 - GetNavConfig config, {  
195 - bool rebuildStack = true,  
196 - }) async {  
197 - //this changes the currentConfiguration  
198 - await _pushHistory(config);  
199 - if (rebuildStack) {  
200 - refresh();  
201 - }  
202 - }  
203 -  
204 - Future<GetNavConfig?> runMiddleware(GetNavConfig config) async {  
205 - final middlewares = config.currentTreeBranch.last.middlewares;  
206 - if (middlewares == null) {  
207 - return config;  
208 - }  
209 - var iterator = config;  
210 - for (var item in middlewares) {  
211 - var redirectRes = await item.redirectDelegate(iterator);  
212 - if (redirectRes == null) return null;  
213 - iterator = redirectRes;  
214 - }  
215 - return iterator; 298 + Future<void> setNewRoutePath(GetNavConfig configuration) async {
  299 + await pushHistory(configuration);
216 } 300 }
217 301
218 @override 302 @override
219 - Future<void> setNewRoutePath(GetNavConfig configuration) async {  
220 - await pushHistory(configuration); 303 + GetNavConfig? get currentConfiguration {
  304 + if (history.isEmpty) return null;
  305 + final route = history.last;
  306 + return route;
221 } 307 }
222 308
223 - Future<T> toNamed<T>( 309 + Future<void> toNamed(
224 String page, { 310 String page, {
225 dynamic arguments, 311 dynamic arguments,
226 Map<String, String>? parameters, 312 Map<String, String>? parameters,
@@ -233,10 +319,7 @@ class GetDelegate extends RouterDelegate<GetNavConfig> @@ -233,10 +319,7 @@ class GetDelegate extends RouterDelegate<GetNavConfig>
233 final decoder = Get.routeTree.matchRoute(page, arguments: arguments); 319 final decoder = Get.routeTree.matchRoute(page, arguments: arguments);
234 decoder.replaceArguments(arguments); 320 decoder.replaceArguments(arguments);
235 321
236 - final completer = Completer<T>();  
237 -  
238 if (decoder.route != null) { 322 if (decoder.route != null) {
239 - _allCompleters[decoder.route!] = completer;  
240 await pushHistory( 323 await pushHistory(
241 GetNavConfig( 324 GetNavConfig(
242 currentTreeBranch: decoder.treeBranch, 325 currentTreeBranch: decoder.treeBranch,
@@ -244,80 +327,76 @@ class GetDelegate extends RouterDelegate<GetNavConfig> @@ -244,80 +327,76 @@ class GetDelegate extends RouterDelegate<GetNavConfig>
244 state: null, //TODO: persist state? 327 state: null, //TODO: persist state?
245 ), 328 ),
246 ); 329 );
247 -  
248 - return completer.future;  
249 } else { 330 } else {
250 - ///TODO: IMPLEMENT ROUTE NOT FOUND  
251 -  
252 - return Future.value();  
253 - }  
254 - }  
255 -  
256 - bool _canPop(PopMode mode) {  
257 - switch (mode) {  
258 - case PopMode.History:  
259 - return _canPopHistory();  
260 - case PopMode.Page:  
261 - default:  
262 - return _canPopPage(); 331 + await pushHistory(
  332 + GetNavConfig(
  333 + currentTreeBranch: [notFoundRoute],
  334 + location: notFoundRoute.name,
  335 + state: null, //TODO: persist state?
  336 + ),
  337 + );
263 } 338 }
264 } 339 }
265 340
266 - bool _canPopHistory() {  
267 - return history.length > 1; 341 + //pops the previous route (if there is one) and goes to new route
  342 + Future<void> offNamed(
  343 + String page, {
  344 + dynamic arguments,
  345 + Map<String, String>? parameters,
  346 + PopMode popMode = PopMode.History,
  347 + }) async {
  348 + await popRoute(popMode: popMode);
  349 + return toNamed(page, arguments: arguments, parameters: parameters);
268 } 350 }
269 351
270 - bool _canPopPage() {  
271 - final currentTreeBranch = currentConfiguration?.currentTreeBranch;  
272 - if (currentTreeBranch == null) return false;  
273 - return currentTreeBranch.length > 1 ? true : _canPopHistory(); 352 + /// Removes routes according to [PopMode]
  353 + /// until it reaches the specifc [fullRoute],
  354 + /// DOES NOT remove the [fullRoute]
  355 + Future<void> backUntil(
  356 + String fullRoute, {
  357 + PopMode popMode = PopMode.History,
  358 + }) async {
  359 + // remove history or page entries until you meet route
  360 + var iterator = currentConfiguration;
  361 + while (_canPop(popMode) &&
  362 + iterator != null &&
  363 + iterator.location != fullRoute) {
  364 + await _pop(popMode);
  365 + // replace iterator
  366 + iterator = currentConfiguration;
  367 + }
  368 + refresh();
274 } 369 }
275 370
276 - Future<GetNavConfig?> _doPopHistory() async {  
277 - return await _unsafeHistoryRemoveAt(history.length - 1); 371 + Future<bool> handlePopupRoutes({
  372 + Object? result,
  373 + }) async {
  374 + Route? currentRoute;
  375 + navigatorKey.currentState!.popUntil((route) {
  376 + currentRoute = route;
  377 + return true;
  378 + });
  379 + if (currentRoute is PopupRoute) {
  380 + return await navigatorKey.currentState!.maybePop(result);
  381 + }
  382 + return false;
278 } 383 }
279 384
280 - // @override  
281 - // Future<void> setInitialRoutePath(GetNavConfig configuration) async {  
282 - // //no need to clear history with Reorder route strategy  
283 - // // _unsafeHistoryClear();  
284 - // // _resultCompleter.clear();  
285 - // await pushHistory(configuration);  
286 - // }  
287 -  
288 - Future<GetNavConfig?> _doPopPage() async {  
289 - final currentBranch = currentConfiguration?.currentTreeBranch;  
290 - if (currentBranch != null && currentBranch.length > 1) {  
291 - //remove last part only  
292 - final remaining = currentBranch.take(currentBranch.length - 1);  
293 - final prevHistoryEntry =  
294 - history.length > 1 ? history[history.length - 2] : null;  
295 -  
296 - //check if current route is the same as the previous route  
297 - if (prevHistoryEntry != null) {  
298 - //if so, pop the entire history entry  
299 - final newLocation = remaining.last.name;  
300 - final prevLocation = prevHistoryEntry.location;  
301 - if (newLocation == prevLocation) {  
302 - //pop the entire history entry  
303 - return await _popHistory();  
304 - }  
305 - }  
306 -  
307 - //create a new route with the remaining tree branch  
308 - final res = await _popHistory();  
309 - await _pushHistory(  
310 - GetNavConfig(  
311 - currentTreeBranch: remaining.toList(),  
312 - location: remaining.last.name,  
313 - state: null, //TOOD: persist state??  
314 - ),  
315 - );  
316 - return res;  
317 - } else {  
318 - //remove entire entry  
319 - return await _popHistory(); 385 + @override
  386 + Future<bool> popRoute({
  387 + Object? result,
  388 + PopMode? popMode,
  389 + }) async {
  390 + //Returning false will cause the entire app to be popped.
  391 + final wasPopup = await handlePopupRoutes(result: result);
  392 + if (wasPopup) return true;
  393 + final _popped = await _pop(popMode ?? backButtonPopMode);
  394 + refresh();
  395 + if (_popped != null) {
  396 + //emulate the old pop with result
  397 + return true;
320 } 398 }
  399 + return false;
321 } 400 }
322 401
323 bool _onPopVisualRoute(Route<dynamic> route, dynamic result) { 402 bool _onPopVisualRoute(Route<dynamic> route, dynamic result) {
@@ -334,153 +413,9 @@ class GetDelegate extends RouterDelegate<GetNavConfig> @@ -334,153 +413,9 @@ class GetDelegate extends RouterDelegate<GetNavConfig>
334 if (config != null) { 413 if (config != null) {
335 _removeHistoryEntry(config); 414 _removeHistoryEntry(config);
336 } 415 }
337 - if (_allCompleters.containsKey(settings)) {  
338 - _allCompleters[settings]?.complete(route.popped);  
339 - }  
340 } 416 }
341 refresh(); 417 refresh();
342 418
343 return true; 419 return true;
344 } 420 }
345 -  
346 - Future<GetNavConfig?> _pop(PopMode mode) async {  
347 - switch (mode) {  
348 - case PopMode.History:  
349 - return await _popHistory();  
350 - case PopMode.Page:  
351 - return await _popPage();  
352 - default:  
353 - return null;  
354 - }  
355 - }  
356 -  
357 - Future<GetNavConfig?> _popHistory() async {  
358 - if (!_canPopHistory()) return null;  
359 - return await _doPopHistory();  
360 - }  
361 -  
362 - Future<GetNavConfig?> _popPage() async {  
363 - if (!_canPopPage()) return null;  
364 - return await _doPopPage();  
365 - }  
366 -  
367 - Future<void> _pushHistory(GetNavConfig config) async {  
368 - if (config.currentPage!.preventDuplicates) {  
369 - final originalEntryIndex =  
370 - history.indexWhere((element) => element.location == config.location);  
371 - if (originalEntryIndex >= 0) {  
372 - switch (preventDuplicateHandlingMode) {  
373 - case PreventDuplicateHandlingMode.PopUntilOriginalRoute:  
374 - await backUntil(config.location!, popMode: PopMode.Page);  
375 - break;  
376 - case PreventDuplicateHandlingMode.ReorderRoutes:  
377 - await _unsafeHistoryRemoveAt(originalEntryIndex);  
378 - await _unsafeHistoryAdd(config);  
379 - break;  
380 - case PreventDuplicateHandlingMode.DoNothing:  
381 - default:  
382 - break;  
383 - }  
384 - return;  
385 - }  
386 - }  
387 - await _unsafeHistoryAdd(config);  
388 - }  
389 -  
390 - Future<void> _removeHistoryEntry(GetNavConfig entry) async {  
391 - await _unsafeHistoryRemove(entry);  
392 - }  
393 -  
394 - Future<void> _unsafeHistoryAdd(GetNavConfig config) async {  
395 - final res = await runMiddleware(config);  
396 - if (res == null) return;  
397 - history.add(res);  
398 - }  
399 -  
400 - Future<void> _unsafeHistoryRemove(GetNavConfig config) async {  
401 - var index = history.indexOf(config);  
402 - if (index >= 0) await _unsafeHistoryRemoveAt(index);  
403 - }  
404 -  
405 - Future<GetNavConfig?> _unsafeHistoryRemoveAt(int index) async {  
406 - if (index == history.length - 1 && history.length > 1) {  
407 - //removing WILL update the current route  
408 - final toCheck = history[history.length - 2];  
409 - final resMiddleware = await runMiddleware(toCheck);  
410 - if (resMiddleware == null) return null;  
411 - history[history.length - 2] = resMiddleware;  
412 - }  
413 - return history.removeAt(index);  
414 - }  
415 -}  
416 -  
417 -class GetNavigator extends Navigator {  
418 - GetNavigator({  
419 - GlobalKey<NavigatorState>? key,  
420 - bool Function(Route<dynamic>, dynamic)? onPopPage,  
421 - required List<Page> pages,  
422 - List<NavigatorObserver>? observers,  
423 - bool reportsRouteUpdateToEngine = false,  
424 - TransitionDelegate? transitionDelegate,  
425 - }) : super(  
426 - //keys should be optional  
427 - key: key,  
428 - onPopPage: onPopPage ??  
429 - (route, result) {  
430 - final didPop = route.didPop(result);  
431 - if (!didPop) {  
432 - return false;  
433 - }  
434 - return true;  
435 - },  
436 - reportsRouteUpdateToEngine: reportsRouteUpdateToEngine,  
437 - pages: pages,  
438 - observers: [  
439 - // GetObserver(),  
440 - if (observers != null) ...observers,  
441 - ],  
442 - transitionDelegate:  
443 - transitionDelegate ?? const DefaultTransitionDelegate<dynamic>(),  
444 - );  
445 -}  
446 -  
447 -/// Enables the user to customize the intended pop behavior  
448 -///  
449 -/// Goes to either the previous history entry or the previous page entry  
450 -///  
451 -/// e.g. if the user navigates to these pages  
452 -/// 1) /home  
453 -/// 2) /home/products/1234  
454 -///  
455 -/// when popping on [History] mode, it will emulate a browser back button.  
456 -///  
457 -/// so the new history stack will be:  
458 -/// 1) /home  
459 -///  
460 -/// when popping on [Page] mode, it will only remove the last part of the route  
461 -/// so the new history stack will be:  
462 -/// 1) /home  
463 -/// 2) /home/products  
464 -///  
465 -/// another pop will change the history stack to:  
466 -/// 1) /home  
467 -enum PopMode {  
468 - History,  
469 - Page,  
470 -}  
471 -  
472 -/// Enables the user to customize the behavior when pushing multiple routes that  
473 -/// shouldn't be duplicates  
474 -enum PreventDuplicateHandlingMode {  
475 - /// Removes the history entries until it reaches the old route  
476 - PopUntilOriginalRoute,  
477 -  
478 - /// Simply don't push the new route  
479 - DoNothing,  
480 -  
481 - /// Recommended - Moves the old route entry to the front  
482 - ///  
483 - /// With this mode, you guarantee there will be only one  
484 - /// route entry for each location  
485 - ReorderRoutes  
486 } 421 }
1 import 'package:flutter/widgets.dart'; 1 import 'package:flutter/widgets.dart';
2 2
  3 +import 'default_route.dart';
  4 +
3 enum Transition { 5 enum Transition {
4 fade, 6 fade,
5 fadeIn, 7 fadeIn,
@@ -20,3 +22,4 @@ enum Transition { @@ -20,3 +22,4 @@ enum Transition {
20 } 22 }
21 23
22 typedef GetPageBuilder = Widget Function(); 24 typedef GetPageBuilder = Widget Function();
  25 +typedef GetRouteAwarePageBuilder<T> = Widget Function([GetPageRoute<T>? route]);
@@ -569,7 +569,4 @@ class BindError<T> extends Error { @@ -569,7 +569,4 @@ class BindError<T> extends Error {
569 /// instance of Bindings to manage the 569 /// instance of Bindings to manage the
570 /// dependencies() (via Get.put()) for the Route you are opening. 570 /// dependencies() (via Get.put()) for the Route you are opening.
571 // ignore: one_member_abstracts 571 // ignore: one_member_abstracts
572 -abstract class Binding extends BindingsInterface<List<Bind>> {  
573 - @override  
574 - List<Bind> dependencies();  
575 -} 572 +abstract class Binding extends BindingsInterface<Iterable<Bind>> {}
@@ -24,36 +24,32 @@ typedef ValueBuilderBuilder<T> = Widget Function( @@ -24,36 +24,32 @@ typedef ValueBuilderBuilder<T> = Widget Function(
24 /// ), 24 /// ),
25 /// ``` 25 /// ```
26 class ValueBuilder<T> extends StatefulWidget { 26 class ValueBuilder<T> extends StatefulWidget {
27 - final T? initialValue; 27 + final T initialValue;
28 final ValueBuilderBuilder<T> builder; 28 final ValueBuilderBuilder<T> builder;
29 final void Function()? onDispose; 29 final void Function()? onDispose;
30 final void Function(T)? onUpdate; 30 final void Function(T)? onUpdate;
31 31
32 const ValueBuilder({ 32 const ValueBuilder({
33 Key? key, 33 Key? key,
34 - this.initialValue, 34 + required this.initialValue,
35 this.onDispose, 35 this.onDispose,
36 this.onUpdate, 36 this.onUpdate,
37 required this.builder, 37 required this.builder,
38 }) : super(key: key); 38 }) : super(key: key);
39 39
40 @override 40 @override
41 - _ValueBuilderState<T> createState() => _ValueBuilderState<T>(); 41 + _ValueBuilderState<T> createState() => _ValueBuilderState<T>(initialValue);
42 } 42 }
43 43
44 -class _ValueBuilderState<T> extends State<ValueBuilder<T?>> {  
45 - T? value; 44 +class _ValueBuilderState<T> extends State<ValueBuilder<T>> {
  45 + T value;
  46 + _ValueBuilderState(this.value);
46 47
47 - @override  
48 - void initState() {  
49 - super.initState();  
50 - value = widget.initialValue;  
51 - }  
52 48
53 @override 49 @override
54 Widget build(BuildContext context) => widget.builder(value, updater); 50 Widget build(BuildContext context) => widget.builder(value, updater);
55 51
56 - void updater(T? newValue) { 52 + void updater(T newValue) {
57 if (widget.onUpdate != null) { 53 if (widget.onUpdate != null) {
58 widget.onUpdate!(newValue); 54 widget.onUpdate!(newValue);
59 } 55 }
@@ -71,7 +67,6 @@ class _ValueBuilderState<T> extends State<ValueBuilder<T?>> { @@ -71,7 +67,6 @@ class _ValueBuilderState<T> extends State<ValueBuilder<T?>> {
71 } else if (value is StreamController) { 67 } else if (value is StreamController) {
72 (value as StreamController?)?.close(); 68 (value as StreamController?)?.close();
73 } 69 }
74 - value = null;  
75 } 70 }
76 } 71 }
77 72
  1 +import 'package:collection/collection.dart';
1 import 'package:flutter/material.dart'; 2 import 'package:flutter/material.dart';
2 3
3 -import '../platform/platform.dart';  
4 -  
5 extension ContextExtensionss on BuildContext { 4 extension ContextExtensionss on BuildContext {
6 /// The same of [MediaQuery.of(context).size] 5 /// The same of [MediaQuery.of(context).size]
7 Size get mediaQuerySize => MediaQuery.of(this).size; 6 Size get mediaQuerySize => MediaQuery.of(this).size;
@@ -100,17 +99,44 @@ extension ContextExtensionss on BuildContext { @@ -100,17 +99,44 @@ extension ContextExtensionss on BuildContext {
100 /// True if width be larger than 800 99 /// True if width be larger than 800
101 bool get showNavbar => (width > 800); 100 bool get showNavbar => (width > 800);
102 101
103 - /// True if the shortestSide is smaller than 600p  
104 - bool get isPhone => (mediaQueryShortestSide < 600); 102 + /// True if the width is smaller than 600p
  103 + bool get isPhoneOrLess => width <= 600;
  104 +
  105 + /// True if the width is higher than 600p
  106 + bool get isPhoneOrWider => width >= 600;
  107 +
  108 + /// same as [isPhoneOrLess]
  109 + bool get isPhone => isPhoneOrLess;
  110 +
  111 + /// True if the width is smaller than 600p
  112 + bool get isSmallTabletOrLess => width <= 600;
  113 +
  114 + /// True if the width is higher than 600p
  115 + bool get isSmallTabletOrWider => width >= 600;
  116 +
  117 + /// same as [isSmallTabletOrLess]
  118 + bool get isSmallTablet => isSmallTabletOrLess;
105 119
106 - /// True if the shortestSide is largest than 600p  
107 - bool get isSmallTablet => (mediaQueryShortestSide >= 600); 120 + /// True if the width is smaller than 720p
  121 + bool get isLargeTabletOrLess => width <= 720;
108 122
109 - /// True if the shortestSide is largest than 720p  
110 - bool get isLargeTablet => (mediaQueryShortestSide >= 720); 123 + /// True if the width is higher than 720p
  124 + bool get isLargeTabletOrWider => width >= 720;
  125 +
  126 + /// same as [isLargeTabletOrLess]
  127 + bool get isLargeTablet => isLargeTabletOrLess;
111 128
112 /// True if the current device is Tablet 129 /// True if the current device is Tablet
113 - bool get isTablet => isSmallTablet || isLargeTablet; 130 + bool get isTablet => isSmallTablet;
  131 +
  132 + /// True if the width is smaller than 1200p
  133 + bool get isDesktopOrLess => width <= 1200;
  134 +
  135 + /// True if the width is higher than 1200p
  136 + bool get isDesktopOrWider => width >= 1200;
  137 +
  138 + /// same as [isDesktopOrLess]
  139 + bool get isDesktop => isDesktopOrLess;
114 140
115 /// Returns a specific value according to the screen size 141 /// Returns a specific value according to the screen size
116 /// if the device width is higher than or equal to 1200 return 142 /// if the device width is higher than or equal to 1200 return
@@ -119,23 +145,28 @@ extension ContextExtensionss on BuildContext { @@ -119,23 +145,28 @@ extension ContextExtensionss on BuildContext {
119 /// if the device width is less than 300 return [watch] value. 145 /// if the device width is less than 300 return [watch] value.
120 /// in other cases return [mobile] value. 146 /// in other cases return [mobile] value.
121 T responsiveValue<T>({ 147 T responsiveValue<T>({
  148 + T? watch,
122 T? mobile, 149 T? mobile,
123 T? tablet, 150 T? tablet,
124 T? desktop, 151 T? desktop,
125 - T? watch,  
126 }) { 152 }) {
127 - var deviceWidth = mediaQuerySize.shortestSide;  
128 - if (GetPlatform.isDesktop) {  
129 - deviceWidth = mediaQuerySize.width;  
130 - }  
131 - if (deviceWidth >= 1200 && desktop != null) {  
132 - return desktop;  
133 - } else if (deviceWidth >= 600 && tablet != null) {  
134 - return tablet;  
135 - } else if (deviceWidth < 300 && watch != null) {  
136 - return watch;  
137 - } else {  
138 - return mobile!;  
139 - } 153 + assert(
  154 + watch != null || mobile != null || tablet != null || desktop != null);
  155 +
  156 + var deviceWidth = mediaQuerySize.width;
  157 + //big screen width can display smaller sizes
  158 + final strictValues = [
  159 + if (deviceWidth >= 1200) desktop, //desktop is allowed
  160 + if (deviceWidth >= 600) tablet, //tablet is allowed
  161 + if (deviceWidth >= 300) mobile, //mobile is allowed
  162 + watch, //watch is allowed
  163 + ].whereType<T>();
  164 + final looseValues = [
  165 + watch,
  166 + mobile,
  167 + tablet,
  168 + desktop,
  169 + ].whereType<T>();
  170 + return strictValues.firstOrNull ?? looseValues.first;
140 } 171 }
141 } 172 }
@@ -34,16 +34,19 @@ void main() { @@ -34,16 +34,19 @@ void main() {
34 expect(isLandscape, context.isLandscape); 34 expect(isLandscape, context.isLandscape);
35 var mediaQueryShortestSide = mediaQuerySize.shortestSide; 35 var mediaQueryShortestSide = mediaQuerySize.shortestSide;
36 expect(mediaQueryShortestSide, context.mediaQueryShortestSide); 36 expect(mediaQueryShortestSide, context.mediaQueryShortestSide);
37 - var isLargeTablet = (mediaQueryShortestSide >= 720);  
38 - expect(isLargeTablet, context.isLargeTablet);  
39 - var isPhone = (mediaQueryShortestSide < 600);  
40 - expect(isPhone, context.isPhone); 37 + var width = mediaQuerySize.width;
  38 + expect(width, context.width);
  39 +
  40 + var isLargeTabletOrWider = (width >= 720);
  41 + expect(isLargeTabletOrWider, context.isLargeTabletOrWider);
  42 + var isPhoneOrLess = (width < 600);
  43 + expect(isPhoneOrLess, context.isPhoneOrLess);
41 var isPortrait = orientation == Orientation.portrait; 44 var isPortrait = orientation == Orientation.portrait;
42 expect(isPortrait, context.isPortrait); 45 expect(isPortrait, context.isPortrait);
43 - var isSmallTablet = (mediaQueryShortestSide >= 600);  
44 - expect(isSmallTablet, context.isSmallTablet);  
45 - var isTablet = isSmallTablet || isLargeTablet;  
46 - expect(isTablet, context.isTablet); 46 + var isSmallTabletOrWider = (width >= 600);
  47 + expect(isSmallTabletOrWider, context.isSmallTabletOrWider);
  48 + var isTablet = isSmallTabletOrWider || isLargeTabletOrWider;
  49 + expect(isTablet, context.isSmallTabletOrWider);
47 var mediaQueryPadding = mediaQuery.padding; 50 var mediaQueryPadding = mediaQuery.padding;
48 expect(mediaQueryPadding, context.mediaQueryPadding); 51 expect(mediaQueryPadding, context.mediaQueryPadding);
49 var mediaQueryViewInsets = mediaQuery.viewInsets; 52 var mediaQueryViewInsets = mediaQuery.viewInsets;
@@ -55,8 +58,7 @@ void main() { @@ -55,8 +58,7 @@ void main() {
55 expect(widthTransformer, context.widthTransformer()); 58 expect(widthTransformer, context.widthTransformer());
56 var ratio = heightTransformer / widthTransformer; 59 var ratio = heightTransformer / widthTransformer;
57 expect(ratio, context.ratio()); 60 expect(ratio, context.ratio());
58 - var width = mediaQuerySize.width;  
59 - expect(width, context.width); 61 +
60 var showNavbar = (width > 800); 62 var showNavbar = (width > 800);
61 expect(showNavbar, context.showNavbar); 63 expect(showNavbar, context.showNavbar);
62 var textScaleFactor = mediaQuery.textScaleFactor; 64 var textScaleFactor = mediaQuery.textScaleFactor;