Ahmed Fwela

- Added Navigator key to dialogs as an optional parameter

- Split dashboard and home routes (in example_nav2)
- Removed 'Pages in a router outlet shouldn't participate in the root navigator' restriction
  1 +import 'package:get/get.dart';
  2 +
  3 +import '../controllers/dashboard_controller.dart';
  4 +
  5 +class DashboardBinding extends Bindings {
  6 + @override
  7 + void dependencies() {
  8 + Get.lazyPut<DashboardController>(
  9 + () => DashboardController(),
  10 + );
  11 + }
  12 +}
  1 +import 'dart:async';
  2 +
  3 +import 'package:get/get.dart';
  4 +
  5 +class DashboardController extends GetxController {
  6 + final now = DateTime.now().obs;
  7 + @override
  8 + void onReady() {
  9 + super.onReady();
  10 + Timer.periodic(
  11 + Duration(seconds: 1),
  12 + (timer) {
  13 + now.value = DateTime.now();
  14 + },
  15 + );
  16 + }
  17 +}
  1 +import 'package:example_nav2/app/modules/dashboard/controllers/dashboard_controller.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
4 -import '../controllers/home_controller.dart'; 5 +import '../../home/controllers/home_controller.dart';
5 6
6 -class DashboardView extends GetView<HomeController> { 7 +class DashboardView extends GetView<DashboardController> {
7 @override 8 @override
8 Widget build(BuildContext context) { 9 Widget build(BuildContext context) {
9 return Scaffold( 10 return Scaffold(
@@ -16,7 +17,7 @@ class DashboardView extends GetView<HomeController> { @@ -16,7 +17,7 @@ class DashboardView extends GetView<HomeController> {
16 'DashboardView is working', 17 'DashboardView is working',
17 style: TextStyle(fontSize: 20), 18 style: TextStyle(fontSize: 20),
18 ), 19 ),
19 - Text('Time: ${controller.now.value.toString()}') 20 + Text('Time: ${controller.now.value.toString()}'),
20 ], 21 ],
21 ), 22 ),
22 ), 23 ),
@@ -2,16 +2,4 @@ import 'dart:async'; @@ -2,16 +2,4 @@ import 'dart:async';
2 2
3 import 'package:get/get.dart'; 3 import 'package:get/get.dart';
4 4
5 -class HomeController extends GetxController {  
6 - final now = DateTime.now().obs;  
7 - @override  
8 - void onReady() {  
9 - super.onReady();  
10 - Timer.periodic(  
11 - Duration(seconds: 1),  
12 - (timer) {  
13 - now.value = DateTime.now();  
14 - },  
15 - );  
16 - }  
17 -} 5 +class HomeController extends GetxController {}
@@ -3,7 +3,7 @@ import 'package:get/get.dart'; @@ -3,7 +3,7 @@ import 'package:get/get.dart';
3 3
4 import '../../../routes/app_pages.dart'; 4 import '../../../routes/app_pages.dart';
5 import '../controllers/home_controller.dart'; 5 import '../controllers/home_controller.dart';
6 -import 'dashboard_view.dart'; 6 +import '../../dashboard/views/dashboard_view.dart';
7 7
8 class HomeView extends GetView<HomeController> { 8 class HomeView extends GetView<HomeController> {
9 @override 9 @override
@@ -22,7 +22,9 @@ class HomeView extends GetView<HomeController> { @@ -22,7 +22,9 @@ class HomeView extends GetView<HomeController> {
22 return Scaffold( 22 return Scaffold(
23 body: GetRouterOutlet( 23 body: GetRouterOutlet(
24 name: Routes.HOME, 24 name: Routes.HOME,
25 - emptyWidget: (delegate) => DashboardView(), 25 + //It's preferable to use emptyPage instead of emptyWidget
  26 + emptyPage: (delegate) =>
  27 + Get.routeTree.matchRoute(Routes.DASHBOARD).route!,
26 pickPages: (currentNavStack) { 28 pickPages: (currentNavStack) {
27 print('Home RouterOutlet: $currentNavStack'); 29 print('Home RouterOutlet: $currentNavStack');
28 30
@@ -37,7 +39,7 @@ class HomeView extends GetView<HomeController> { @@ -37,7 +39,7 @@ class HomeView extends GetView<HomeController> {
37 onTap: (value) { 39 onTap: (value) {
38 switch (value) { 40 switch (value) {
39 case 0: 41 case 0:
40 - delegate.until(Routes.HOME); 42 + delegate.toNamed(Routes.HOME);
41 break; 43 break;
42 case 1: 44 case 1:
43 delegate.toNamed(Routes.PROFILE); 45 delegate.toNamed(Routes.PROFILE);
@@ -12,7 +12,14 @@ class ProductsView extends GetView<ProductsController> { @@ -12,7 +12,14 @@ class ProductsView extends GetView<ProductsController> {
12 onPressed: controller.loadDemoProductsFromSomeWhere, 12 onPressed: controller.loadDemoProductsFromSomeWhere,
13 label: Text('Add'), 13 label: Text('Add'),
14 ), 14 ),
15 - body: Obx( 15 + body: Column(
  16 + children: [
  17 + Hero(
  18 + tag: 'heroLogo',
  19 + child: const FlutterLogo(),
  20 + ),
  21 + Expanded(
  22 + child: Obx(
16 () => RefreshIndicator( 23 () => RefreshIndicator(
17 onRefresh: () async { 24 onRefresh: () async {
18 controller.products.clear(); 25 controller.products.clear();
@@ -24,7 +31,8 @@ class ProductsView extends GetView<ProductsController> { @@ -24,7 +31,8 @@ class ProductsView extends GetView<ProductsController> {
24 final item = controller.products[index]; 31 final item = controller.products[index];
25 return ListTile( 32 return ListTile(
26 onTap: () { 33 onTap: () {
27 - Get.rootDelegate.toNamed(Routes.PRODUCT_DETAILS(item.id)); 34 + Get.rootDelegate
  35 + .toNamed(Routes.PRODUCT_DETAILS(item.id));
28 }, 36 },
29 title: Text(item.name), 37 title: Text(item.name),
30 subtitle: Text(item.id), 38 subtitle: Text(item.id),
@@ -33,6 +41,9 @@ class ProductsView extends GetView<ProductsController> { @@ -33,6 +41,9 @@ class ProductsView extends GetView<ProductsController> {
33 ), 41 ),
34 ), 42 ),
35 ), 43 ),
  44 + ),
  45 + ],
  46 + ),
36 ); 47 );
37 } 48 }
38 } 49 }
  1 +import 'package:example_nav2/app/routes/app_pages.dart';
1 import 'package:flutter/material.dart'; 2 import 'package:flutter/material.dart';
2 3
3 import 'package:get/get.dart'; 4 import 'package:get/get.dart';
@@ -11,11 +12,16 @@ class ProfileView extends GetView<ProfileController> { @@ -11,11 +12,16 @@ class ProfileView extends GetView<ProfileController> {
11 backgroundColor: Colors.amber, 12 backgroundColor: Colors.amber,
12 body: Center( 13 body: Center(
13 child: Column( 14 child: Column(
  15 + mainAxisSize: MainAxisSize.min,
14 children: [ 16 children: [
15 Text( 17 Text(
16 'ProfileView is working', 18 'ProfileView is working',
17 style: TextStyle(fontSize: 20), 19 style: TextStyle(fontSize: 20),
18 ), 20 ),
  21 + Hero(
  22 + tag: 'heroLogo',
  23 + child: const FlutterLogo(),
  24 + ),
19 MaterialButton( 25 MaterialButton(
20 child: Text('Show a test dialog'), 26 child: Text('Show a test dialog'),
21 onPressed: () { 27 onPressed: () {
@@ -25,6 +31,18 @@ class ProfileView extends GetView<ProfileController> { @@ -25,6 +31,18 @@ class ProfileView extends GetView<ProfileController> {
25 barrierDismissible: true, 31 barrierDismissible: true,
26 ); 32 );
27 }, 33 },
  34 + ),
  35 + MaterialButton(
  36 + child: Text('Show a test dialog in Home router outlet'),
  37 + onPressed: () {
  38 + //shows a dialog
  39 +
  40 + Get.defaultDialog(
  41 + title: 'Test Dialog In Home Outlet !!',
  42 + barrierDismissible: true,
  43 + navigatorKey: Get.nestedKey(Routes.HOME),
  44 + );
  45 + },
28 ) 46 )
29 ], 47 ],
30 ), 48 ),
1 import 'package:get/get.dart'; 1 import 'package:get/get.dart';
2 2
  3 +import 'package:example_nav2/app/modules/dashboard/bindings/dashboard_binding.dart';
  4 +import 'package:example_nav2/app/modules/dashboard/views/dashboard_view.dart';
  5 +
3 import '../middleware/auth_middleware.dart'; 6 import '../middleware/auth_middleware.dart';
4 import '../modules/home/bindings/home_binding.dart'; 7 import '../modules/home/bindings/home_binding.dart';
5 import '../modules/home/views/home_view.dart'; 8 import '../modules/home/views/home_view.dart';
@@ -50,6 +53,11 @@ class AppPages { @@ -50,6 +53,11 @@ class AppPages {
50 title: null, 53 title: null,
51 children: [ 54 children: [
52 GetPage( 55 GetPage(
  56 + name: _Paths.DASHBOARD,
  57 + page: () => DashboardView(),
  58 + binding: DashboardBinding(),
  59 + ),
  60 + GetPage(
53 middlewares: [ 61 middlewares: [
54 //only enter this route when authed 62 //only enter this route when authed
55 EnsureAuthMiddleware(), 63 EnsureAuthMiddleware(),
@@ -14,6 +14,7 @@ abstract class Routes { @@ -14,6 +14,7 @@ abstract class Routes {
14 static const LOGIN = _Paths.LOGIN; 14 static const LOGIN = _Paths.LOGIN;
15 static String LOGIN_THEN(String afterSuccessfulLogin) => 15 static String LOGIN_THEN(String afterSuccessfulLogin) =>
16 '$LOGIN?then=${Uri.encodeQueryComponent(afterSuccessfulLogin)}'; 16 '$LOGIN?then=${Uri.encodeQueryComponent(afterSuccessfulLogin)}';
  17 + static const DASHBOARD = _Paths.HOME + _Paths.DASHBOARD;
17 } 18 }
18 19
19 abstract class _Paths { 20 abstract class _Paths {
@@ -23,4 +24,5 @@ abstract class _Paths { @@ -23,4 +24,5 @@ abstract class _Paths {
23 static const SETTINGS = '/settings'; 24 static const SETTINGS = '/settings';
24 static const PRODUCT_DETAILS = '/:productId'; 25 static const PRODUCT_DETAILS = '/:productId';
25 static const LOGIN = '/login'; 26 static const LOGIN = '/login';
  27 + static const DASHBOARD = '/dashboard';
26 } 28 }
@@ -215,7 +215,7 @@ extension ExtensionDialog on GetInterface { @@ -215,7 +215,7 @@ extension ExtensionDialog on GetInterface {
215 bool barrierDismissible = true, 215 bool barrierDismissible = true,
216 Color? barrierColor, 216 Color? barrierColor,
217 bool useSafeArea = true, 217 bool useSafeArea = true,
218 - bool useRootNavigator = true, 218 + GlobalKey<NavigatorState>? navigatorKey,
219 Object? arguments, 219 Object? arguments,
220 Duration? transitionDuration, 220 Duration? transitionDuration,
221 Curve? transitionCurve, 221 Curve? transitionCurve,
@@ -250,7 +250,7 @@ extension ExtensionDialog on GetInterface { @@ -250,7 +250,7 @@ extension ExtensionDialog on GetInterface {
250 child: child, 250 child: child,
251 ); 251 );
252 }, 252 },
253 - useRootNavigator: useRootNavigator, 253 + navigatorKey: navigatorKey,
254 routeSettings: 254 routeSettings:
255 routeSettings ?? RouteSettings(arguments: arguments, name: name), 255 routeSettings ?? RouteSettings(arguments: arguments, name: name),
256 ); 256 );
@@ -264,12 +264,16 @@ extension ExtensionDialog on GetInterface { @@ -264,12 +264,16 @@ extension ExtensionDialog on GetInterface {
264 Color barrierColor = const Color(0x80000000), 264 Color barrierColor = const Color(0x80000000),
265 Duration transitionDuration = const Duration(milliseconds: 200), 265 Duration transitionDuration = const Duration(milliseconds: 200),
266 RouteTransitionsBuilder? transitionBuilder, 266 RouteTransitionsBuilder? transitionBuilder,
267 - bool useRootNavigator = true, 267 + GlobalKey<NavigatorState>? navigatorKey,
268 RouteSettings? routeSettings, 268 RouteSettings? routeSettings,
269 }) { 269 }) {
270 assert(!barrierDismissible || barrierLabel != null); 270 assert(!barrierDismissible || barrierLabel != null);
271 - return Navigator.of(overlayContext!, rootNavigator: useRootNavigator)  
272 - .push<T>(GetDialogRoute<T>( 271 + final nav = navigatorKey?.currentState ??
  272 + Navigator.of(overlayContext!,
  273 + rootNavigator:
  274 + true); //overlay context will always return the root navigator
  275 + return nav.push<T>(
  276 + GetDialogRoute<T>(
273 pageBuilder: pageBuilder, 277 pageBuilder: pageBuilder,
274 barrierDismissible: barrierDismissible, 278 barrierDismissible: barrierDismissible,
275 barrierLabel: barrierLabel, 279 barrierLabel: barrierLabel,
@@ -277,7 +281,8 @@ extension ExtensionDialog on GetInterface { @@ -277,7 +281,8 @@ extension ExtensionDialog on GetInterface {
277 transitionDuration: transitionDuration, 281 transitionDuration: transitionDuration,
278 transitionBuilder: transitionBuilder, 282 transitionBuilder: transitionBuilder,
279 settings: routeSettings, 283 settings: routeSettings,
280 - )); 284 + ),
  285 + );
281 } 286 }
282 287
283 /// Custom UI Dialog. 288 /// Custom UI Dialog.
@@ -309,6 +314,9 @@ extension ExtensionDialog on GetInterface { @@ -309,6 +314,9 @@ extension ExtensionDialog on GetInterface {
309 314
310 // onWillPop Scope 315 // onWillPop Scope
311 WillPopCallback? onWillPop, 316 WillPopCallback? onWillPop,
  317 +
  318 + // the navigator used to push the dialog
  319 + GlobalKey<NavigatorState>? navigatorKey,
312 }) { 320 }) {
313 var leanCancel = onCancel != null || textCancel != null; 321 var leanCancel = onCancel != null || textCancel != null;
314 var leanConfirm = onConfirm != null || textConfirm != null; 322 var leanConfirm = onConfirm != null || textConfirm != null;
@@ -394,19 +402,15 @@ extension ExtensionDialog on GetInterface { @@ -394,19 +402,15 @@ extension ExtensionDialog on GetInterface {
394 buttonPadding: EdgeInsets.zero, 402 buttonPadding: EdgeInsets.zero,
395 ); 403 );
396 404
397 - if (onWillPop != null) {  
398 return dialog<T>( 405 return dialog<T>(
399 - WillPopScope( 406 + onWillPop != null
  407 + ? WillPopScope(
400 onWillPop: onWillPop, 408 onWillPop: onWillPop,
401 child: baseAlertDialog, 409 child: baseAlertDialog,
402 - ),  
403 - barrierDismissible: barrierDismissible,  
404 - );  
405 - }  
406 -  
407 - return dialog<T>(  
408 - baseAlertDialog, 410 + )
  411 + : baseAlertDialog,
409 barrierDismissible: barrierDismissible, 412 barrierDismissible: barrierDismissible,
  413 + navigatorKey: navigatorKey,
410 ); 414 );
411 } 415 }
412 } 416 }
@@ -106,13 +106,6 @@ class GetRouterOutlet extends RouterOutlet<GetDelegate, GetNavConfig> { @@ -106,13 +106,6 @@ class GetRouterOutlet extends RouterOutlet<GetDelegate, GetNavConfig> {
106 (pages ?? <GetPage<dynamic>?>[emptyPage?.call(rDelegate)]) 106 (pages ?? <GetPage<dynamic>?>[emptyPage?.call(rDelegate)])
107 .whereType<GetPage<dynamic>>() 107 .whereType<GetPage<dynamic>>()
108 .toList(); 108 .toList();
109 -  
110 - final badPages = pageRes.where(  
111 - (element) => element.participatesInRootNavigator == true);  
112 - if (badPages.length > 0) {  
113 - throw """Pages in a router outlet shouldn't participate in the root navigator  
114 - $badPages""";  
115 - }  
116 if (pageRes.length > 0) { 109 if (pageRes.length > 0) {
117 return GetNavigator( 110 return GetNavigator(
118 onPopPage: onPopPage ?? 111 onPopPage: onPopPage ??