+Fixed middleware parsing
+Added redirectDelegate to GetMiddleware, which simplifies creating route guards +Introduced `participatesInRootNavigator` which replaces the old `RouterOutletContainerMiddleWare` +Introduced `PreventDuplicateHandlingMode.ReorderRoutes` and made it the default recommended option +Added route guard example to `example_nav2 `
Showing
20 changed files
with
396 additions
and
97 deletions
1 | +import 'package:get/get.dart'; | ||
2 | + | ||
3 | +import '../../services/auth_service.dart'; | ||
4 | +import '../routes/app_pages.dart'; | ||
5 | + | ||
6 | +class EnsureAuthMiddleware extends GetMiddleware { | ||
7 | + @override | ||
8 | + GetNavConfig? redirectDelegate(GetNavConfig route) { | ||
9 | + if (!AuthService.to.isLoggedInValue) { | ||
10 | + final newRoute = Routes.LOGIN_THEN(route.location!); | ||
11 | + return GetNavConfig.fromRoute(newRoute); | ||
12 | + } | ||
13 | + return super.redirectDelegate(route); | ||
14 | + } | ||
15 | +} | ||
16 | + | ||
17 | +class EnsureNotAuthedMiddleware extends GetMiddleware { | ||
18 | + @override | ||
19 | + GetNavConfig? redirectDelegate(GetNavConfig route) { | ||
20 | + if (AuthService.to.isLoggedInValue) { | ||
21 | + //NEVER navigate to auth screen, when user is already authed | ||
22 | + return null; | ||
23 | + | ||
24 | + //OR redirect user to another screen | ||
25 | + //return GetNavConfig.fromRoute(Routes.PROFILE); | ||
26 | + } | ||
27 | + return super.redirectDelegate(route); | ||
28 | + } | ||
29 | +} |
1 | +import 'package:get/get.dart'; | ||
2 | + | ||
3 | +class LoginController extends GetxController { | ||
4 | + //TODO: Implement LoginController | ||
5 | + | ||
6 | + final count = 0.obs; | ||
7 | + @override | ||
8 | + void onInit() { | ||
9 | + super.onInit(); | ||
10 | + } | ||
11 | + | ||
12 | + @override | ||
13 | + void onReady() { | ||
14 | + super.onReady(); | ||
15 | + } | ||
16 | + | ||
17 | + @override | ||
18 | + void onClose() {} | ||
19 | + void increment() => count.value++; | ||
20 | +} |
1 | +import 'package:example_nav2/app/routes/app_pages.dart'; | ||
2 | +import 'package:example_nav2/services/auth_service.dart'; | ||
3 | +import 'package:flutter/material.dart'; | ||
4 | + | ||
5 | +import 'package:get/get.dart'; | ||
6 | + | ||
7 | +import '../controllers/login_controller.dart'; | ||
8 | + | ||
9 | +class LoginView extends GetView<LoginController> { | ||
10 | + @override | ||
11 | + Widget build(BuildContext context) { | ||
12 | + return Scaffold( | ||
13 | + body: Center( | ||
14 | + child: Column( | ||
15 | + mainAxisSize: MainAxisSize.min, | ||
16 | + children: [ | ||
17 | + Obx( | ||
18 | + () { | ||
19 | + final isLoggedIn = AuthService.to.isLoggedInValue; | ||
20 | + return Text( | ||
21 | + 'You are currently:' | ||
22 | + ' ${isLoggedIn ? "Logged In" : "Not Logged In"}' | ||
23 | + "\nIt's impossible to enter this " | ||
24 | + "route when you are logged in!", | ||
25 | + ); | ||
26 | + }, | ||
27 | + ), | ||
28 | + MaterialButton( | ||
29 | + child: Text( | ||
30 | + 'Do LOGIN !!', | ||
31 | + style: TextStyle(color: Colors.blue, fontSize: 20), | ||
32 | + ), | ||
33 | + onPressed: () { | ||
34 | + AuthService.to.login(); | ||
35 | + final thenTo = Get.getDelegate()! | ||
36 | + .currentConfiguration! | ||
37 | + .currentPage! | ||
38 | + .parameter?['then']; | ||
39 | + Get.getDelegate()!.toNamed(thenTo ?? Routes.HOME); | ||
40 | + }, | ||
41 | + ), | ||
42 | + ], | ||
43 | + ), | ||
44 | + ), | ||
45 | + ); | ||
46 | + } | ||
47 | +} |
1 | +import 'package:example_nav2/models/demo_product.dart'; | ||
1 | import 'package:get/get.dart'; | 2 | import 'package:get/get.dart'; |
2 | 3 | ||
3 | -import '../../../models/demo_product.dart'; | ||
4 | - | ||
5 | class ProductsController extends GetxController { | 4 | class ProductsController extends GetxController { |
6 | final products = <DemoProduct>[].obs; | 5 | final products = <DemoProduct>[].obs; |
7 | 6 |
1 | -import 'package:flutter/material.dart'; | ||
2 | - | ||
3 | -import 'package:get/get.dart'; | ||
4 | - | ||
5 | -import '../controllers/profile_controller.dart'; | ||
6 | - | ||
7 | -class ProfileView extends GetView<ProfileController> { | ||
8 | - @override | ||
9 | - Widget build(BuildContext context) { | ||
10 | - return Scaffold( | ||
11 | - body: Center( | ||
12 | - child: Text( | ||
13 | - 'ProfileView is working', | ||
14 | - style: TextStyle(fontSize: 20), | ||
15 | - ), | ||
16 | - ), | ||
17 | - ); | ||
18 | - } | ||
19 | -} | 1 | +import 'package:flutter/material.dart'; |
2 | + | ||
3 | +import 'package:get/get.dart'; | ||
4 | + | ||
5 | +import '../controllers/profile_controller.dart'; | ||
6 | + | ||
7 | +class ProfileView extends GetView<ProfileController> { | ||
8 | + @override | ||
9 | + Widget build(BuildContext context) { | ||
10 | + return Scaffold( | ||
11 | + backgroundColor: Colors.amber, | ||
12 | + body: Center( | ||
13 | + child: Text( | ||
14 | + 'ProfileView is working', | ||
15 | + style: TextStyle(fontSize: 20), | ||
16 | + ), | ||
17 | + ), | ||
18 | + ); | ||
19 | + } | ||
20 | +} |
1 | +import 'package:example_nav2/services/auth_service.dart'; | ||
1 | import 'package:flutter/material.dart'; | 2 | import 'package:flutter/material.dart'; |
2 | import 'package:get/get.dart'; | 3 | import 'package:get/get.dart'; |
3 | 4 | ||
@@ -35,6 +36,37 @@ class DrawerWidget extends StatelessWidget { | @@ -35,6 +36,37 @@ class DrawerWidget extends StatelessWidget { | ||
35 | Navigator.of(context).pop(); | 36 | Navigator.of(context).pop(); |
36 | }, | 37 | }, |
37 | ), | 38 | ), |
39 | + if (AuthService.to.isLoggedInValue) | ||
40 | + ListTile( | ||
41 | + title: Text( | ||
42 | + 'Logout', | ||
43 | + style: TextStyle( | ||
44 | + color: Colors.red, | ||
45 | + ), | ||
46 | + ), | ||
47 | + onTap: () { | ||
48 | + AuthService.to.logout(); | ||
49 | + Get.getDelegate()!.toNamed(Routes.LOGIN); | ||
50 | + //to close the drawer | ||
51 | + | ||
52 | + Navigator.of(context).pop(); | ||
53 | + }, | ||
54 | + ), | ||
55 | + if (!AuthService.to.isLoggedInValue) | ||
56 | + ListTile( | ||
57 | + title: Text( | ||
58 | + 'Login', | ||
59 | + style: TextStyle( | ||
60 | + color: Colors.blue, | ||
61 | + ), | ||
62 | + ), | ||
63 | + onTap: () { | ||
64 | + Get.getDelegate()!.toNamed(Routes.LOGIN); | ||
65 | + //to close the drawer | ||
66 | + | ||
67 | + Navigator.of(context).pop(); | ||
68 | + }, | ||
69 | + ), | ||
38 | ], | 70 | ], |
39 | ), | 71 | ), |
40 | ); | 72 | ); |
1 | +import 'package:example_nav2/app/middleware/auth_middleware.dart'; | ||
1 | import 'package:get/get.dart'; | 2 | import 'package:get/get.dart'; |
2 | import 'package:get/get_navigation/src/nav2/router_outlet.dart'; | 3 | import 'package:get/get_navigation/src/nav2/router_outlet.dart'; |
3 | 4 | ||
5 | +import 'package:example_nav2/app/modules/login/bindings/login_binding.dart'; | ||
6 | +import 'package:example_nav2/app/modules/login/views/login_view.dart'; | ||
7 | + | ||
4 | import '../modules/home/bindings/home_binding.dart'; | 8 | import '../modules/home/bindings/home_binding.dart'; |
5 | import '../modules/home/views/home_view.dart'; | 9 | import '../modules/home/views/home_view.dart'; |
6 | import '../modules/product_details/bindings/product_details_binding.dart'; | 10 | import '../modules/product_details/bindings/product_details_binding.dart'; |
@@ -25,14 +29,21 @@ class AppPages { | @@ -25,14 +29,21 @@ class AppPages { | ||
25 | GetPage( | 29 | GetPage( |
26 | name: '/', | 30 | name: '/', |
27 | page: () => RootView(), | 31 | page: () => RootView(), |
28 | - middlewares: [ | ||
29 | - RouterOutletContainerMiddleWare('/'), | ||
30 | - ], | ||
31 | binding: RootBinding(), | 32 | binding: RootBinding(), |
33 | + participatesInRootNavigator: true, | ||
32 | children: [ | 34 | children: [ |
33 | GetPage( | 35 | GetPage( |
36 | + middlewares: [ | ||
37 | + //only enter this route when not authed | ||
38 | + EnsureNotAuthedMiddleware(), | ||
39 | + ], | ||
40 | + name: _Paths.LOGIN, | ||
41 | + page: () => LoginView(), | ||
42 | + binding: LoginBinding(), | ||
43 | + ), | ||
44 | + GetPage( | ||
45 | + participatesInRootNavigator: true, | ||
34 | name: _Paths.HOME, | 46 | name: _Paths.HOME, |
35 | - preventDuplicates: true, | ||
36 | page: () => HomeView(), | 47 | page: () => HomeView(), |
37 | bindings: [ | 48 | bindings: [ |
38 | HomeBinding(), | 49 | HomeBinding(), |
@@ -40,6 +51,10 @@ class AppPages { | @@ -40,6 +51,10 @@ class AppPages { | ||
40 | title: null, | 51 | title: null, |
41 | children: [ | 52 | children: [ |
42 | GetPage( | 53 | GetPage( |
54 | + middlewares: [ | ||
55 | + //only enter this route when authed | ||
56 | + EnsureAuthMiddleware(), | ||
57 | + ], | ||
43 | name: _Paths.PROFILE, | 58 | name: _Paths.PROFILE, |
44 | page: () => ProfileView(), | 59 | page: () => ProfileView(), |
45 | title: 'Profile', | 60 | title: 'Profile', |
@@ -57,12 +72,17 @@ class AppPages { | @@ -57,12 +72,17 @@ class AppPages { | ||
57 | name: _Paths.PRODUCT_DETAILS, | 72 | name: _Paths.PRODUCT_DETAILS, |
58 | page: () => ProductDetailsView(), | 73 | page: () => ProductDetailsView(), |
59 | binding: ProductDetailsBinding(), | 74 | binding: ProductDetailsBinding(), |
75 | + middlewares: [ | ||
76 | + //only enter this route when authed | ||
77 | + EnsureAuthMiddleware(), | ||
78 | + ], | ||
60 | ), | 79 | ), |
61 | ], | 80 | ], |
62 | ), | 81 | ), |
63 | ], | 82 | ], |
64 | ), | 83 | ), |
65 | GetPage( | 84 | GetPage( |
85 | + participatesInRootNavigator: true, | ||
66 | name: _Paths.SETTINGS, | 86 | name: _Paths.SETTINGS, |
67 | page: () => SettingsView(), | 87 | page: () => SettingsView(), |
68 | binding: SettingsBinding(), | 88 | binding: SettingsBinding(), |
@@ -11,6 +11,9 @@ abstract class Routes { | @@ -11,6 +11,9 @@ abstract class Routes { | ||
11 | 11 | ||
12 | static const PRODUCTS = _Paths.HOME + _Paths.PRODUCTS; | 12 | static const PRODUCTS = _Paths.HOME + _Paths.PRODUCTS; |
13 | static String PRODUCT_DETAILS(String productId) => '$PRODUCTS/$productId'; | 13 | static String PRODUCT_DETAILS(String productId) => '$PRODUCTS/$productId'; |
14 | + static const LOGIN = _Paths.LOGIN; | ||
15 | + static String LOGIN_THEN(String afterSuccessfulLogin) => | ||
16 | + '$LOGIN?then=${Uri.encodeQueryComponent(afterSuccessfulLogin)}'; | ||
14 | } | 17 | } |
15 | 18 | ||
16 | abstract class _Paths { | 19 | abstract class _Paths { |
@@ -19,4 +22,5 @@ abstract class _Paths { | @@ -19,4 +22,5 @@ abstract class _Paths { | ||
19 | static const PROFILE = '/profile'; | 22 | static const PROFILE = '/profile'; |
20 | static const SETTINGS = '/settings'; | 23 | static const SETTINGS = '/settings'; |
21 | static const PRODUCT_DETAILS = '/:productId'; | 24 | static const PRODUCT_DETAILS = '/:productId'; |
25 | + static const LOGIN = '/login'; | ||
22 | } | 26 | } |
1 | -import 'package:flutter/material.dart'; | ||
2 | - | ||
3 | -import 'package:get/get.dart'; | ||
4 | -import 'package:get/get_navigation/src/nav2/get_router_delegate.dart'; | ||
5 | - | ||
6 | -import 'app/routes/app_pages.dart'; | ||
7 | - | ||
8 | -void main() { | ||
9 | - runApp( | ||
10 | - GetMaterialApp.router( | ||
11 | - title: "Application", | ||
12 | - getPages: AppPages.routes, | ||
13 | - routeInformationParser: GetInformationParser( | ||
14 | - // initialRoute: Routes.HOME, | ||
15 | - ), | ||
16 | - routerDelegate: GetDelegate( | ||
17 | - backButtonPopMode: PopMode.History, | ||
18 | - preventDuplicateHandlingMode: | ||
19 | - PreventDuplicateHandlingMode.PopUntilOriginalRoute, | ||
20 | - ), | ||
21 | - ), | ||
22 | - ); | ||
23 | -} | 1 | +import 'package:example_nav2/services/auth_service.dart'; |
2 | +import 'package:flutter/material.dart'; | ||
3 | + | ||
4 | +import 'package:get/get.dart'; | ||
5 | +import 'package:get/get_navigation/src/nav2/get_router_delegate.dart'; | ||
6 | + | ||
7 | +import 'app/routes/app_pages.dart'; | ||
8 | + | ||
9 | +void main() { | ||
10 | + runApp( | ||
11 | + GetMaterialApp.router( | ||
12 | + title: "Application", | ||
13 | + initialBinding: BindingsBuilder( | ||
14 | + () { | ||
15 | + Get.put(AuthService()); | ||
16 | + }, | ||
17 | + ), | ||
18 | + getPages: AppPages.routes, | ||
19 | + routeInformationParser: GetInformationParser( | ||
20 | + // initialRoute: Routes.HOME, | ||
21 | + ), | ||
22 | + routerDelegate: GetDelegate( | ||
23 | + backButtonPopMode: PopMode.History, | ||
24 | + preventDuplicateHandlingMode: | ||
25 | + PreventDuplicateHandlingMode.ReorderRoutes, | ||
26 | + ), | ||
27 | + ), | ||
28 | + ); | ||
29 | +} |
example_nav2/lib/services/auth_service.dart
0 → 100644
1 | +import 'package:get/get.dart'; | ||
2 | + | ||
3 | +class AuthService extends GetxService { | ||
4 | + static AuthService get to => Get.find(); | ||
5 | + | ||
6 | + /// Mocks a login process | ||
7 | + final isLoggedIn = false.obs; | ||
8 | + bool get isLoggedInValue => isLoggedIn.value; | ||
9 | + | ||
10 | + void login() { | ||
11 | + isLoggedIn.value = true; | ||
12 | + } | ||
13 | + | ||
14 | + void logout() { | ||
15 | + isLoggedIn.value = false; | ||
16 | + } | ||
17 | +} |
@@ -29,6 +29,16 @@ class GetNavConfig extends RouteInformation { | @@ -29,6 +29,16 @@ class GetNavConfig extends RouteInformation { | ||
29 | ); | 29 | ); |
30 | } | 30 | } |
31 | 31 | ||
32 | + static GetNavConfig? fromRoute(String route) { | ||
33 | + final res = Get.routeTree.matchRoute(route); | ||
34 | + if (res.treeBranch.isEmpty) return null; | ||
35 | + return GetNavConfig( | ||
36 | + currentTreeBranch: res.treeBranch, | ||
37 | + location: route, | ||
38 | + state: null, | ||
39 | + ); | ||
40 | + } | ||
41 | + | ||
32 | @override | 42 | @override |
33 | String toString() => ''' | 43 | String toString() => ''' |
34 | ======GetNavConfig=====\ncurrentTreeBranch: $currentTreeBranch\ncurrentPage: $currentPage\n======GetNavConfig====='''; | 44 | ======GetNavConfig=====\ncurrentTreeBranch: $currentTreeBranch\ncurrentPage: $currentPage\n======GetNavConfig====='''; |
@@ -38,6 +38,12 @@ enum PreventDuplicateHandlingMode { | @@ -38,6 +38,12 @@ enum PreventDuplicateHandlingMode { | ||
38 | 38 | ||
39 | /// Simply don't push the new route | 39 | /// Simply don't push the new route |
40 | DoNothing, | 40 | DoNothing, |
41 | + | ||
42 | + /// Recommended - Moves the old route entry to the front | ||
43 | + /// | ||
44 | + /// With this mode, you guarantee there will be only one | ||
45 | + /// route entry for each location | ||
46 | + ReorderRoutes | ||
41 | } | 47 | } |
42 | 48 | ||
43 | class GetDelegate extends RouterDelegate<GetNavConfig> | 49 | class GetDelegate extends RouterDelegate<GetNavConfig> |
@@ -60,9 +66,47 @@ class GetDelegate extends RouterDelegate<GetNavConfig> | @@ -60,9 +66,47 @@ class GetDelegate extends RouterDelegate<GetNavConfig> | ||
60 | this.navigatorObservers, | 66 | this.navigatorObservers, |
61 | this.transitionDelegate, | 67 | this.transitionDelegate, |
62 | this.backButtonPopMode = PopMode.History, | 68 | this.backButtonPopMode = PopMode.History, |
63 | - this.preventDuplicateHandlingMode = PreventDuplicateHandlingMode.DoNothing, | 69 | + this.preventDuplicateHandlingMode = |
70 | + PreventDuplicateHandlingMode.ReorderRoutes, | ||
64 | }); | 71 | }); |
65 | 72 | ||
73 | + GetNavConfig? runMiddleware(GetNavConfig config) { | ||
74 | + final middlewares = config.currentTreeBranch.last.middlewares ?? []; | ||
75 | + var iterator = config; | ||
76 | + for (var item in middlewares) { | ||
77 | + var redirectRes = item.redirectDelegate(iterator); | ||
78 | + if (redirectRes == null) return null; | ||
79 | + iterator = redirectRes; | ||
80 | + } | ||
81 | + return iterator; | ||
82 | + } | ||
83 | + | ||
84 | + void _unsafeHistoryAdd(GetNavConfig config) { | ||
85 | + final res = runMiddleware(config); | ||
86 | + if (res == null) return; | ||
87 | + history.add(res); | ||
88 | + } | ||
89 | + | ||
90 | + void _unsafeHistoryRemove(GetNavConfig config) { | ||
91 | + var index = history.indexOf(config); | ||
92 | + if (index >= 0) _unsafeHistoryRemoveAt(index); | ||
93 | + } | ||
94 | + | ||
95 | + GetNavConfig? _unsafeHistoryRemoveAt(int index) { | ||
96 | + if (index == history.length - 1) { | ||
97 | + //removing WILL update the current route | ||
98 | + final toCheck = history[history.length - 2]; | ||
99 | + final resMiddleware = runMiddleware(toCheck); | ||
100 | + if (resMiddleware == null) return null; | ||
101 | + history[history.length - 2] = resMiddleware; | ||
102 | + } | ||
103 | + return history.removeAt(index); | ||
104 | + } | ||
105 | + | ||
106 | + void _unsafeHistoryClear() { | ||
107 | + history.clear(); | ||
108 | + } | ||
109 | + | ||
66 | /// Adds a new history entry and waits for the result | 110 | /// Adds a new history entry and waits for the result |
67 | Future<T?> pushHistory<T>( | 111 | Future<T?> pushHistory<T>( |
68 | GetNavConfig config, { | 112 | GetNavConfig config, { |
@@ -79,25 +123,32 @@ class GetDelegate extends RouterDelegate<GetNavConfig> | @@ -79,25 +123,32 @@ class GetDelegate extends RouterDelegate<GetNavConfig> | ||
79 | } | 123 | } |
80 | 124 | ||
81 | void _removeHistoryEntry(GetNavConfig entry) { | 125 | void _removeHistoryEntry(GetNavConfig entry) { |
82 | - history.remove(entry); | 126 | + _unsafeHistoryRemove(entry); |
83 | final lastCompleter = _resultCompleter.remove(entry); | 127 | final lastCompleter = _resultCompleter.remove(entry); |
84 | lastCompleter?.complete(entry); | 128 | lastCompleter?.complete(entry); |
85 | } | 129 | } |
86 | 130 | ||
87 | void _pushHistory(GetNavConfig config) { | 131 | void _pushHistory(GetNavConfig config) { |
88 | if (config.currentPage!.preventDuplicates) { | 132 | if (config.currentPage!.preventDuplicates) { |
89 | - if (history.any((element) => element.location == config.location)) { | 133 | + final originalEntryIndex = |
134 | + history.indexWhere((element) => element.location == config.location); | ||
135 | + if (originalEntryIndex >= 0) { | ||
90 | switch (preventDuplicateHandlingMode) { | 136 | switch (preventDuplicateHandlingMode) { |
91 | case PreventDuplicateHandlingMode.PopUntilOriginalRoute: | 137 | case PreventDuplicateHandlingMode.PopUntilOriginalRoute: |
92 | until(config.location!, popMode: PopMode.Page); | 138 | until(config.location!, popMode: PopMode.Page); |
93 | - return; | 139 | + break; |
140 | + case PreventDuplicateHandlingMode.ReorderRoutes: | ||
141 | + _unsafeHistoryRemoveAt(originalEntryIndex); | ||
142 | + _unsafeHistoryAdd(config); | ||
143 | + break; | ||
94 | case PreventDuplicateHandlingMode.DoNothing: | 144 | case PreventDuplicateHandlingMode.DoNothing: |
95 | default: | 145 | default: |
96 | - return; | 146 | + break; |
97 | } | 147 | } |
148 | + return; | ||
98 | } | 149 | } |
99 | } | 150 | } |
100 | - history.add(config); | 151 | + _unsafeHistoryAdd(config); |
101 | } | 152 | } |
102 | 153 | ||
103 | // GetPageRoute getPageRoute(RouteSettings? settings) { | 154 | // GetPageRoute getPageRoute(RouteSettings? settings) { |
@@ -110,9 +161,8 @@ class GetDelegate extends RouterDelegate<GetNavConfig> | @@ -110,9 +161,8 @@ class GetDelegate extends RouterDelegate<GetNavConfig> | ||
110 | return _doPopHistory(); | 161 | return _doPopHistory(); |
111 | } | 162 | } |
112 | 163 | ||
113 | - GetNavConfig _doPopHistory() { | ||
114 | - final res = history.removeLast(); | ||
115 | - return res; | 164 | + GetNavConfig? _doPopHistory() { |
165 | + return _unsafeHistoryRemoveAt(history.length - 1); | ||
116 | } | 166 | } |
117 | 167 | ||
118 | GetNavConfig? _popPage() { | 168 | GetNavConfig? _popPage() { |
@@ -206,12 +256,18 @@ class GetDelegate extends RouterDelegate<GetNavConfig> | @@ -206,12 +256,18 @@ class GetDelegate extends RouterDelegate<GetNavConfig> | ||
206 | List<GetPage> getVisualPages() { | 256 | List<GetPage> getVisualPages() { |
207 | final currentHistory = currentConfiguration; | 257 | final currentHistory = currentConfiguration; |
208 | if (currentHistory == null) return <GetPage>[]; | 258 | if (currentHistory == null) return <GetPage>[]; |
209 | - return currentHistory.currentTreeBranch.where((r) { | ||
210 | - final mware = | ||
211 | - (r.middlewares ?? []).whereType<RouterOutletContainerMiddleWare>(); | ||
212 | - if (mware.length == 0) return true; | ||
213 | - return r.name == mware.first.stayAt; | ||
214 | - }).toList(); | 259 | + |
260 | + final res = currentHistory.currentTreeBranch | ||
261 | + .where((r) => r.participatesInRootNavigator != null); | ||
262 | + if (res.length == 0) { | ||
263 | + //default behavoir, all routes participate in root navigator | ||
264 | + return currentHistory.currentTreeBranch; | ||
265 | + } else { | ||
266 | + //user specified at least one participatesInRootNavigator | ||
267 | + return res | ||
268 | + .where((element) => element.participatesInRootNavigator == true) | ||
269 | + .toList(); | ||
270 | + } | ||
215 | } | 271 | } |
216 | 272 | ||
217 | @override | 273 | @override |
@@ -234,7 +290,7 @@ class GetDelegate extends RouterDelegate<GetNavConfig> | @@ -234,7 +290,7 @@ class GetDelegate extends RouterDelegate<GetNavConfig> | ||
234 | 290 | ||
235 | @override | 291 | @override |
236 | Future<void> setInitialRoutePath(GetNavConfig configuration) async { | 292 | Future<void> setInitialRoutePath(GetNavConfig configuration) async { |
237 | - history.clear(); | 293 | + _unsafeHistoryClear(); |
238 | _resultCompleter.clear(); | 294 | _resultCompleter.clear(); |
239 | await pushHistory(configuration); | 295 | await pushHistory(configuration); |
240 | } | 296 | } |
@@ -120,15 +120,6 @@ class GetRouterOutlet extends RouterOutlet<GetDelegate, GetNavConfig> { | @@ -120,15 +120,6 @@ class GetRouterOutlet extends RouterOutlet<GetDelegate, GetNavConfig> { | ||
120 | ); | 120 | ); |
121 | } | 121 | } |
122 | 122 | ||
123 | -/// A marker outlet to identify which pages are visual | ||
124 | -/// (handled by the navigator) and which are logical | ||
125 | -/// (handled by the delegate) | ||
126 | -class RouterOutletContainerMiddleWare extends GetMiddleware { | ||
127 | - final String stayAt; | ||
128 | - | ||
129 | - RouterOutletContainerMiddleWare(this.stayAt); | ||
130 | -} | ||
131 | - | ||
132 | extension PagesListExt on List<GetPage> { | 123 | extension PagesListExt on List<GetPage> { |
133 | List<GetPage> pickAtRoute(String route) { | 124 | List<GetPage> pickAtRoute(String route) { |
134 | return skipWhile((value) => value.name != route).toList(); | 125 | return skipWhile((value) => value.name != route).toList(); |
@@ -98,14 +98,28 @@ class ParseRouteTree { | @@ -98,14 +98,28 @@ class ParseRouteTree { | ||
98 | final parentPath = route.name; | 98 | final parentPath = route.name; |
99 | for (var page in route.children!) { | 99 | for (var page in route.children!) { |
100 | // Add Parent middlewares to children | 100 | // Add Parent middlewares to children |
101 | - final pageMiddlewares = page.middlewares ?? <GetMiddleware>[]; | ||
102 | - pageMiddlewares.addAll(route.middlewares ?? <GetMiddleware>[]); | ||
103 | - result.add(_addChild(page, parentPath, pageMiddlewares)); | 101 | + final parentMiddlewares = [ |
102 | + if (page.middlewares != null) ...page.middlewares!, | ||
103 | + if (route.middlewares != null) ...route.middlewares! | ||
104 | + ]; | ||
105 | + result.add( | ||
106 | + _addChild( | ||
107 | + page, | ||
108 | + parentPath, | ||
109 | + parentMiddlewares, | ||
110 | + ), | ||
111 | + ); | ||
104 | 112 | ||
105 | final children = _flattenPage(page); | 113 | final children = _flattenPage(page); |
106 | for (var child in children) { | 114 | for (var child in children) { |
107 | - pageMiddlewares.addAll(child.middlewares ?? <GetMiddleware>[]); | ||
108 | - result.add(_addChild(child, parentPath, pageMiddlewares)); | 115 | + result.add(_addChild( |
116 | + child, | ||
117 | + parentPath, | ||
118 | + [ | ||
119 | + ...parentMiddlewares, | ||
120 | + if (child.middlewares != null) ...child.middlewares!, | ||
121 | + ], | ||
122 | + )); | ||
109 | } | 123 | } |
110 | } | 124 | } |
111 | return result; | 125 | return result; |
@@ -34,6 +34,7 @@ class GetPage<T> extends Page<T> { | @@ -34,6 +34,7 @@ class GetPage<T> extends Page<T> { | ||
34 | final String? title; | 34 | final String? title; |
35 | final Transition? transition; | 35 | final Transition? transition; |
36 | final Curve curve; | 36 | final Curve curve; |
37 | + final bool? participatesInRootNavigator; | ||
37 | final Alignment? alignment; | 38 | final Alignment? alignment; |
38 | final bool maintainState; | 39 | final bool maintainState; |
39 | final bool opaque; | 40 | final bool opaque; |
@@ -65,6 +66,7 @@ class GetPage<T> extends Page<T> { | @@ -65,6 +66,7 @@ class GetPage<T> extends Page<T> { | ||
65 | required this.name, | 66 | required this.name, |
66 | required this.page, | 67 | required this.page, |
67 | this.title, | 68 | this.title, |
69 | + this.participatesInRootNavigator, | ||
68 | this.gestureWidth = 20, | 70 | this.gestureWidth = 20, |
69 | // RouteSettings settings, | 71 | // RouteSettings settings, |
70 | this.maintainState = true, | 72 | this.maintainState = true, |
@@ -82,7 +84,7 @@ class GetPage<T> extends Page<T> { | @@ -82,7 +84,7 @@ class GetPage<T> extends Page<T> { | ||
82 | this.children, | 84 | this.children, |
83 | this.middlewares, | 85 | this.middlewares, |
84 | this.unknownRoute, | 86 | this.unknownRoute, |
85 | - this.preventDuplicates = false, | 87 | + this.preventDuplicates = true, |
86 | }) : path = _nameToRegex(name), | 88 | }) : path = _nameToRegex(name), |
87 | super( | 89 | super( |
88 | key: ValueKey(name), | 90 | key: ValueKey(name), |
@@ -134,8 +136,11 @@ class GetPage<T> extends Page<T> { | @@ -134,8 +136,11 @@ class GetPage<T> extends Page<T> { | ||
134 | List<GetMiddleware>? middlewares, | 136 | List<GetMiddleware>? middlewares, |
135 | bool? preventDuplicates, | 137 | bool? preventDuplicates, |
136 | double? gestureWidth, | 138 | double? gestureWidth, |
139 | + bool? participatesInRootNavigator, | ||
137 | }) { | 140 | }) { |
138 | return GetPage( | 141 | return GetPage( |
142 | + participatesInRootNavigator: | ||
143 | + participatesInRootNavigator ?? this.participatesInRootNavigator, | ||
139 | preventDuplicates: preventDuplicates ?? this.preventDuplicates, | 144 | preventDuplicates: preventDuplicates ?? this.preventDuplicates, |
140 | name: name ?? this.name, | 145 | name: name ?? this.name, |
141 | page: page ?? this.page, | 146 | page: page ?? this.page, |
@@ -32,6 +32,25 @@ abstract class _RouteMiddleware { | @@ -32,6 +32,25 @@ abstract class _RouteMiddleware { | ||
32 | /// {@end-tool} | 32 | /// {@end-tool} |
33 | RouteSettings? redirect(String route); | 33 | RouteSettings? redirect(String route); |
34 | 34 | ||
35 | + /// Similar to [redirect], | ||
36 | + /// This function will be called when the router delegate changes the | ||
37 | + /// current route. | ||
38 | + /// | ||
39 | + /// The default implmentation is to navigate to | ||
40 | + /// the input route, with no redirection. | ||
41 | + /// | ||
42 | + /// if this returns null, the navigation is stopped, | ||
43 | + /// and no new routes are pushed. | ||
44 | + /// {@tool snippet} | ||
45 | + /// ```dart | ||
46 | + /// GetNavConfig? redirect(GetNavConfig route) { | ||
47 | + /// final authService = Get.find<AuthService>(); | ||
48 | + /// return authService.authed.value ? null : RouteSettings(name: '/login'); | ||
49 | + /// } | ||
50 | + /// ``` | ||
51 | + /// {@end-tool} | ||
52 | + GetNavConfig? redirectDelegate(GetNavConfig route); | ||
53 | + | ||
35 | /// This function will be called when this Page is called | 54 | /// This function will be called when this Page is called |
36 | /// you can use it to change something about the page or give it new page | 55 | /// you can use it to change something about the page or give it new page |
37 | /// {@tool snippet} | 56 | /// {@tool snippet} |
@@ -97,6 +116,9 @@ class GetMiddleware implements _RouteMiddleware { | @@ -97,6 +116,9 @@ class GetMiddleware implements _RouteMiddleware { | ||
97 | 116 | ||
98 | @override | 117 | @override |
99 | void onPageDispose() {} | 118 | void onPageDispose() {} |
119 | + | ||
120 | + @override | ||
121 | + GetNavConfig? redirectDelegate(GetNavConfig route) => route; | ||
100 | } | 122 | } |
101 | 123 | ||
102 | class MiddlewareRunner { | 124 | class MiddlewareRunner { |
@@ -204,6 +226,10 @@ class PageRedirect { | @@ -204,6 +226,10 @@ class PageRedirect { | ||
204 | return GetPageRoute<T>( | 226 | return GetPageRoute<T>( |
205 | page: _r.page, | 227 | page: _r.page, |
206 | parameter: _r.parameter, | 228 | parameter: _r.parameter, |
229 | + alignment: _r.alignment, | ||
230 | + title: _r.title, | ||
231 | + maintainState: _r.maintainState, | ||
232 | + routeName: _r.name, | ||
207 | settings: _r, | 233 | settings: _r, |
208 | curve: _r.curve, | 234 | curve: _r.curve, |
209 | gestureWidth: _r.gestureWidth, | 235 | gestureWidth: _r.gestureWidth, |
1 | -export 'context_extensions.dart'; | ||
2 | -export 'double_extensions.dart'; | ||
3 | -export 'duration_extensions.dart'; | ||
4 | -export 'dynamic_extensions.dart'; | ||
5 | -export 'event_loop_extensions.dart'; | ||
6 | -export 'internacionalization.dart'; | ||
7 | -export 'num_extensions.dart'; | ||
8 | -export 'string_extensions.dart'; | ||
9 | -export 'widget_extensions.dart'; | 1 | +export 'context_extensions.dart'; |
2 | +export 'double_extensions.dart'; | ||
3 | +export 'duration_extensions.dart'; | ||
4 | +export 'dynamic_extensions.dart'; | ||
5 | +export 'event_loop_extensions.dart'; | ||
6 | +export 'internacionalization.dart'; | ||
7 | +export 'num_extensions.dart'; | ||
8 | +export 'string_extensions.dart'; | ||
9 | +export 'widget_extensions.dart'; | ||
10 | +export 'iterable_extensions.dart'; |
-
Please register or login to post a comment