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 {
Curve? curve,
Duration? duration,
int? id,
String? routeName,
bool fullscreenDialog = false,
dynamic arguments,
Bindings? binding,
... ... @@ -546,18 +547,20 @@ extension GetNavigation on GetInterface {
bool? popGesture,
double Function(BuildContext context)? gestureWidth,
}) {
var routeName = "/${page.runtimeType.toString()}";
// var routeName = "/${page.runtimeType}";
routeName ??= "/${page.runtimeType}";
routeName = _cleanRouteName(routeName);
if (preventDuplicates && routeName == currentRoute) {
return null;
}
return global(id).currentState?.push<T>(
GetPageRoute<T>(
opaque: opaque ?? true,
page: _resolve(page, 'to'),
page: _resolvePage(page, 'to'),
routeName: routeName,
gestureWidth: gestureWidth,
settings: RouteSettings(
// name: forceRouteName ? '${a.runtimeType}' : '',
name: routeName,
arguments: arguments,
),
popGesture: popGesture ?? defaultPopGesture,
... ... @@ -570,7 +573,7 @@ extension GetNavigation on GetInterface {
);
}
GetPageBuilder _resolve(dynamic page, String method) {
GetPageBuilder _resolvePage(dynamic page, String method) {
if (page is GetPageBuilder) {
return page;
} else if (page is Widget) {
... ... @@ -909,6 +912,7 @@ you can only use widgets and widget functions here''';
Curve? curve,
bool? popGesture,
int? id,
String? routeName,
dynamic arguments,
Bindings? binding,
bool fullscreenDialog = false,
... ... @@ -916,16 +920,20 @@ you can only use widgets and widget functions here''';
Duration? duration,
double Function(BuildContext context)? gestureWidth,
}) {
var routeName = "/${page.runtimeType.toString()}";
routeName ??= "/${page.runtimeType.toString()}";
routeName = _cleanRouteName(routeName);
if (preventDuplicates && routeName == currentRoute) {
return null;
}
return global(id).currentState?.pushReplacement(GetPageRoute(
opaque: opaque,
gestureWidth: gestureWidth,
page: _resolve(page, 'off'),
page: _resolvePage(page, 'off'),
binding: binding,
settings: RouteSettings(arguments: arguments),
settings: RouteSettings(
arguments: arguments,
name: routeName,
),
routeName: routeName,
fullscreenDialog: fullscreenDialog,
popGesture: popGesture ?? defaultPopGesture,
... ... @@ -934,7 +942,6 @@ you can only use widgets and widget functions here''';
transitionDuration: duration ?? defaultTransitionDuration));
}
/// **Navigation.pushAndRemoveUntil()** shortcut .<br><br>
///
/// Push a `page` and pop several pages in the stack
/// until [predicate] returns true. [predicate] is optional
... ... @@ -971,6 +978,7 @@ you can only use widgets and widget functions here''';
bool opaque = false,
bool? popGesture,
int? id,
String? routeName,
dynamic arguments,
Bindings? binding,
bool fullscreenDialog = false,
... ... @@ -979,16 +987,19 @@ you can only use widgets and widget functions here''';
Duration? duration,
double Function(BuildContext context)? gestureWidth,
}) {
var routeName = "/${page.runtimeType.toString()}";
routeName ??= "/${page.runtimeType.toString()}";
routeName = _cleanRouteName(routeName);
return global(id).currentState?.pushAndRemoveUntil<T>(
GetPageRoute<T>(
opaque: opaque,
popGesture: popGesture ?? defaultPopGesture,
page: _resolve(page, 'offAll'),
page: _resolvePage(page, 'offAll'),
binding: binding,
gestureWidth: gestureWidth,
settings: RouteSettings(arguments: arguments),
settings: RouteSettings(
name: routeName,
arguments: arguments,
),
fullscreenDialog: fullscreenDialog,
routeName: routeName,
transition: transition ?? defaultTransition,
... ... @@ -998,6 +1009,21 @@ you can only use widgets and widget functions here''';
predicate ?? (route) => false);
}
/// Takes a route [name] String generated by [to], [off], [offAll]
/// (and similar context navigation methods), cleans the extra chars and
/// accommodates the format.
/// TODO: check for a more "appealing" URL naming convention.
/// `() => MyHomeScreenView` becomes `/my-home-screen-view`.
String _cleanRouteName(String name) {
name = name.replaceAll('() => ', '');
/// uncommonent for URL styling.
// name = name.paramCase!;
if (!name.startsWith('/')) {
name = '/$name';
}
return Uri.tryParse(name)?.toString() ?? name;
}
/// change default config of Get
void config(
{bool? enableLog,
... ...
... ... @@ -46,10 +46,10 @@ class RouterReportManager<T> {
static void reportRouteDispose(Route disposed) {
if (Get.smartManagement != SmartManagement.onlyBuilder) {
WidgetsBinding.instance!.addPostFrameCallback((_) {
///TODO: Is necessary this comparator?
if (_current != disposed) {
_removeDependencyByRoute(disposed);
}
///TODO: Check if it's necessary to compare _current != disposed
///Adding it breaks the context Navigation logic,
///as it resolves by Route name.
_removeDependencyByRoute(disposed);
});
}
}
... ...
... ... @@ -77,6 +77,8 @@ extension GetStringUtils on String {
String? get camelCase => GetUtils.camelCase(this);
String? get paramCase => GetUtils.paramCase(this);
String numericOnly({bool firstWordOnly = false}) =>
GetUtils.numericOnly(this, firstWordOnly: firstWordOnly);
... ...
... ... @@ -551,6 +551,44 @@ class GetUtils {
return newString[0].toLowerCase() + newString.substring(1);
}
/// credits to "ReCase" package.
static final RegExp _upperAlphaRegex = RegExp(r'[A-Z]');
static final _symbolSet = {' ', '.', '/', '_', '\\', '-'};
static List<String> _groupIntoWords(String text) {
var sb = StringBuffer();
var words = <String>[];
var isAllCaps = text.toUpperCase() == text;
for (var i = 0; i < text.length; i++) {
var char = text[i];
var nextChar = i + 1 == text.length ? null : text[i + 1];
if (_symbolSet.contains(char)) {
continue;
}
sb.write(char);
var isEndOfWord = nextChar == null ||
(_upperAlphaRegex.hasMatch(nextChar) && !isAllCaps) ||
_symbolSet.contains(nextChar);
if (isEndOfWord) {
words.add('$sb');
sb.clear();
}
}
return words;
}
/// snake_case
static String? snakeCase(String? text, {String separator = '_'}) {
if (isNullOrBlank(text)!) {
return null;
}
return _groupIntoWords(text!)
.map((word) => word.toLowerCase()).join(separator);
}
/// param-case
static String? paramCase(String? text) => snakeCase(text, separator: '-');
/// Extract numeric value of string
/// Example: OTP 12312 27/04/2020 => 1231227042020ß
/// If firstword only is true, then the example return is "12312"
... ...