Committed by
GitHub
Merge pull request #2087 from Bdaya-Dev/master
Minor improvements with small breaking changes
Showing
16 changed files
with
539 additions
and
439 deletions
@@ -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 | #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; |
-
Please register or login to post a comment