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