Line data Source code
1 : import 'package:code_builder/code_builder.dart';
2 : import 'package:collection/collection.dart';
3 : import 'package:flutter_navigation_generator/src/models/importable_type.dart';
4 : import 'package:flutter_navigation_generator/src/models/route_config.dart';
5 : import 'package:flutter_navigation_generator/src/utils/case_utils.dart';
6 : import 'package:flutter_navigation_generator/src/utils/importable_type_to_string.dart';
7 : import 'package:flutter_navigation_generator/src/utils/route_config_extension.dart';
8 : import 'package:flutter_navigation_generator/src/utils/utils.dart';
9 : import 'package:flutter_navigation_generator_annotations/flutter_navigation_generator_annotations.dart';
10 :
11 : class OnGenerateRouteBuilder {
12 : final Set<RouteConfig> routes;
13 : final ImportableType? pageType;
14 : final ImportableType? unknownRoute;
15 : final List<ImportableType> defaultGuards;
16 : final Uri? targetFile;
17 :
18 1 : OnGenerateRouteBuilder({
19 : required this.routes,
20 : this.pageType,
21 : this.unknownRoute,
22 : this.targetFile,
23 : this.defaultGuards = const [],
24 : });
25 :
26 1 : String _withPageType(RouteConfig? route, String screen) {
27 1 : final pageClass = route?.pageType != null
28 3 : ? typeRefer(route!.pageType).symbol
29 1 : : pageType != null
30 3 : ? typeRefer(pageType).symbol
31 : : 'MaterialPageRoute';
32 6 : return '$pageClass<${typeRefer(route?.returnType).symbol}>(builder: (_) => $screen, settings: settings, fullscreenDialog: ${route?.isFullscreenDialog == true},)';
33 : }
34 :
35 1 : String _generateRoute(RouteConfig route) {
36 4 : final constructor = route.constructorName == route.routeWidget.className ||
37 2 : route.constructorName.isEmpty
38 2 : ? route.routeWidget.className
39 4 : : '${route.routeWidget.className}.${route.constructorName}';
40 : final constructorCall =
41 4 : '$constructor(${route.parameters.asMap().map((_, p) {
42 1 : final nullableSuffix = p.isNullable ? '?' : '';
43 : final convertFromString =
44 1 : ImportableTypeStringConverter.convertFromString(
45 2 : p, 'arguments[\'${p.argumentName}\']');
46 1 : return MapEntry(
47 1 : p.argumentName,
48 4 : (p.className == 'Key' || p.className == 'String')
49 0 : ? "arguments['${p.argumentName}'] as ${p.className}$nullableSuffix"
50 6 : : "arguments['${p.argumentName}'] is String ? $convertFromString : arguments['${p.argumentName}'] as ${p.className}${p.typeArguments.isNotEmpty ? '<${p.typeArguments.map((e) => e.className).join(',')}>' : ''}$nullableSuffix",
51 : );
52 8 : }).entries.map((e) => '${e.key}: ${e.value},').join('')})';
53 :
54 : var guardsCode = '';
55 3 : if ((route.guards ?? defaultGuards).isNotEmpty) {
56 4 : for (final guard in route.guards ?? defaultGuards) {
57 3 : final insanceName = CaseUtil(guard.className).camelCase;
58 1 : guardsCode +=
59 3 : 'final $insanceName = guards.whereType<${typeRefer(guard).symbol}>().first;\n';
60 2 : guardsCode += 'if (!$insanceName.value) {\n';
61 1 : guardsCode += ' guardedRouteSettings = settings;\n';
62 1 : guardsCode += ' return onGenerateRoute(RouteSettings(\n';
63 1 : guardsCode += ' arguments: settings.arguments,\n';
64 2 : guardsCode += ' name: $insanceName.alternativeRoute,\n';
65 1 : guardsCode += ' ));\n';
66 1 : guardsCode += '}\n';
67 : }
68 : }
69 2 : return '${guardsCode}return ${_withPageType(route, constructorCall)};';
70 : }
71 :
72 1 : Method generate() {
73 1 : final pageRoutes = routes
74 2 : .where((r) =>
75 1 : r.generatePageRoute &&
76 2 : r.navigationType != NavigationType.bottomSheet &&
77 2 : r.navigationType != NavigationType.dialog)
78 1 : .toList();
79 2 : for (final pageRoute in pageRoutes.toList()) {
80 : final pageRoute2 = pageRoutes
81 5 : .firstWhere((element) => element.routeName == pageRoute.routeName);
82 1 : if (pageRoute != pageRoute2) {
83 0 : pageRoutes.remove(pageRoute);
84 : }
85 : }
86 1 : return Method(
87 1 : (m) => m
88 1 : ..name = 'onGenerateRoute'
89 1 : ..returns = const Reference('Route<dynamic>?')
90 2 : ..requiredParameters.add(
91 1 : Parameter(
92 1 : (p) => p
93 1 : ..name = 'settings'
94 1 : ..type = const Reference('RouteSettings'),
95 : ),
96 : )
97 3 : ..body = Block.of([
98 2 : if (pageRoutes.isNotEmpty) ...[
99 : const Code(
100 : '''final arguments = settings.arguments is Map ? (settings.arguments as Map).cast<String, dynamic>() : <String, dynamic>{};
101 : final settingsUri = Uri.parse(settings.name ?? '');
102 : settingsUri.queryParameters.forEach((key, value) {
103 : arguments[key] ??= value;
104 : });'''),
105 1 : Code(
106 10 : 'switch (settingsUri.path) {${pageRoutes.where((route) => !route.routeNameContainsParameters).map((route) => 'case RouteNames.${route.asRouteName}: ${_generateRoute(route)}').join('')}}'),
107 : ],
108 : if (pageRoutes
109 4 : .any((element) => element.routeNameContainsParameters)) ...[
110 : const Code('final pathSegments = settingsUri.pathSegments;'),
111 : ...pageRoutes
112 3 : .where((pageRoute) => pageRoute.routeNameContainsParameters)
113 1 : .groupListsBy(
114 4 : (pageRoute) => pageRoute.routeName.pathSegments.length)
115 1 : .entries
116 1 : .sorted((a, b) => -a.key.compareTo(b.key))
117 2 : .map((group) {
118 1 : final pathSegments = group.key;
119 1 : var code = 'if (pathSegments.length == $pathSegments) {';
120 4 : final pageRoutesMap = group.value.asMap().map((key, value) =>
121 1 : MapEntry(
122 3 : value, value.routeName.parametersFromRouteName.length));
123 1 : final pageRoutes = pageRoutesMap.entries
124 1 : .sorted((a, b) => a.value.compareTo(b.value))
125 3 : .map((e) => e.key)
126 1 : .toList();
127 2 : for (final pageRoute in pageRoutes) {
128 2 : final pathSegments = pageRoute.routeName.pathSegments;
129 : final hasRigidSegments =
130 3 : pathSegments.any((element) => !element.startsWith(':'));
131 : if (hasRigidSegments) {
132 1 : code += 'if (';
133 1 : code += pathSegments
134 1 : .asMap()
135 1 : .entries
136 1 : .where(
137 3 : (pathSegment) => !pathSegment.value.startsWith(':'))
138 2 : .map((pathSegment) =>
139 3 : 'pathSegments[${pathSegment.key}] == \'${pathSegment.value}\'')
140 1 : .join(' && ');
141 1 : code += ') {';
142 : }
143 1 : code += pathSegments
144 1 : .asMap()
145 1 : .entries
146 4 : .where((pathSegment) => pathSegment.value.startsWith(':'))
147 2 : .map((pathSegment) =>
148 5 : 'arguments[\'${pathSegments[pathSegment.key].substring(1)}\'] = pathSegments[${pathSegment.key}];')
149 1 : .join('\n');
150 2 : code += _generateRoute(pageRoute);
151 1 : if (hasRigidSegments) code += '}';
152 : }
153 :
154 2 : return Code('$code}');
155 : }),
156 : ],
157 2 : if (unknownRoute != null) ...[
158 1 : Code(
159 6 : 'return ${_withPageType(null, '${typeRefer(unknownRoute!).symbol!}()')};'),
160 1 : ] else ...[
161 : const Code('return null;'),
162 : ],
163 : ]),
164 : );
165 : }
166 : }
|