Showing
19 changed files
with
531 additions
and
335 deletions
| @@ -7,14 +7,11 @@ import 'package:get/get.dart'; | @@ -7,14 +7,11 @@ import 'package:get/get.dart'; | ||
| 7 | // import 'package:get_test/get_test.dart'; | 7 | // import 'package:get_test/get_test.dart'; |
| 8 | import 'package:matcher/matcher.dart' as m; | 8 | import 'package:matcher/matcher.dart' as m; |
| 9 | 9 | ||
| 10 | - | ||
| 11 | import '../lib/pages/home/domain/adapters/repository_adapter.dart'; | 10 | import '../lib/pages/home/domain/adapters/repository_adapter.dart'; |
| 12 | import '../lib/pages/home/domain/entity/cases_model.dart'; | 11 | import '../lib/pages/home/domain/entity/cases_model.dart'; |
| 13 | import '../lib/pages/home/presentation/controllers/home_controller.dart'; | 12 | import '../lib/pages/home/presentation/controllers/home_controller.dart'; |
| 14 | 13 | ||
| 15 | - | ||
| 16 | class MockRepositorySuccess implements IHomeRepository { | 14 | class MockRepositorySuccess implements IHomeRepository { |
| 17 | - | ||
| 18 | @override | 15 | @override |
| 19 | Future<CasesModel> getCases() async { | 16 | Future<CasesModel> getCases() async { |
| 20 | return CasesModel( | 17 | return CasesModel( |
| @@ -47,27 +47,6 @@ extension Inst on GetInterface { | @@ -47,27 +47,6 @@ extension Inst on GetInterface { | ||
| 47 | // ); | 47 | // ); |
| 48 | // } | 48 | // } |
| 49 | 49 | ||
| 50 | - /// async version of `Get.put()`. | ||
| 51 | - /// Awaits for the resolution of the Future from `builder()` parameter and | ||
| 52 | - /// stores the Instance returned. | ||
| 53 | - Future<S> putAsync<S>( | ||
| 54 | - AsyncInstanceBuilderCallback<S> builder, { | ||
| 55 | - String? tag, | ||
| 56 | - bool permanent = false, | ||
| 57 | - }) async { | ||
| 58 | - return put<S>(await builder(), tag: tag, permanent: permanent); | ||
| 59 | - } | ||
| 60 | - | ||
| 61 | - /// Injects an instance `<S>` in memory to be globally accessible. | ||
| 62 | - /// | ||
| 63 | - /// No need to define the generic type `<S>` as it's inferred from | ||
| 64 | - /// the [dependency] | ||
| 65 | - /// | ||
| 66 | - /// - [dependency] The Instance to be injected. | ||
| 67 | - /// - [tag] optionally, use a [tag] as an "id" to create multiple records of | ||
| 68 | - /// the same Type<[S]> | ||
| 69 | - /// - [permanent] keeps the Instance in memory, not following | ||
| 70 | - /// `Get.smartManagement` rules. | ||
| 71 | S put<S>( | 50 | S put<S>( |
| 72 | S dependency, { | 51 | S dependency, { |
| 73 | String? tag, | 52 | String? tag, |
| @@ -544,9 +544,9 @@ extension GetNavigationExt on GetInterface { | @@ -544,9 +544,9 @@ extension GetNavigationExt on GetInterface { | ||
| 544 | // return page; | 544 | // return page; |
| 545 | // } else if (page is Widget) { | 545 | // } else if (page is Widget) { |
| 546 | // Get.log( | 546 | // Get.log( |
| 547 | -// '''WARNING, consider using: "Get.$method(() => Page())" | 547 | +// '''WARNING, consider using: "Get.$method(() => Page())" |
| 548 | //instead of "Get.$method(Page())". | 548 | //instead of "Get.$method(Page())". |
| 549 | -// Using a widget function instead of a widget fully guarantees that the widget | 549 | +// Using a widget function instead of a widget fully guarantees that the widget |
| 550 | //and its controllers will be removed from memory when they are no longer used. | 550 | //and its controllers will be removed from memory when they are no longer used. |
| 551 | // '''); | 551 | // '''); |
| 552 | // return () => page; | 552 | // return () => page; |
| @@ -1253,8 +1253,6 @@ extension GetNavigationExt on GetInterface { | @@ -1253,8 +1253,6 @@ extension GetNavigationExt on GetInterface { | ||
| 1253 | set parameters(Map<String, String?> newParameters) => | 1253 | set parameters(Map<String, String?> newParameters) => |
| 1254 | _getxController.parameters = newParameters; | 1254 | _getxController.parameters = newParameters; |
| 1255 | 1255 | ||
| 1256 | - | ||
| 1257 | - | ||
| 1258 | bool get testMode => _getxController.testMode; | 1256 | bool get testMode => _getxController.testMode; |
| 1259 | set testMode(bool isTest) => _getxController.testMode = isTest; | 1257 | set testMode(bool isTest) => _getxController.testMode = isTest; |
| 1260 | 1258 |
| @@ -19,7 +19,8 @@ class GetInformationParser extends RouteInformationParser<RouteDecoder> { | @@ -19,7 +19,8 @@ class GetInformationParser extends RouteInformationParser<RouteDecoder> { | ||
| 19 | if (location == '/') { | 19 | if (location == '/') { |
| 20 | //check if there is a corresponding page | 20 | //check if there is a corresponding page |
| 21 | //if not, relocate to initialRoute | 21 | //if not, relocate to initialRoute |
| 22 | - if (!(Get.rootController.routerDelegate as GetDelegate).registeredRoutes | 22 | + if (!(Get.rootController.routerDelegate as GetDelegate) |
| 23 | + .registeredRoutes | ||
| 23 | .any((element) => element.name == '/')) { | 24 | .any((element) => element.name == '/')) { |
| 24 | location = initialRoute; | 25 | location = initialRoute; |
| 25 | } | 26 | } |
| @@ -45,7 +45,7 @@ class GetPage<T> extends Page<T> { | @@ -45,7 +45,7 @@ class GetPage<T> extends Page<T> { | ||
| 45 | 45 | ||
| 46 | final List<GetPage> children; | 46 | final List<GetPage> children; |
| 47 | final List<GetMiddleware>? middlewares; | 47 | final List<GetMiddleware>? middlewares; |
| 48 | - // final PathDecoded path; | 48 | + final PathDecoded path; |
| 49 | final GetPage? unknownRoute; | 49 | final GetPage? unknownRoute; |
| 50 | final bool showCupertinoParallax; | 50 | final bool showCupertinoParallax; |
| 51 | 51 | ||
| @@ -82,7 +82,7 @@ class GetPage<T> extends Page<T> { | @@ -82,7 +82,7 @@ class GetPage<T> extends Page<T> { | ||
| 82 | PreventDuplicateHandlingMode.reorderRoutes, | 82 | PreventDuplicateHandlingMode.reorderRoutes, |
| 83 | this.completer, | 83 | this.completer, |
| 84 | LocalKey? key, | 84 | LocalKey? key, |
| 85 | - }) : // path = _nameToRegex(name), | 85 | + }) : path = _nameToRegex(name), |
| 86 | assert(name.startsWith('/'), | 86 | assert(name.startsWith('/'), |
| 87 | 'It is necessary to start route name [$name] with a slash: /$name'), | 87 | 'It is necessary to start route name [$name] with a slash: /$name'), |
| 88 | super( | 88 | super( |
| @@ -168,26 +168,26 @@ class GetPage<T> extends Page<T> { | @@ -168,26 +168,26 @@ class GetPage<T> extends Page<T> { | ||
| 168 | return _page; | 168 | return _page; |
| 169 | } | 169 | } |
| 170 | 170 | ||
| 171 | - // static PathDecoded _nameToRegex(String path) { | ||
| 172 | - // var keys = <String?>[]; | 171 | + static PathDecoded _nameToRegex(String path) { |
| 172 | + var keys = <String?>[]; | ||
| 173 | 173 | ||
| 174 | - // String _replace(Match pattern) { | ||
| 175 | - // var buffer = StringBuffer('(?:'); | 174 | + String _replace(Match pattern) { |
| 175 | + var buffer = StringBuffer('(?:'); | ||
| 176 | 176 | ||
| 177 | - // if (pattern[1] != null) buffer.write('.'); | ||
| 178 | - // buffer.write('([\\w%+-._~!\$&\'()*,;=:@]+))'); | ||
| 179 | - // if (pattern[3] != null) buffer.write('?'); | 177 | + if (pattern[1] != null) buffer.write('.'); |
| 178 | + buffer.write('([\\w%+-._~!\$&\'()*,;=:@]+))'); | ||
| 179 | + if (pattern[3] != null) buffer.write('?'); | ||
| 180 | 180 | ||
| 181 | - // keys.add(pattern[2]); | ||
| 182 | - // return "$buffer"; | ||
| 183 | - // } | 181 | + keys.add(pattern[2]); |
| 182 | + return "$buffer"; | ||
| 183 | + } | ||
| 184 | 184 | ||
| 185 | - // var stringPath = '$path/?' | ||
| 186 | - // .replaceAllMapped(RegExp(r'(\.)?:(\w+)(\?)?'), _replace) | ||
| 187 | - // .replaceAll('//', '/'); | 185 | + var stringPath = '$path/?' |
| 186 | + .replaceAllMapped(RegExp(r'(\.)?:(\w+)(\?)?'), _replace) | ||
| 187 | + .replaceAll('//', '/'); | ||
| 188 | 188 | ||
| 189 | - // return PathDecoded(RegExp('^$stringPath\$'), keys); | ||
| 190 | - // } | 189 | + return PathDecoded(RegExp('^$stringPath\$'), keys); |
| 190 | + } | ||
| 191 | 191 | ||
| 192 | @override | 192 | @override |
| 193 | bool operator ==(Object other) { | 193 | bool operator ==(Object other) { |
| @@ -205,20 +205,20 @@ class GetPage<T> extends Page<T> { | @@ -205,20 +205,20 @@ class GetPage<T> extends Page<T> { | ||
| 205 | } | 205 | } |
| 206 | } | 206 | } |
| 207 | 207 | ||
| 208 | -// @immutable | ||
| 209 | -// class PathDecoded { | ||
| 210 | -// final RegExp regex; | ||
| 211 | -// final List<String?> keys; | ||
| 212 | -// const PathDecoded(this.regex, this.keys); | 208 | +@immutable |
| 209 | +class PathDecoded { | ||
| 210 | + final RegExp regex; | ||
| 211 | + final List<String?> keys; | ||
| 212 | + const PathDecoded(this.regex, this.keys); | ||
| 213 | 213 | ||
| 214 | -// @override | ||
| 215 | -// int get hashCode => regex.hashCode; | 214 | + @override |
| 215 | + int get hashCode => regex.hashCode; | ||
| 216 | 216 | ||
| 217 | -// @override | ||
| 218 | -// bool operator ==(Object other) { | ||
| 219 | -// if (identical(this, other)) return true; | 217 | + @override |
| 218 | + bool operator ==(Object other) { | ||
| 219 | + if (identical(this, other)) return true; | ||
| 220 | 220 | ||
| 221 | -// return other is PathDecoded && | ||
| 222 | -// other.regex == regex; // && listEquals(other.keys, keys); | ||
| 223 | -// } | ||
| 224 | -// } | 221 | + return other is PathDecoded && |
| 222 | + other.regex == regex; // && listEquals(other.keys, keys); | ||
| 223 | + } | ||
| 224 | +} |
| @@ -26,31 +26,28 @@ class GetDelegate extends RouterDelegate<RouteDecoder> | @@ -26,31 +26,28 @@ class GetDelegate extends RouterDelegate<RouteDecoder> | ||
| 26 | 26 | ||
| 27 | List<RouteDecoder> get activePages => _activePages; | 27 | List<RouteDecoder> get activePages => _activePages; |
| 28 | 28 | ||
| 29 | - final _routeTree = ParseRouteTree(); | 29 | + final _routeTree = ParseRouteTree(routes: []); |
| 30 | 30 | ||
| 31 | - final List<GetPage> _routes = []; | ||
| 32 | - | ||
| 33 | - List<GetPage> get registeredRoutes => _routes; | 31 | + List<GetPage> get registeredRoutes => _routeTree.routes; |
| 34 | 32 | ||
| 35 | void addPages(List<GetPage> getPages) { | 33 | void addPages(List<GetPage> getPages) { |
| 36 | - _routes.addRoutes(getPages); | 34 | + _routeTree.addRoutes(getPages); |
| 37 | } | 35 | } |
| 38 | 36 | ||
| 39 | void clearRouteTree() { | 37 | void clearRouteTree() { |
| 40 | - _routes.clear(); | 38 | + _routeTree.routes.clear(); |
| 41 | } | 39 | } |
| 42 | 40 | ||
| 43 | void addPage(GetPage getPage) { | 41 | void addPage(GetPage getPage) { |
| 44 | - _routes.addRoute(getPage); | 42 | + _routeTree.addRoute(getPage); |
| 45 | } | 43 | } |
| 46 | 44 | ||
| 47 | void removePage(GetPage getPage) { | 45 | void removePage(GetPage getPage) { |
| 48 | - _routes.removeRoute(getPage); | 46 | + _routeTree.removeRoute(getPage); |
| 49 | } | 47 | } |
| 50 | 48 | ||
| 51 | - RouteDecoder? matchRoute(String name, {PageSettings? arguments}) { | ||
| 52 | - final settings = _buildPageSettings(name, arguments); | ||
| 53 | - return _getRouteDecoder(settings); | 49 | + RouteDecoder matchRoute(String name, {PageSettings? arguments}) { |
| 50 | + return _routeTree.matchRoute(name, arguments: arguments); | ||
| 54 | } | 51 | } |
| 55 | 52 | ||
| 56 | // GlobalKey<NavigatorState> get navigatorKey => Get.key; | 53 | // GlobalKey<NavigatorState> get navigatorKey => Get.key; |
| @@ -141,9 +138,7 @@ class GetDelegate extends RouterDelegate<RouteDecoder> | @@ -141,9 +138,7 @@ class GetDelegate extends RouterDelegate<RouteDecoder> | ||
| 141 | } | 138 | } |
| 142 | 139 | ||
| 143 | Map<String, String> get parameters { | 140 | Map<String, String> get parameters { |
| 144 | - return currentConfiguration?.route?.parameters ?? | ||
| 145 | - // currentConfiguration?.pageSettings?.params ?? | ||
| 146 | - {}; | 141 | + return currentConfiguration?.pageSettings?.params ?? {}; |
| 147 | } | 142 | } |
| 148 | 143 | ||
| 149 | PageSettings? get pageSettings { | 144 | PageSettings? get pageSettings { |
| @@ -365,7 +360,7 @@ class GetDelegate extends RouterDelegate<RouteDecoder> | @@ -365,7 +360,7 @@ class GetDelegate extends RouterDelegate<RouteDecoder> | ||
| 365 | }) async { | 360 | }) async { |
| 366 | routeName = _cleanRouteName("/${page.runtimeType}"); | 361 | routeName = _cleanRouteName("/${page.runtimeType}"); |
| 367 | // if (preventDuplicateHandlingMode == | 362 | // if (preventDuplicateHandlingMode == |
| 368 | - // PreventDuplicateHandlingMode.Recreate) { | 363 | + //PreventDuplicateHandlingMode.Recreate) { |
| 369 | // routeName = routeName + page.hashCode.toString(); | 364 | // routeName = routeName + page.hashCode.toString(); |
| 370 | // } | 365 | // } |
| 371 | 366 | ||
| @@ -384,14 +379,14 @@ class GetDelegate extends RouterDelegate<RouteDecoder> | @@ -384,14 +379,14 @@ class GetDelegate extends RouterDelegate<RouteDecoder> | ||
| 384 | preventDuplicateHandlingMode: preventDuplicateHandlingMode, | 379 | preventDuplicateHandlingMode: preventDuplicateHandlingMode, |
| 385 | ); | 380 | ); |
| 386 | 381 | ||
| 387 | - _routes.addRoute(getPage); | 382 | + _routeTree.addRoute(getPage); |
| 388 | final args = _buildPageSettings(routeName, arguments); | 383 | final args = _buildPageSettings(routeName, arguments); |
| 389 | final route = _getRouteDecoder<T>(args); | 384 | final route = _getRouteDecoder<T>(args); |
| 390 | final result = await _push<T>( | 385 | final result = await _push<T>( |
| 391 | route!, | 386 | route!, |
| 392 | rebuildStack: rebuildStack, | 387 | rebuildStack: rebuildStack, |
| 393 | ); | 388 | ); |
| 394 | - _routes.removeRoute(getPage); | 389 | + _routeTree.removeRoute(getPage); |
| 395 | return result; | 390 | return result; |
| 396 | } | 391 | } |
| 397 | 392 | ||
| @@ -628,7 +623,7 @@ class GetDelegate extends RouterDelegate<RouteDecoder> | @@ -628,7 +623,7 @@ class GetDelegate extends RouterDelegate<RouteDecoder> | ||
| 628 | 623 | ||
| 629 | Future<T?> _replace<T>(PageSettings arguments, GetPage<T> page) async { | 624 | Future<T?> _replace<T>(PageSettings arguments, GetPage<T> page) async { |
| 630 | final index = _activePages.length > 1 ? _activePages.length - 1 : 0; | 625 | final index = _activePages.length > 1 ? _activePages.length - 1 : 0; |
| 631 | - _routes.addRoute(page); | 626 | + _routeTree.addRoute(page); |
| 632 | 627 | ||
| 633 | final activePage = _getRouteDecoder(arguments); | 628 | final activePage = _getRouteDecoder(arguments); |
| 634 | 629 | ||
| @@ -638,7 +633,7 @@ class GetDelegate extends RouterDelegate<RouteDecoder> | @@ -638,7 +633,7 @@ class GetDelegate extends RouterDelegate<RouteDecoder> | ||
| 638 | 633 | ||
| 639 | notifyListeners(); | 634 | notifyListeners(); |
| 640 | final result = await activePage.route?.completer?.future as Future<T?>?; | 635 | final result = await activePage.route?.completer?.future as Future<T?>?; |
| 641 | - _routes.removeRoute(page); | 636 | + _routeTree.removeRoute(page); |
| 642 | 637 | ||
| 643 | return result; | 638 | return result; |
| 644 | } | 639 | } |
| @@ -683,8 +678,7 @@ class GetDelegate extends RouterDelegate<RouteDecoder> | @@ -683,8 +678,7 @@ class GetDelegate extends RouterDelegate<RouteDecoder> | ||
| 683 | page = uri.toString(); | 678 | page = uri.toString(); |
| 684 | } | 679 | } |
| 685 | 680 | ||
| 686 | - final decoder = | ||
| 687 | - _routeTree.matchRoute(registeredRoutes, page, arguments: arguments); | 681 | + final decoder = _routeTree.matchRoute(page, arguments: arguments); |
| 688 | final route = decoder.route; | 682 | final route = decoder.route; |
| 689 | if (route == null) return null; | 683 | if (route == null) return null; |
| 690 | 684 | ||
| @@ -707,7 +701,7 @@ class GetDelegate extends RouterDelegate<RouteDecoder> | @@ -707,7 +701,7 @@ class GetDelegate extends RouterDelegate<RouteDecoder> | ||
| 707 | completer: _activePages.isEmpty ? null : Completer(), | 701 | completer: _activePages.isEmpty ? null : Completer(), |
| 708 | arguments: arguments, | 702 | arguments: arguments, |
| 709 | parameters: parameters, | 703 | parameters: parameters, |
| 710 | - // key: ValueKey(arguments.name), | 704 | + key: ValueKey(arguments.name), |
| 711 | ); | 705 | ); |
| 712 | 706 | ||
| 713 | return decoder; | 707 | return decoder; |
| @@ -13,10 +13,6 @@ class Dependencies { | @@ -13,10 +13,6 @@ class Dependencies { | ||
| 13 | return find<S>(); | 13 | return find<S>(); |
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | - Future<S> putAsync<S>(AsyncInstanceBuilderCallback<S> builder, | ||
| 17 | - {String? tag, bool permanent = false}) async => | ||
| 18 | - Get.putAsync<S>(builder, tag: tag, permanent: permanent); | ||
| 19 | - | ||
| 20 | void create<S>(InstanceBuilderCallback<S> builder, | 16 | void create<S>(InstanceBuilderCallback<S> builder, |
| 21 | {String? tag, bool permanent = true}) => | 17 | {String? tag, bool permanent = true}) => |
| 22 | Get.create<S>(builder, tag: tag, permanent: permanent); | 18 | Get.create<S>(builder, tag: tag, permanent: permanent); |
| 1 | -import 'dart:math'; | ||
| 2 | - | ||
| 3 | import 'package:flutter/foundation.dart'; | 1 | import 'package:flutter/foundation.dart'; |
| 4 | 2 | ||
| 5 | import '../../../route_manager.dart'; | 3 | import '../../../route_manager.dart'; |
| @@ -18,11 +16,10 @@ class RouteDecoder { | @@ -18,11 +16,10 @@ class RouteDecoder { | ||
| 18 | final args = PageSettings(uri); | 16 | final args = PageSettings(uri); |
| 19 | final decoder = (Get.rootController.routerDelegate as GetDelegate) | 17 | final decoder = (Get.rootController.routerDelegate as GetDelegate) |
| 20 | .matchRoute(location, arguments: args); | 18 | .matchRoute(location, arguments: args); |
| 21 | - | ||
| 22 | - decoder!.route = decoder.route?.copy( | 19 | + decoder.route = decoder.route?.copy( |
| 23 | completer: null, | 20 | completer: null, |
| 24 | arguments: args, | 21 | arguments: args, |
| 25 | - parameters: decoder.parameters, | 22 | + parameters: args.params, |
| 26 | ); | 23 | ); |
| 27 | return decoder; | 24 | return decoder; |
| 28 | } | 25 | } |
| @@ -81,62 +78,96 @@ class RouteDecoder { | @@ -81,62 +78,96 @@ class RouteDecoder { | ||
| 81 | } | 78 | } |
| 82 | 79 | ||
| 83 | class ParseRouteTree { | 80 | class ParseRouteTree { |
| 84 | - RouteDecoder matchRoute(List<GetPage> routes, String name, | ||
| 85 | - {PageSettings? arguments}) { | ||
| 86 | - final args = arguments ?? PageSettings(Uri.parse(name)); | ||
| 87 | - final treeBranch = routes | ||
| 88 | - .where((e) => RouteParser.hasMatch( | ||
| 89 | - pushedRoute: name, routeName: e.name, withChildren: true)) | ||
| 90 | - .map((e) { | ||
| 91 | - final parameters = | ||
| 92 | - RouteParser.parse(pushedRoute: name, routeName: e.name).parameters; | ||
| 93 | - final routeParams = e.parameters; | ||
| 94 | - if (routeParams != null) { | ||
| 95 | - parameters.addAll(routeParams); | ||
| 96 | - } | ||
| 97 | - if (args.params.isNotEmpty) { | ||
| 98 | - parameters.addAll(args.params); | 81 | + ParseRouteTree({ |
| 82 | + required this.routes, | ||
| 83 | + }); | ||
| 84 | + | ||
| 85 | + final List<GetPage> routes; | ||
| 86 | + | ||
| 87 | + RouteDecoder matchRoute(String name, {PageSettings? arguments}) { | ||
| 88 | + final uri = Uri.parse(name); | ||
| 89 | + // /home/profile/123 => home,profile,123 => /,/home,/home/profile,/home/profile/123 | ||
| 90 | + final split = uri.path.split('/').where((element) => element.isNotEmpty); | ||
| 91 | + var curPath = '/'; | ||
| 92 | + final cumulativePaths = <String>[ | ||
| 93 | + '/', | ||
| 94 | + ]; | ||
| 95 | + for (var item in split) { | ||
| 96 | + if (curPath.endsWith('/')) { | ||
| 97 | + curPath += item; | ||
| 98 | + } else { | ||
| 99 | + curPath += '/$item'; | ||
| 99 | } | 100 | } |
| 101 | + cumulativePaths.add(curPath); | ||
| 102 | + } | ||
| 100 | 103 | ||
| 101 | - args.params.clear(); | ||
| 102 | - args.params.addAll(parameters); | ||
| 103 | - | ||
| 104 | - return e.copy( | ||
| 105 | - settings: args, | ||
| 106 | - parameters: parameters, | 104 | + final treeBranch = cumulativePaths |
| 105 | + .map((e) => MapEntry(e, _findRoute(e))) | ||
| 106 | + .where((element) => element.value != null) | ||
| 107 | + | ||
| 108 | + ///Prevent page be disposed | ||
| 109 | + .map((e) => MapEntry(e.key, e.value!.copy(key: ValueKey(e.key)))) | ||
| 110 | + .toList(); | ||
| 111 | + | ||
| 112 | + final params = Map<String, String>.from(uri.queryParameters); | ||
| 113 | + if (treeBranch.isNotEmpty) { | ||
| 114 | + //route is found, do further parsing to get nested query params | ||
| 115 | + final lastRoute = treeBranch.last; | ||
| 116 | + final parsedParams = _parseParams(name, lastRoute.value.path); | ||
| 117 | + if (parsedParams.isNotEmpty) { | ||
| 118 | + params.addAll(parsedParams); | ||
| 119 | + } | ||
| 120 | + //copy parameters to all pages. | ||
| 121 | + final mappedTreeBranch = treeBranch | ||
| 122 | + .map( | ||
| 123 | + (e) => e.value.copy( | ||
| 124 | + parameters: { | ||
| 125 | + if (e.value.parameters != null) ...e.value.parameters!, | ||
| 126 | + ...params, | ||
| 127 | + }, | ||
| 128 | + name: e.key, | ||
| 129 | + ), | ||
| 130 | + ) | ||
| 131 | + .toList(); | ||
| 132 | + arguments?.params.clear(); | ||
| 133 | + arguments?.params.addAll(params); | ||
| 134 | + return RouteDecoder( | ||
| 135 | + mappedTreeBranch, | ||
| 136 | + arguments, | ||
| 107 | ); | 137 | ); |
| 108 | - }).toList(); | 138 | + } |
| 139 | + | ||
| 140 | + arguments?.params.clear(); | ||
| 141 | + arguments?.params.addAll(params); | ||
| 109 | 142 | ||
| 110 | //route not found | 143 | //route not found |
| 111 | return RouteDecoder( | 144 | return RouteDecoder( |
| 112 | - treeBranch, | 145 | + treeBranch.map((e) => e.value).toList(), |
| 113 | arguments, | 146 | arguments, |
| 114 | ); | 147 | ); |
| 115 | } | 148 | } |
| 116 | -} | ||
| 117 | 149 | ||
| 118 | -extension FirstWhereOrNullExt<T> on List<GetPage<T>> { | ||
| 119 | - void addRoutes(List<GetPage<T>> getPages) { | 150 | + void addRoutes<T>(List<GetPage<T>> getPages) { |
| 120 | for (final route in getPages) { | 151 | for (final route in getPages) { |
| 121 | addRoute(route); | 152 | addRoute(route); |
| 122 | } | 153 | } |
| 123 | } | 154 | } |
| 124 | 155 | ||
| 125 | - void removeRoutes(List<GetPage<T>> getPages) { | 156 | + void removeRoutes<T>(List<GetPage<T>> getPages) { |
| 126 | for (final route in getPages) { | 157 | for (final route in getPages) { |
| 127 | removeRoute(route); | 158 | removeRoute(route); |
| 128 | } | 159 | } |
| 129 | } | 160 | } |
| 130 | 161 | ||
| 131 | - void removeRoute(GetPage<T> route) { | ||
| 132 | - remove(route); | 162 | + void removeRoute<T>(GetPage<T> route) { |
| 163 | + routes.remove(route); | ||
| 133 | for (var page in _flattenPage(route)) { | 164 | for (var page in _flattenPage(route)) { |
| 134 | removeRoute(page); | 165 | removeRoute(page); |
| 135 | } | 166 | } |
| 136 | } | 167 | } |
| 137 | 168 | ||
| 138 | - void addRoute(GetPage<T> route) { | ||
| 139 | - add(route); | 169 | + void addRoute<T>(GetPage<T> route) { |
| 170 | + routes.add(route); | ||
| 140 | 171 | ||
| 141 | // Add Page children. | 172 | // Add Page children. |
| 142 | for (var page in _flattenPage(route)) { | 173 | for (var page in _flattenPage(route)) { |
| @@ -144,15 +175,14 @@ extension FirstWhereOrNullExt<T> on List<GetPage<T>> { | @@ -144,15 +175,14 @@ extension FirstWhereOrNullExt<T> on List<GetPage<T>> { | ||
| 144 | } | 175 | } |
| 145 | } | 176 | } |
| 146 | 177 | ||
| 147 | - List<GetPage<T>> _flattenPage(GetPage<T> route) { | ||
| 148 | - final result = <GetPage<T>>[]; | 178 | + List<GetPage> _flattenPage(GetPage route) { |
| 179 | + final result = <GetPage>[]; | ||
| 149 | if (route.children.isEmpty) { | 180 | if (route.children.isEmpty) { |
| 150 | return result; | 181 | return result; |
| 151 | } | 182 | } |
| 152 | 183 | ||
| 153 | - var parentPathOld = route.name; | 184 | + final parentPath = route.name; |
| 154 | for (var page in route.children) { | 185 | for (var page in route.children) { |
| 155 | - final parentPath2 = (parentPathOld + page.name).replaceAll(r'//', '/'); | ||
| 156 | // Add Parent middlewares to children | 186 | // Add Parent middlewares to children |
| 157 | final parentMiddlewares = [ | 187 | final parentMiddlewares = [ |
| 158 | if (page.middlewares != null) ...page.middlewares!, | 188 | if (page.middlewares != null) ...page.middlewares!, |
| @@ -160,146 +190,71 @@ extension FirstWhereOrNullExt<T> on List<GetPage<T>> { | @@ -160,146 +190,71 @@ extension FirstWhereOrNullExt<T> on List<GetPage<T>> { | ||
| 160 | ]; | 190 | ]; |
| 161 | result.add( | 191 | result.add( |
| 162 | _addChild( | 192 | _addChild( |
| 163 | - page as GetPage<T>, | ||
| 164 | - parentPath2, | 193 | + page, |
| 194 | + parentPath, | ||
| 165 | parentMiddlewares, | 195 | parentMiddlewares, |
| 166 | ), | 196 | ), |
| 167 | ); | 197 | ); |
| 168 | 198 | ||
| 169 | final children = _flattenPage(page); | 199 | final children = _flattenPage(page); |
| 170 | - // for (var child in children) { | ||
| 171 | - // final parentPath = (parentPath2 + page.name).replaceAll(r'//', '/'); | ||
| 172 | - // result.add(_addChild( | ||
| 173 | - // child, | ||
| 174 | - // parentPath, | ||
| 175 | - // [ | ||
| 176 | - // ...parentMiddlewares, | ||
| 177 | - // if (child.middlewares != null) ...child.middlewares!, | ||
| 178 | - // ], | ||
| 179 | - // )); | ||
| 180 | - // } | 200 | + for (var child in children) { |
| 201 | + result.add(_addChild( | ||
| 202 | + child, | ||
| 203 | + parentPath, | ||
| 204 | + [ | ||
| 205 | + ...parentMiddlewares, | ||
| 206 | + if (child.middlewares != null) ...child.middlewares!, | ||
| 207 | + ], | ||
| 208 | + )); | ||
| 209 | + } | ||
| 181 | } | 210 | } |
| 182 | return result; | 211 | return result; |
| 183 | } | 212 | } |
| 184 | 213 | ||
| 185 | /// Change the Path for a [GetPage] | 214 | /// Change the Path for a [GetPage] |
| 186 | - GetPage<T> _addChild( | ||
| 187 | - GetPage<T> origin, String parentPath, List<GetMiddleware> middlewares) { | 215 | + GetPage _addChild( |
| 216 | + GetPage origin, String parentPath, List<GetMiddleware> middlewares) { | ||
| 188 | return origin.copy( | 217 | return origin.copy( |
| 189 | middlewares: middlewares, | 218 | middlewares: middlewares, |
| 190 | - name: parentPath, | ||
| 191 | - key: ValueKey(parentPath), | 219 | + name: (parentPath + origin.name).replaceAll(r'//', '/'), |
| 220 | + // key: | ||
| 192 | ); | 221 | ); |
| 193 | } | 222 | } |
| 194 | 223 | ||
| 195 | - // GetPage<T>? _findRoute(String name) { | ||
| 196 | - // final value = firstWhereOrNull( | ||
| 197 | - // (route) => route.path.regex.hasMatch(name), | ||
| 198 | - // ); | ||
| 199 | - | ||
| 200 | - // return value; | ||
| 201 | - // } | ||
| 202 | -} | 224 | + GetPage? _findRoute(String name) { |
| 225 | + final value = routes.firstWhereOrNull( | ||
| 226 | + (route) => route.path.regex.hasMatch(name), | ||
| 227 | + ); | ||
| 203 | 228 | ||
| 204 | -extension FirstWhereExt<T> on List<T> { | ||
| 205 | - /// The first element satisfying [test], or `null` if there are none. | ||
| 206 | - T? firstWhereOrNull(bool Function(T element) test) { | ||
| 207 | - for (var element in this) { | ||
| 208 | - if (test(element)) return element; | ||
| 209 | - } | ||
| 210 | - return null; | 229 | + return value; |
| 211 | } | 230 | } |
| 212 | -} | ||
| 213 | - | ||
| 214 | -class RouteParser { | ||
| 215 | - static RouteParser parse({required String pushedRoute, required routeName}) { | ||
| 216 | - final data = RouteParser(pushedRoute: pushedRoute, routeName: routeName); | ||
| 217 | 231 | ||
| 218 | - final minLength = | ||
| 219 | - min(data.originalPathSegments.length, data.newPathSegments.length); | ||
| 220 | - | ||
| 221 | - for (var i = 0; i < minLength; i++) { | ||
| 222 | - final originalPathSegment = data.originalPathSegments[i]; | ||
| 223 | - final newPathSegment = Uri.parse(data.newPathSegments[i]); | ||
| 224 | - | ||
| 225 | - if (originalPathSegment.startsWith(':')) { | ||
| 226 | - final key = originalPathSegment.replaceFirst(':', ''); | ||
| 227 | - data.parameters[key] = newPathSegment.toString(); | ||
| 228 | - data.matchingSegments.add(newPathSegment); | ||
| 229 | - continue; | 232 | + Map<String, String> _parseParams(String path, PathDecoded routePath) { |
| 233 | + final params = <String, String>{}; | ||
| 234 | + var idx = path.indexOf('?'); | ||
| 235 | + if (idx > -1) { | ||
| 236 | + path = path.substring(0, idx); | ||
| 237 | + final uri = Uri.tryParse(path); | ||
| 238 | + if (uri != null) { | ||
| 239 | + params.addAll(uri.queryParameters); | ||
| 230 | } | 240 | } |
| 231 | - | ||
| 232 | - if (newPathSegment.path == originalPathSegment) { | ||
| 233 | - data.matchingSegments.add(newPathSegment); | ||
| 234 | - data.parameters.addAll(data.newRouteUri.queryParameters); | ||
| 235 | - | ||
| 236 | - continue; | ||
| 237 | - } else { | ||
| 238 | - break; | ||
| 239 | - } | ||
| 240 | - } | ||
| 241 | - | ||
| 242 | - return data; | ||
| 243 | - } | ||
| 244 | - | ||
| 245 | - static bool hasMatch({ | ||
| 246 | - required String pushedRoute, | ||
| 247 | - required routeName, | ||
| 248 | - bool withChildren = false, | ||
| 249 | - }) { | ||
| 250 | - final data = RouteParser(pushedRoute: pushedRoute, routeName: routeName); | ||
| 251 | - final matches = <bool>[]; | ||
| 252 | - | ||
| 253 | - final minLength = | ||
| 254 | - min(data.originalPathSegments.length, data.newPathSegments.length); | ||
| 255 | - | ||
| 256 | - if ((!withChildren && | ||
| 257 | - data.newPathSegments.length > data.originalPathSegments.length) || | ||
| 258 | - data.newPathSegments.length < data.originalPathSegments.length) { | ||
| 259 | - matches.add(false); | ||
| 260 | } | 241 | } |
| 242 | + var paramsMatch = routePath.regex.firstMatch(path); | ||
| 261 | 243 | ||
| 262 | - for (var i = 0; i < minLength; i++) { | ||
| 263 | - final originalPathSegment = data.originalPathSegments[i]; | ||
| 264 | - final newPathSegment = Uri.parse(data.newPathSegments[i]); | ||
| 265 | - | ||
| 266 | - if (originalPathSegment.startsWith(':')) { | ||
| 267 | - matches.add(true); | ||
| 268 | - continue; | ||
| 269 | - } | ||
| 270 | - | ||
| 271 | - if (newPathSegment.path == originalPathSegment) { | ||
| 272 | - matches.add(true); | ||
| 273 | - continue; | ||
| 274 | - } else { | ||
| 275 | - matches.add(false); | ||
| 276 | - break; | ||
| 277 | - } | 244 | + for (var i = 0; i < routePath.keys.length; i++) { |
| 245 | + var param = Uri.decodeQueryComponent(paramsMatch![i + 1]!); | ||
| 246 | + params[routePath.keys[i]!] = param; | ||
| 278 | } | 247 | } |
| 279 | - | ||
| 280 | - return matches.every((element) => element); | 248 | + return params; |
| 281 | } | 249 | } |
| 250 | +} | ||
| 282 | 251 | ||
| 283 | - RouteParser({required String routeName, required String pushedRoute}) | ||
| 284 | - : _cleanRouteName = '/' + | ||
| 285 | - routeName | ||
| 286 | - .replaceAll(RegExp(r'^\s+|\s+$'), '') | ||
| 287 | - .replaceAll(RegExp(r'^\/+|\/+$'), ''), | ||
| 288 | - newRouteUri = Uri.parse(pushedRoute) { | ||
| 289 | - originalRouteUri = Uri(path: _cleanRouteName); | 252 | +extension FirstWhereOrNullExt<T> on List<T> { |
| 253 | + /// The first element satisfying [test], or `null` if there are none. | ||
| 254 | + T? firstWhereOrNull(bool Function(T element) test) { | ||
| 255 | + for (var element in this) { | ||
| 256 | + if (test(element)) return element; | ||
| 257 | + } | ||
| 258 | + return null; | ||
| 290 | } | 259 | } |
| 291 | - late final Uri originalRouteUri; | ||
| 292 | - | ||
| 293 | - final Uri newRouteUri; | ||
| 294 | - final Map<String, String> parameters = <String, String>{}; | ||
| 295 | - final List<Uri> matchingSegments = <Uri>[]; | ||
| 296 | - final String _cleanRouteName; | ||
| 297 | - | ||
| 298 | - List<String> get newPathSegments => newRouteUri.pathSegments; | ||
| 299 | - List<String> get originalPathSegments => originalRouteUri.pathSegments; | ||
| 300 | - String get matchingPath => '/' + matchingSegments.join('/'); | ||
| 301 | - | ||
| 302 | - @override | ||
| 303 | - String toString() => | ||
| 304 | - 'RouteParser(originalRouteUri: $originalRouteUri, newRouteUri: $newRouteUri, _cleanRouteName: $_cleanRouteName)'; | ||
| 305 | } | 260 | } |
| @@ -234,9 +234,8 @@ class PageRedirect { | @@ -234,9 +234,8 @@ class PageRedirect { | ||
| 234 | if (settings == null && route != null) { | 234 | if (settings == null && route != null) { |
| 235 | settings = route; | 235 | settings = route; |
| 236 | } | 236 | } |
| 237 | - final match = context.navigation | ||
| 238 | - .matchRoute((settings!.arguments as PageSettings).name); | ||
| 239 | - Get.parameters = match!.parameters; | 237 | + final match = context.navigation.matchRoute(settings!.name!); |
| 238 | + Get.parameters = match.parameters; | ||
| 240 | 239 | ||
| 241 | // No Match found | 240 | // No Match found |
| 242 | if (match.route == null) { | 241 | if (match.route == null) { |
| @@ -147,8 +147,7 @@ class GetRouterOutlet extends RouterOutlet<GetDelegate, RouteDecoder> { | @@ -147,8 +147,7 @@ class GetRouterOutlet extends RouterOutlet<GetDelegate, RouteDecoder> { | ||
| 147 | return ret; | 147 | return ret; |
| 148 | }, | 148 | }, |
| 149 | emptyPage: (delegate) => | 149 | emptyPage: (delegate) => |
| 150 | - delegate.matchRoute(initialRoute)?.route ?? | ||
| 151 | - delegate.notFoundRoute, | 150 | + delegate.matchRoute(initialRoute).route ?? delegate.notFoundRoute, |
| 152 | key: Get.nestedKey(anchorRoute)?.navigatorKey, | 151 | key: Get.nestedKey(anchorRoute)?.navigatorKey, |
| 153 | delegate: delegate, | 152 | delegate: delegate, |
| 154 | ); | 153 | ); |
| @@ -68,18 +68,6 @@ mixin StateMixin<T> on ListNotifier { | @@ -68,18 +68,6 @@ mixin StateMixin<T> on ListNotifier { | ||
| 68 | if (status != this.status) { | 68 | if (status != this.status) { |
| 69 | this.status = status; | 69 | this.status = status; |
| 70 | } | 70 | } |
| 71 | - // var _canUpdate = false; | ||
| 72 | - // if (status != null) { | ||
| 73 | - // _status = status; | ||
| 74 | - // _canUpdate = true; | ||
| 75 | - // } | ||
| 76 | - // if (newState != _value) { | ||
| 77 | - // _value = newState; | ||
| 78 | - // _canUpdate = true; | ||
| 79 | - // } | ||
| 80 | - // if (_canUpdate) { | ||
| 81 | - // refresh(); | ||
| 82 | - // } | ||
| 83 | } | 71 | } |
| 84 | 72 | ||
| 85 | void futurize(Future<T> Function() Function() body, | 73 | void futurize(Future<T> Function() Function() body, |
| 1 | -import 'dart:async'; | ||
| 2 | import 'dart:collection'; | 1 | import 'dart:collection'; |
| 3 | 2 | ||
| 4 | import 'package:flutter/foundation.dart'; | 3 | import 'package:flutter/foundation.dart'; |
| @@ -24,8 +23,8 @@ class ListNotifierGroup = ListNotifier with ListNotifierGroupMixin; | @@ -24,8 +23,8 @@ class ListNotifierGroup = ListNotifier with ListNotifierGroupMixin; | ||
| 24 | mixin ListNotifierSingleMixin on Listenable { | 23 | mixin ListNotifierSingleMixin on Listenable { |
| 25 | List<GetStateUpdate>? _updaters = <GetStateUpdate>[]; | 24 | List<GetStateUpdate>? _updaters = <GetStateUpdate>[]; |
| 26 | 25 | ||
| 27 | - int _version = 0; | ||
| 28 | - int _microtaskVersion = 0; | 26 | + // final int _version = 0; |
| 27 | + // final int _microtaskVersion = 0; | ||
| 29 | 28 | ||
| 30 | @override | 29 | @override |
| 31 | Disposer addListener(GetStateUpdate listener) { | 30 | Disposer addListener(GetStateUpdate listener) { |
| @@ -61,16 +60,18 @@ mixin ListNotifierSingleMixin on Listenable { | @@ -61,16 +60,18 @@ mixin ListNotifierSingleMixin on Listenable { | ||
| 61 | } | 60 | } |
| 62 | 61 | ||
| 63 | void _notifyUpdate() { | 62 | void _notifyUpdate() { |
| 64 | - if (_microtaskVersion == _version) { | ||
| 65 | - _microtaskVersion++; | ||
| 66 | - scheduleMicrotask(() { | ||
| 67 | - _version++; | ||
| 68 | - _microtaskVersion = _version; | ||
| 69 | - for (var element in _updaters!) { | ||
| 70 | - element(); | ||
| 71 | - } | ||
| 72 | - }); | 63 | + // if (_microtaskVersion == _version) { |
| 64 | + // _microtaskVersion++; | ||
| 65 | + // scheduleMicrotask(() { | ||
| 66 | + // _version++; | ||
| 67 | + // _microtaskVersion = _version; | ||
| 68 | + final list = _updaters?.toList() ?? []; | ||
| 69 | + | ||
| 70 | + for (var element in list) { | ||
| 71 | + element(); | ||
| 73 | } | 72 | } |
| 73 | + // }); | ||
| 74 | + // } | ||
| 74 | } | 75 | } |
| 75 | 76 | ||
| 76 | bool get isDisposed => _updaters == null; | 77 | bool get isDisposed => _updaters == null; |
| @@ -2,7 +2,6 @@ import 'dart:async'; | @@ -2,7 +2,6 @@ import 'dart:async'; | ||
| 2 | 2 | ||
| 3 | import 'package:flutter/widgets.dart'; | 3 | import 'package:flutter/widgets.dart'; |
| 4 | 4 | ||
| 5 | -import '../../../get_core/src/typedefs.dart'; | ||
| 6 | import 'list_notifier.dart'; | 5 | import 'list_notifier.dart'; |
| 7 | 6 | ||
| 8 | typedef ValueBuilderUpdateCallback<T> = void Function(T snapshot); | 7 | typedef ValueBuilderUpdateCallback<T> = void Function(T snapshot); |
lib/get_utils/src/equality/equality.dart
0 → 100644
| 1 | +library equality; | ||
| 2 | + | ||
| 3 | +import 'dart:collection'; | ||
| 4 | + | ||
| 5 | +mixin Equality { | ||
| 6 | + List get props; | ||
| 7 | + | ||
| 8 | + @override | ||
| 9 | + bool operator ==(dynamic other) { | ||
| 10 | + return identical(this, other) || | ||
| 11 | + const DeepCollectionEquality().equals(props, other.props); | ||
| 12 | + } | ||
| 13 | + | ||
| 14 | + @override | ||
| 15 | + int get hashCode { | ||
| 16 | + return runtimeType.hashCode ^ const DeepCollectionEquality().hash(props); | ||
| 17 | + } | ||
| 18 | +} | ||
| 19 | + | ||
| 20 | +const int _hashMask = 0x7fffffff; | ||
| 21 | + | ||
| 22 | +/// A generic equality relation on objects. | ||
| 23 | +abstract class IEquality<E> { | ||
| 24 | + const factory IEquality() = DefaultEquality<E>; | ||
| 25 | + | ||
| 26 | + /// Compare two elements for being equal. | ||
| 27 | + /// | ||
| 28 | + /// This should be a proper equality relation. | ||
| 29 | + bool equals(E e1, E e2); | ||
| 30 | + | ||
| 31 | + /// Get a hashcode of an element. | ||
| 32 | + /// | ||
| 33 | + /// The hashcode should be compatible with [equals], so that if | ||
| 34 | + /// `equals(a, b)` then `hash(a) == hash(b)`. | ||
| 35 | + int hash(E e); | ||
| 36 | + | ||
| 37 | + /// Test whether an object is a valid argument to [equals] and [hash]. | ||
| 38 | + /// | ||
| 39 | + /// Some implementations may be restricted to only work on specific types | ||
| 40 | + /// of objects. | ||
| 41 | + bool isValidKey(Object? o); | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +class DefaultEquality<E> implements IEquality<E> { | ||
| 45 | + const DefaultEquality(); | ||
| 46 | + @override | ||
| 47 | + bool equals(Object? e1, Object? e2) => e1 == e2; | ||
| 48 | + @override | ||
| 49 | + int hash(Object? e) => e.hashCode; | ||
| 50 | + @override | ||
| 51 | + bool isValidKey(Object? o) => true; | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +/// Equality of objects that compares only the identity of the objects. | ||
| 55 | +class IdentityEquality<E> implements IEquality<E> { | ||
| 56 | + const IdentityEquality(); | ||
| 57 | + @override | ||
| 58 | + bool equals(E e1, E e2) => identical(e1, e2); | ||
| 59 | + @override | ||
| 60 | + int hash(E e) => identityHashCode(e); | ||
| 61 | + @override | ||
| 62 | + bool isValidKey(Object? o) => true; | ||
| 63 | +} | ||
| 64 | + | ||
| 65 | +class DeepCollectionEquality implements IEquality { | ||
| 66 | + final IEquality _base = const DefaultEquality<Never>(); | ||
| 67 | + final bool _unordered = false; | ||
| 68 | + const DeepCollectionEquality(); | ||
| 69 | + | ||
| 70 | + @override | ||
| 71 | + bool equals(e1, e2) { | ||
| 72 | + if (e1 is Set) { | ||
| 73 | + return e2 is Set && SetEquality(this).equals(e1, e2); | ||
| 74 | + } | ||
| 75 | + if (e1 is Map) { | ||
| 76 | + return e2 is Map && MapEquality(keys: this, values: this).equals(e1, e2); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + if (e1 is List) { | ||
| 80 | + return e2 is List && ListEquality(this).equals(e1, e2); | ||
| 81 | + } | ||
| 82 | + if (e1 is Iterable) { | ||
| 83 | + return e2 is Iterable && IterableEquality(this).equals(e1, e2); | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + return _base.equals(e1, e2); | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + @override | ||
| 90 | + int hash(Object? o) { | ||
| 91 | + if (o is Set) return SetEquality(this).hash(o); | ||
| 92 | + if (o is Map) return MapEquality(keys: this, values: this).hash(o); | ||
| 93 | + if (!_unordered) { | ||
| 94 | + if (o is List) return ListEquality(this).hash(o); | ||
| 95 | + if (o is Iterable) return IterableEquality(this).hash(o); | ||
| 96 | + } else if (o is Iterable) { | ||
| 97 | + return UnorderedIterableEquality(this).hash(o); | ||
| 98 | + } | ||
| 99 | + return _base.hash(o); | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + @override | ||
| 103 | + bool isValidKey(Object? o) => | ||
| 104 | + o is Iterable || o is Map || _base.isValidKey(o); | ||
| 105 | +} | ||
| 106 | + | ||
| 107 | +/// Equality on lists. | ||
| 108 | +/// | ||
| 109 | +/// Two lists are equal if they have the same length and their elements | ||
| 110 | +/// at each index are equal. | ||
| 111 | +class ListEquality<E> implements IEquality<List<E>> { | ||
| 112 | + final IEquality<E> _elementEquality; | ||
| 113 | + const ListEquality( | ||
| 114 | + [IEquality<E> elementEquality = const DefaultEquality<Never>()]) | ||
| 115 | + : _elementEquality = elementEquality; | ||
| 116 | + | ||
| 117 | + @override | ||
| 118 | + bool equals(List<E>? list1, List<E>? list2) { | ||
| 119 | + if (identical(list1, list2)) return true; | ||
| 120 | + if (list1 == null || list2 == null) return false; | ||
| 121 | + var length = list1.length; | ||
| 122 | + if (length != list2.length) return false; | ||
| 123 | + for (var i = 0; i < length; i++) { | ||
| 124 | + if (!_elementEquality.equals(list1[i], list2[i])) return false; | ||
| 125 | + } | ||
| 126 | + return true; | ||
| 127 | + } | ||
| 128 | + | ||
| 129 | + @override | ||
| 130 | + int hash(List<E>? list) { | ||
| 131 | + if (list == null) return null.hashCode; | ||
| 132 | + // Jenkins's one-at-a-time hash function. | ||
| 133 | + // This code is almost identical to the one in IterableEquality, except | ||
| 134 | + // that it uses indexing instead of iterating to get the elements. | ||
| 135 | + var hash = 0; | ||
| 136 | + for (var i = 0; i < list.length; i++) { | ||
| 137 | + var c = _elementEquality.hash(list[i]); | ||
| 138 | + hash = (hash + c) & _hashMask; | ||
| 139 | + hash = (hash + (hash << 10)) & _hashMask; | ||
| 140 | + hash ^= (hash >> 6); | ||
| 141 | + } | ||
| 142 | + hash = (hash + (hash << 3)) & _hashMask; | ||
| 143 | + hash ^= (hash >> 11); | ||
| 144 | + hash = (hash + (hash << 15)) & _hashMask; | ||
| 145 | + return hash; | ||
| 146 | + } | ||
| 147 | + | ||
| 148 | + @override | ||
| 149 | + bool isValidKey(Object? o) => o is List<E>; | ||
| 150 | +} | ||
| 151 | + | ||
| 152 | +/// Equality on maps. | ||
| 153 | +/// | ||
| 154 | +/// Two maps are equal if they have the same number of entries, and if the | ||
| 155 | +/// entries of the two maps are pairwise equal on both key and value. | ||
| 156 | +class MapEquality<K, V> implements IEquality<Map<K, V>> { | ||
| 157 | + final IEquality<K> _keyEquality; | ||
| 158 | + final IEquality<V> _valueEquality; | ||
| 159 | + const MapEquality( | ||
| 160 | + {IEquality<K> keys = const DefaultEquality<Never>(), | ||
| 161 | + IEquality<V> values = const DefaultEquality<Never>()}) | ||
| 162 | + : _keyEquality = keys, | ||
| 163 | + _valueEquality = values; | ||
| 164 | + | ||
| 165 | + @override | ||
| 166 | + bool equals(Map<K, V>? map1, Map<K, V>? map2) { | ||
| 167 | + if (identical(map1, map2)) return true; | ||
| 168 | + if (map1 == null || map2 == null) return false; | ||
| 169 | + var length = map1.length; | ||
| 170 | + if (length != map2.length) return false; | ||
| 171 | + Map<_MapEntry, int> equalElementCounts = HashMap(); | ||
| 172 | + for (var key in map1.keys) { | ||
| 173 | + var entry = _MapEntry(this, key, map1[key]); | ||
| 174 | + var count = equalElementCounts[entry] ?? 0; | ||
| 175 | + equalElementCounts[entry] = count + 1; | ||
| 176 | + } | ||
| 177 | + for (var key in map2.keys) { | ||
| 178 | + var entry = _MapEntry(this, key, map2[key]); | ||
| 179 | + var count = equalElementCounts[entry]; | ||
| 180 | + if (count == null || count == 0) return false; | ||
| 181 | + equalElementCounts[entry] = count - 1; | ||
| 182 | + } | ||
| 183 | + return true; | ||
| 184 | + } | ||
| 185 | + | ||
| 186 | + @override | ||
| 187 | + int hash(Map<K, V>? map) { | ||
| 188 | + if (map == null) return null.hashCode; | ||
| 189 | + var hash = 0; | ||
| 190 | + for (var key in map.keys) { | ||
| 191 | + var keyHash = _keyEquality.hash(key); | ||
| 192 | + var valueHash = _valueEquality.hash(map[key] as V); | ||
| 193 | + hash = (hash + 3 * keyHash + 7 * valueHash) & _hashMask; | ||
| 194 | + } | ||
| 195 | + hash = (hash + (hash << 3)) & _hashMask; | ||
| 196 | + hash ^= (hash >> 11); | ||
| 197 | + hash = (hash + (hash << 15)) & _hashMask; | ||
| 198 | + return hash; | ||
| 199 | + } | ||
| 200 | + | ||
| 201 | + @override | ||
| 202 | + bool isValidKey(Object? o) => o is Map<K, V>; | ||
| 203 | +} | ||
| 204 | + | ||
| 205 | +class _MapEntry { | ||
| 206 | + final MapEquality equality; | ||
| 207 | + final Object? key; | ||
| 208 | + final Object? value; | ||
| 209 | + _MapEntry(this.equality, this.key, this.value); | ||
| 210 | + | ||
| 211 | + @override | ||
| 212 | + int get hashCode => | ||
| 213 | + (3 * equality._keyEquality.hash(key) + | ||
| 214 | + 7 * equality._valueEquality.hash(value)) & | ||
| 215 | + _hashMask; | ||
| 216 | + | ||
| 217 | + @override | ||
| 218 | + bool operator ==(Object other) => | ||
| 219 | + other is _MapEntry && | ||
| 220 | + equality._keyEquality.equals(key, other.key) && | ||
| 221 | + equality._valueEquality.equals(value, other.value); | ||
| 222 | +} | ||
| 223 | + | ||
| 224 | +/// Equality on iterables. | ||
| 225 | +/// | ||
| 226 | +/// Two iterables are equal if they have the same elements in the same order. | ||
| 227 | +class IterableEquality<E> implements IEquality<Iterable<E>> { | ||
| 228 | + final IEquality<E?> _elementEquality; | ||
| 229 | + const IterableEquality( | ||
| 230 | + [IEquality<E> elementEquality = const DefaultEquality<Never>()]) | ||
| 231 | + : _elementEquality = elementEquality; | ||
| 232 | + | ||
| 233 | + @override | ||
| 234 | + bool equals(Iterable<E>? elements1, Iterable<E>? elements2) { | ||
| 235 | + if (identical(elements1, elements2)) return true; | ||
| 236 | + if (elements1 == null || elements2 == null) return false; | ||
| 237 | + var it1 = elements1.iterator; | ||
| 238 | + var it2 = elements2.iterator; | ||
| 239 | + while (true) { | ||
| 240 | + var hasNext = it1.moveNext(); | ||
| 241 | + if (hasNext != it2.moveNext()) return false; | ||
| 242 | + if (!hasNext) return true; | ||
| 243 | + if (!_elementEquality.equals(it1.current, it2.current)) return false; | ||
| 244 | + } | ||
| 245 | + } | ||
| 246 | + | ||
| 247 | + @override | ||
| 248 | + int hash(Iterable<E>? elements) { | ||
| 249 | + if (elements == null) return null.hashCode; | ||
| 250 | + // Jenkins's one-at-a-time hash function. | ||
| 251 | + var hash = 0; | ||
| 252 | + for (var element in elements) { | ||
| 253 | + var c = _elementEquality.hash(element); | ||
| 254 | + hash = (hash + c) & _hashMask; | ||
| 255 | + hash = (hash + (hash << 10)) & _hashMask; | ||
| 256 | + hash ^= (hash >> 6); | ||
| 257 | + } | ||
| 258 | + hash = (hash + (hash << 3)) & _hashMask; | ||
| 259 | + hash ^= (hash >> 11); | ||
| 260 | + hash = (hash + (hash << 15)) & _hashMask; | ||
| 261 | + return hash; | ||
| 262 | + } | ||
| 263 | + | ||
| 264 | + @override | ||
| 265 | + bool isValidKey(Object? o) => o is Iterable<E>; | ||
| 266 | +} | ||
| 267 | + | ||
| 268 | +/// Equality of sets. | ||
| 269 | +/// | ||
| 270 | +/// Two sets are considered equal if they have the same number of elements, | ||
| 271 | +/// and the elements of one set can be paired with the elements | ||
| 272 | +/// of the other set, so that each pair are equal. | ||
| 273 | +class SetEquality<E> extends _UnorderedEquality<E, Set<E>> { | ||
| 274 | + const SetEquality( | ||
| 275 | + [IEquality<E> elementEquality = const DefaultEquality<Never>()]) | ||
| 276 | + : super(elementEquality); | ||
| 277 | + | ||
| 278 | + @override | ||
| 279 | + bool isValidKey(Object? o) => o is Set<E>; | ||
| 280 | +} | ||
| 281 | + | ||
| 282 | +abstract class _UnorderedEquality<E, T extends Iterable<E>> | ||
| 283 | + implements IEquality<T> { | ||
| 284 | + final IEquality<E> _elementEquality; | ||
| 285 | + | ||
| 286 | + const _UnorderedEquality(this._elementEquality); | ||
| 287 | + | ||
| 288 | + @override | ||
| 289 | + bool equals(T? elements1, T? elements2) { | ||
| 290 | + if (identical(elements1, elements2)) return true; | ||
| 291 | + if (elements1 == null || elements2 == null) return false; | ||
| 292 | + var counts = HashMap<E, int>( | ||
| 293 | + equals: _elementEquality.equals, | ||
| 294 | + hashCode: _elementEquality.hash, | ||
| 295 | + isValidKey: _elementEquality.isValidKey); | ||
| 296 | + var length = 0; | ||
| 297 | + for (var e in elements1) { | ||
| 298 | + var count = counts[e] ?? 0; | ||
| 299 | + counts[e] = count + 1; | ||
| 300 | + length++; | ||
| 301 | + } | ||
| 302 | + for (var e in elements2) { | ||
| 303 | + var count = counts[e]; | ||
| 304 | + if (count == null || count == 0) return false; | ||
| 305 | + counts[e] = count - 1; | ||
| 306 | + length--; | ||
| 307 | + } | ||
| 308 | + return length == 0; | ||
| 309 | + } | ||
| 310 | + | ||
| 311 | + @override | ||
| 312 | + int hash(T? elements) { | ||
| 313 | + if (elements == null) return null.hashCode; | ||
| 314 | + var hash = 0; | ||
| 315 | + for (E element in elements) { | ||
| 316 | + var c = _elementEquality.hash(element); | ||
| 317 | + hash = (hash + c) & _hashMask; | ||
| 318 | + } | ||
| 319 | + hash = (hash + (hash << 3)) & _hashMask; | ||
| 320 | + hash ^= (hash >> 11); | ||
| 321 | + hash = (hash + (hash << 15)) & _hashMask; | ||
| 322 | + return hash; | ||
| 323 | + } | ||
| 324 | +} | ||
| 325 | + | ||
| 326 | +/// Equality of the elements of two iterables without considering order. | ||
| 327 | +/// | ||
| 328 | +/// Two iterables are considered equal if they have the same number of elements, | ||
| 329 | +/// and the elements of one set can be paired with the elements | ||
| 330 | +/// of the other iterable, so that each pair are equal. | ||
| 331 | +class UnorderedIterableEquality<E> extends _UnorderedEquality<E, Iterable<E>> { | ||
| 332 | + const UnorderedIterableEquality( | ||
| 333 | + [IEquality<E> elementEquality = const DefaultEquality<Never>()]) | ||
| 334 | + : super(elementEquality); | ||
| 335 | + | ||
| 336 | + @override | ||
| 337 | + bool isValidKey(Object? o) => o is Iterable<E>; | ||
| 338 | +} |
| @@ -29,12 +29,7 @@ class Api implements Service { | @@ -29,12 +29,7 @@ class Api implements Service { | ||
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | void main() { | 31 | void main() { |
| 32 | - test('Get.putAsync test', () async { | ||
| 33 | - await Get.putAsync<String>(Mock.test); | ||
| 34 | - expect('test', Get.find<String>()); | ||
| 35 | - Get.reset(); | ||
| 36 | - }); | ||
| 37 | - | 32 | + TestWidgetsFlutterBinding.ensureInitialized(); |
| 38 | test('Get.put test', () async { | 33 | test('Get.put test', () async { |
| 39 | final instance = Get.put<Controller>(Controller()); | 34 | final instance = Get.put<Controller>(Controller()); |
| 40 | expect(instance, Get.find<Controller>()); | 35 | expect(instance, Get.find<Controller>()); |
| @@ -4,7 +4,7 @@ import 'package:get/get.dart'; | @@ -4,7 +4,7 @@ import 'package:get/get.dart'; | ||
| 4 | 4 | ||
| 5 | void main() { | 5 | void main() { |
| 6 | test('Parse Page with children', () { | 6 | test('Parse Page with children', () { |
| 7 | - // final testParams = {'hi': 'value'}; | 7 | + final testParams = {'hi': 'value'}; |
| 8 | final pageTree = GetPage( | 8 | final pageTree = GetPage( |
| 9 | name: '/city', | 9 | name: '/city', |
| 10 | page: () => Container(), | 10 | page: () => Container(), |
| @@ -40,7 +40,7 @@ void main() { | @@ -40,7 +40,7 @@ void main() { | ||
| 40 | name: '/pen', | 40 | name: '/pen', |
| 41 | transition: Transition.cupertino, | 41 | transition: Transition.cupertino, |
| 42 | page: () => Container(), | 42 | page: () => Container(), |
| 43 | - // parameters: testParams, | 43 | + parameters: testParams, |
| 44 | ), | 44 | ), |
| 45 | GetPage( | 45 | GetPage( |
| 46 | name: '/paper', | 46 | name: '/paper', |
| @@ -58,63 +58,20 @@ void main() { | @@ -58,63 +58,20 @@ void main() { | ||
| 58 | ), | 58 | ), |
| 59 | ], | 59 | ], |
| 60 | ); | 60 | ); |
| 61 | - final routes = <GetPage>[]; | ||
| 62 | - final tree = ParseRouteTree(); | ||
| 63 | 61 | ||
| 64 | - routes.addRoute(pageTree); | 62 | + final tree = ParseRouteTree(routes: <GetPage>[]); |
| 63 | + | ||
| 64 | + tree.addRoute(pageTree); | ||
| 65 | 65 | ||
| 66 | // tree.addRoute(pageTree); | 66 | // tree.addRoute(pageTree); |
| 67 | final searchRoute = '/city/work/office/pen'; | 67 | final searchRoute = '/city/work/office/pen'; |
| 68 | - final match = tree.matchRoute(routes, searchRoute); | 68 | + final match = tree.matchRoute(searchRoute); |
| 69 | expect(match, isNotNull); | 69 | expect(match, isNotNull); |
| 70 | expect(match.route!.name, searchRoute); | 70 | expect(match.route!.name, searchRoute); |
| 71 | - final testRouteParam = match.parameters; | ||
| 72 | - print(testRouteParam); | ||
| 73 | - // for (final tParam in testParams.entries) { | ||
| 74 | - // expect(testRouteParam[tParam.key], tParam.value); | ||
| 75 | - // } | ||
| 76 | - }); | ||
| 77 | - | ||
| 78 | - test('Parse ', () { | ||
| 79 | - final testParams = {'hi': 'value'}; | ||
| 80 | - final pageTree = GetPage( | ||
| 81 | - name: '/city', | ||
| 82 | - parameters: testParams, | ||
| 83 | - page: () => Container(), | ||
| 84 | - ); | ||
| 85 | - final routes = <GetPage>[]; | ||
| 86 | - final tree = ParseRouteTree(); | ||
| 87 | - | ||
| 88 | - routes.addRoute(pageTree); | ||
| 89 | - | ||
| 90 | - // tree.addRoute(pageTree); | ||
| 91 | - final searchRoute = '/city?abc=1234'; | ||
| 92 | - | ||
| 93 | - final hasMatch = RouteParser.hasMatch( | ||
| 94 | - pushedRoute: searchRoute, routeName: pageTree.name); | ||
| 95 | - expect(hasMatch, true); | ||
| 96 | - | ||
| 97 | - final parsed = | ||
| 98 | - RouteParser.parse(pushedRoute: searchRoute, routeName: pageTree.name); | ||
| 99 | - | ||
| 100 | - final match = tree.matchRoute(routes, searchRoute); | ||
| 101 | - expect(match, isNotNull); | ||
| 102 | - expect(parsed.newRouteUri.toString(), searchRoute); | ||
| 103 | - final testRouteParam = match.route?.parameters; | ||
| 104 | - | 71 | + final testRouteParam = match.route!.parameters!; |
| 105 | for (final tParam in testParams.entries) { | 72 | for (final tParam in testParams.entries) { |
| 106 | - expect(testRouteParam![tParam.key], tParam.value); | 73 | + expect(testRouteParam[tParam.key], tParam.value); |
| 107 | } | 74 | } |
| 108 | - | ||
| 109 | - final hasMatch2 = RouteParser.hasMatch( | ||
| 110 | - pushedRoute: '/home/123/ana', routeName: '/home/:id/:name'); | ||
| 111 | - print(hasMatch2); | ||
| 112 | - expect(hasMatch2, true); | ||
| 113 | - | ||
| 114 | - final parsed2 = RouteParser.parse( | ||
| 115 | - pushedRoute: '/home/123/ana/profile', | ||
| 116 | - routeName: '/home/:id/:name/profile'); | ||
| 117 | - print(parsed2.parameters); | ||
| 118 | }); | 75 | }); |
| 119 | 76 | ||
| 120 | test('Parse Page without children', () { | 77 | test('Parse Page without children', () { |
| @@ -157,14 +114,14 @@ void main() { | @@ -157,14 +114,14 @@ void main() { | ||
| 157 | transition: Transition.rightToLeft), | 114 | transition: Transition.rightToLeft), |
| 158 | ]; | 115 | ]; |
| 159 | 116 | ||
| 160 | - final tree = ParseRouteTree(); | 117 | + final tree = ParseRouteTree(routes: pageTree); |
| 161 | 118 | ||
| 162 | // for (var p in pageTree) { | 119 | // for (var p in pageTree) { |
| 163 | // tree.addRoute(p); | 120 | // tree.addRoute(p); |
| 164 | // } | 121 | // } |
| 165 | 122 | ||
| 166 | final searchRoute = '/city/work/office/pen'; | 123 | final searchRoute = '/city/work/office/pen'; |
| 167 | - final match = tree.matchRoute(pageTree, searchRoute); | 124 | + final match = tree.matchRoute(searchRoute); |
| 168 | expect(match, isNotNull); | 125 | expect(match, isNotNull); |
| 169 | expect(match.route!.name, searchRoute); | 126 | expect(match.route!.name, searchRoute); |
| 170 | }); | 127 | }); |
| @@ -182,8 +139,6 @@ void main() { | @@ -182,8 +139,6 @@ void main() { | ||
| 182 | ], | 139 | ], |
| 183 | )); | 140 | )); |
| 184 | 141 | ||
| 185 | - print(Get.parameters); | ||
| 186 | - | ||
| 187 | expect(Get.parameters['name'], 'juan'); | 142 | expect(Get.parameters['name'], 'juan'); |
| 188 | 143 | ||
| 189 | Get.toNamed('/second/1234'); | 144 | Get.toNamed('/second/1234'); |
| @@ -3,8 +3,8 @@ import 'package:flutter_test/flutter_test.dart'; | @@ -3,8 +3,8 @@ import 'package:flutter_test/flutter_test.dart'; | ||
| 3 | import 'package:get/get.dart'; | 3 | import 'package:get/get.dart'; |
| 4 | 4 | ||
| 5 | void main() { | 5 | void main() { |
| 6 | - final controller = Get.put(Controller()); | ||
| 7 | testWidgets("GetxController smoke test", (tester) async { | 6 | testWidgets("GetxController smoke test", (tester) async { |
| 7 | + final controller = Get.put(Controller()); | ||
| 8 | await tester.pumpWidget( | 8 | await tester.pumpWidget( |
| 9 | MaterialApp( | 9 | MaterialApp( |
| 10 | home: Column( | 10 | home: Column( |
-
Please register or login to post a comment