Roi Peker

Adjustments in context Navigations methods.

- Add  `String? routeName` to the following context Navigation methods:
`to`, `off`, `offAll`. To enforce the usage of `RouteSettings` and avoid issues with GetxController not being disposed properly, and also to keep control and consistency in the methods.
- modified `RouterReportManager.reportRouteDispose` to make it work with context Routes.
- added extension_navigator.dart `_cleanRouteName` to remove unwanted format where the Route name is taking from the page Type in context Routes.
- added `GetStringUtils.paramCase` to support potentially a clean url name for the context Routes. So `/()=> MyView` can become `/my-view'
- added `GetUtils.snakeCase` and `GetUtils.paramCase` to support `_cleanRouteName` url Strings (currently deactivated.) Base code taken from the ReCase package and proper credit attributed in comments.
@@ -539,6 +539,7 @@ extension GetNavigation on GetInterface { @@ -539,6 +539,7 @@ extension GetNavigation on GetInterface {
539 Curve? curve, 539 Curve? curve,
540 Duration? duration, 540 Duration? duration,
541 int? id, 541 int? id,
  542 + String? routeName,
542 bool fullscreenDialog = false, 543 bool fullscreenDialog = false,
543 dynamic arguments, 544 dynamic arguments,
544 Bindings? binding, 545 Bindings? binding,
@@ -546,18 +547,20 @@ extension GetNavigation on GetInterface { @@ -546,18 +547,20 @@ extension GetNavigation on GetInterface {
546 bool? popGesture, 547 bool? popGesture,
547 double Function(BuildContext context)? gestureWidth, 548 double Function(BuildContext context)? gestureWidth,
548 }) { 549 }) {
549 - var routeName = "/${page.runtimeType.toString()}"; 550 + // var routeName = "/${page.runtimeType}";
  551 + routeName ??= "/${page.runtimeType}";
  552 + routeName = _cleanRouteName(routeName);
550 if (preventDuplicates && routeName == currentRoute) { 553 if (preventDuplicates && routeName == currentRoute) {
551 return null; 554 return null;
552 } 555 }
553 return global(id).currentState?.push<T>( 556 return global(id).currentState?.push<T>(
554 GetPageRoute<T>( 557 GetPageRoute<T>(
555 opaque: opaque ?? true, 558 opaque: opaque ?? true,
556 - page: _resolve(page, 'to'), 559 + page: _resolvePage(page, 'to'),
557 routeName: routeName, 560 routeName: routeName,
558 gestureWidth: gestureWidth, 561 gestureWidth: gestureWidth,
559 settings: RouteSettings( 562 settings: RouteSettings(
560 - // name: forceRouteName ? '${a.runtimeType}' : '', 563 + name: routeName,
561 arguments: arguments, 564 arguments: arguments,
562 ), 565 ),
563 popGesture: popGesture ?? defaultPopGesture, 566 popGesture: popGesture ?? defaultPopGesture,
@@ -570,7 +573,7 @@ extension GetNavigation on GetInterface { @@ -570,7 +573,7 @@ extension GetNavigation on GetInterface {
570 ); 573 );
571 } 574 }
572 575
573 - GetPageBuilder _resolve(dynamic page, String method) { 576 + GetPageBuilder _resolvePage(dynamic page, String method) {
574 if (page is GetPageBuilder) { 577 if (page is GetPageBuilder) {
575 return page; 578 return page;
576 } else if (page is Widget) { 579 } else if (page is Widget) {
@@ -909,6 +912,7 @@ you can only use widgets and widget functions here'''; @@ -909,6 +912,7 @@ you can only use widgets and widget functions here''';
909 Curve? curve, 912 Curve? curve,
910 bool? popGesture, 913 bool? popGesture,
911 int? id, 914 int? id,
  915 + String? routeName,
912 dynamic arguments, 916 dynamic arguments,
913 Bindings? binding, 917 Bindings? binding,
914 bool fullscreenDialog = false, 918 bool fullscreenDialog = false,
@@ -916,16 +920,20 @@ you can only use widgets and widget functions here'''; @@ -916,16 +920,20 @@ you can only use widgets and widget functions here''';
916 Duration? duration, 920 Duration? duration,
917 double Function(BuildContext context)? gestureWidth, 921 double Function(BuildContext context)? gestureWidth,
918 }) { 922 }) {
919 - var routeName = "/${page.runtimeType.toString()}"; 923 + routeName ??= "/${page.runtimeType.toString()}";
  924 + routeName = _cleanRouteName(routeName);
920 if (preventDuplicates && routeName == currentRoute) { 925 if (preventDuplicates && routeName == currentRoute) {
921 return null; 926 return null;
922 } 927 }
923 return global(id).currentState?.pushReplacement(GetPageRoute( 928 return global(id).currentState?.pushReplacement(GetPageRoute(
924 opaque: opaque, 929 opaque: opaque,
925 gestureWidth: gestureWidth, 930 gestureWidth: gestureWidth,
926 - page: _resolve(page, 'off'), 931 + page: _resolvePage(page, 'off'),
927 binding: binding, 932 binding: binding,
928 - settings: RouteSettings(arguments: arguments), 933 + settings: RouteSettings(
  934 + arguments: arguments,
  935 + name: routeName,
  936 + ),
929 routeName: routeName, 937 routeName: routeName,
930 fullscreenDialog: fullscreenDialog, 938 fullscreenDialog: fullscreenDialog,
931 popGesture: popGesture ?? defaultPopGesture, 939 popGesture: popGesture ?? defaultPopGesture,
@@ -934,7 +942,6 @@ you can only use widgets and widget functions here'''; @@ -934,7 +942,6 @@ you can only use widgets and widget functions here''';
934 transitionDuration: duration ?? defaultTransitionDuration)); 942 transitionDuration: duration ?? defaultTransitionDuration));
935 } 943 }
936 944
937 - /// **Navigation.pushAndRemoveUntil()** shortcut .<br><br>  
938 /// 945 ///
939 /// Push a `page` and pop several pages in the stack 946 /// Push a `page` and pop several pages in the stack
940 /// until [predicate] returns true. [predicate] is optional 947 /// until [predicate] returns true. [predicate] is optional
@@ -971,6 +978,7 @@ you can only use widgets and widget functions here'''; @@ -971,6 +978,7 @@ you can only use widgets and widget functions here''';
971 bool opaque = false, 978 bool opaque = false,
972 bool? popGesture, 979 bool? popGesture,
973 int? id, 980 int? id,
  981 + String? routeName,
974 dynamic arguments, 982 dynamic arguments,
975 Bindings? binding, 983 Bindings? binding,
976 bool fullscreenDialog = false, 984 bool fullscreenDialog = false,
@@ -979,16 +987,19 @@ you can only use widgets and widget functions here'''; @@ -979,16 +987,19 @@ you can only use widgets and widget functions here''';
979 Duration? duration, 987 Duration? duration,
980 double Function(BuildContext context)? gestureWidth, 988 double Function(BuildContext context)? gestureWidth,
981 }) { 989 }) {
982 - var routeName = "/${page.runtimeType.toString()}";  
983 - 990 + routeName ??= "/${page.runtimeType.toString()}";
  991 + routeName = _cleanRouteName(routeName);
984 return global(id).currentState?.pushAndRemoveUntil<T>( 992 return global(id).currentState?.pushAndRemoveUntil<T>(
985 GetPageRoute<T>( 993 GetPageRoute<T>(
986 opaque: opaque, 994 opaque: opaque,
987 popGesture: popGesture ?? defaultPopGesture, 995 popGesture: popGesture ?? defaultPopGesture,
988 - page: _resolve(page, 'offAll'), 996 + page: _resolvePage(page, 'offAll'),
989 binding: binding, 997 binding: binding,
990 gestureWidth: gestureWidth, 998 gestureWidth: gestureWidth,
991 - settings: RouteSettings(arguments: arguments), 999 + settings: RouteSettings(
  1000 + name: routeName,
  1001 + arguments: arguments,
  1002 + ),
992 fullscreenDialog: fullscreenDialog, 1003 fullscreenDialog: fullscreenDialog,
993 routeName: routeName, 1004 routeName: routeName,
994 transition: transition ?? defaultTransition, 1005 transition: transition ?? defaultTransition,
@@ -998,6 +1009,21 @@ you can only use widgets and widget functions here'''; @@ -998,6 +1009,21 @@ you can only use widgets and widget functions here''';
998 predicate ?? (route) => false); 1009 predicate ?? (route) => false);
999 } 1010 }
1000 1011
  1012 + /// Takes a route [name] String generated by [to], [off], [offAll]
  1013 + /// (and similar context navigation methods), cleans the extra chars and
  1014 + /// accommodates the format.
  1015 + /// TODO: check for a more "appealing" URL naming convention.
  1016 + /// `() => MyHomeScreenView` becomes `/my-home-screen-view`.
  1017 + String _cleanRouteName(String name) {
  1018 + name = name.replaceAll('() => ', '');
  1019 + /// uncommonent for URL styling.
  1020 + // name = name.paramCase!;
  1021 + if (!name.startsWith('/')) {
  1022 + name = '/$name';
  1023 + }
  1024 + return Uri.tryParse(name)?.toString() ?? name;
  1025 + }
  1026 +
1001 /// change default config of Get 1027 /// change default config of Get
1002 void config( 1028 void config(
1003 {bool? enableLog, 1029 {bool? enableLog,
@@ -46,10 +46,10 @@ class RouterReportManager<T> { @@ -46,10 +46,10 @@ class RouterReportManager<T> {
46 static void reportRouteDispose(Route disposed) { 46 static void reportRouteDispose(Route disposed) {
47 if (Get.smartManagement != SmartManagement.onlyBuilder) { 47 if (Get.smartManagement != SmartManagement.onlyBuilder) {
48 WidgetsBinding.instance!.addPostFrameCallback((_) { 48 WidgetsBinding.instance!.addPostFrameCallback((_) {
49 - ///TODO: Is necessary this comparator?  
50 - if (_current != disposed) {  
51 - _removeDependencyByRoute(disposed);  
52 - } 49 + ///TODO: Check if it's necessary to compare _current != disposed
  50 + ///Adding it breaks the context Navigation logic,
  51 + ///as it resolves by Route name.
  52 + _removeDependencyByRoute(disposed);
53 }); 53 });
54 } 54 }
55 } 55 }
@@ -77,6 +77,8 @@ extension GetStringUtils on String { @@ -77,6 +77,8 @@ extension GetStringUtils on String {
77 77
78 String? get camelCase => GetUtils.camelCase(this); 78 String? get camelCase => GetUtils.camelCase(this);
79 79
  80 + String? get paramCase => GetUtils.paramCase(this);
  81 +
80 String numericOnly({bool firstWordOnly = false}) => 82 String numericOnly({bool firstWordOnly = false}) =>
81 GetUtils.numericOnly(this, firstWordOnly: firstWordOnly); 83 GetUtils.numericOnly(this, firstWordOnly: firstWordOnly);
82 84
@@ -551,6 +551,44 @@ class GetUtils { @@ -551,6 +551,44 @@ class GetUtils {
551 return newString[0].toLowerCase() + newString.substring(1); 551 return newString[0].toLowerCase() + newString.substring(1);
552 } 552 }
553 553
  554 + /// credits to "ReCase" package.
  555 + static final RegExp _upperAlphaRegex = RegExp(r'[A-Z]');
  556 + static final _symbolSet = {' ', '.', '/', '_', '\\', '-'};
  557 + static List<String> _groupIntoWords(String text) {
  558 + var sb = StringBuffer();
  559 + var words = <String>[];
  560 + var isAllCaps = text.toUpperCase() == text;
  561 +
  562 + for (var i = 0; i < text.length; i++) {
  563 + var char = text[i];
  564 + var nextChar = i + 1 == text.length ? null : text[i + 1];
  565 + if (_symbolSet.contains(char)) {
  566 + continue;
  567 + }
  568 + sb.write(char);
  569 + var isEndOfWord = nextChar == null ||
  570 + (_upperAlphaRegex.hasMatch(nextChar) && !isAllCaps) ||
  571 + _symbolSet.contains(nextChar);
  572 + if (isEndOfWord) {
  573 + words.add('$sb');
  574 + sb.clear();
  575 + }
  576 + }
  577 + return words;
  578 + }
  579 +
  580 + /// snake_case
  581 + static String? snakeCase(String? text, {String separator = '_'}) {
  582 + if (isNullOrBlank(text)!) {
  583 + return null;
  584 + }
  585 + return _groupIntoWords(text!)
  586 + .map((word) => word.toLowerCase()).join(separator);
  587 + }
  588 +
  589 + /// param-case
  590 + static String? paramCase(String? text) => snakeCase(text, separator: '-');
  591 +
554 /// Extract numeric value of string 592 /// Extract numeric value of string
555 /// Example: OTP 12312 27/04/2020 => 1231227042020ß 593 /// Example: OTP 12312 27/04/2020 => 1231227042020ß
556 /// If firstword only is true, then the example return is "12312" 594 /// If firstword only is true, then the example return is "12312"