[go_router_builder] Support custom types#11068
[go_router_builder] Support custom types#11068ValentinVignal wants to merge 7 commits intoflutter:mainfrom
Conversation
|
It needs #11067 to be merged first |
There was a problem hiding this comment.
Code Review
This pull request adds support for custom types as query parameters by allowing custom encoder, decoder, and compare functions in the TypedQueryParameter annotation. The changes are well-implemented across the builder logic, tests, and example code. I have a few suggestions to improve robustness in the example and enhance correctness and clarity in the builder logic.
|
|
||
| if (this is ConstructorElement) { | ||
| // The default constructor. | ||
| if (name == 'new') { |
There was a problem hiding this comment.
The check if (name == 'new') for an unnamed constructor seems incorrect. According to the analyzer package documentation, the name of an unnamed constructor is an empty string. Using 'new' might be unreliable and could fail if a class has a named constructor called new.
Please change this to check for an empty string to correctly identify the default constructor.
| if (name == 'new') { | |
| if (name.isEmpty) { |
| static CustomParameter? decode(String? value) { | ||
| if (value == null) { | ||
| return null; | ||
| } | ||
| final List<String> parts = value.split(','); | ||
| return CustomParameter( | ||
| valueString: parts[0], | ||
| valueInt: int.parse(parts[1]), | ||
| ); | ||
| } |
There was a problem hiding this comment.
The decode method is not robust against malformed input and could lead to runtime exceptions. For example, if the value string doesn't contain a comma, accessing parts[1] will throw a RangeError. If the second part is not a valid integer, int.parse will throw a FormatException.
Since this is an example file, it's a good opportunity to demonstrate best practices for parsing. Consider adding checks for the number of parts and using int.tryParse for safer integer conversion.
static CustomParameter? decode(String? value) {
if (value == null) {
return null;
}
final List<String> parts = value.split(',');
if (parts.length != 2) {
return null;
}
final int? valueInt = int.tryParse(parts[1]);
if (valueInt == null) {
return null;
}
return CustomParameter(
valueString: parts[0],
valueInt: valueInt,
);
}There was a problem hiding this comment.
This is true. But that's only an example. So I'd think the current code is okay. Let me know if you think otherwise
| if (annotatedDecoder != null || helper._matchesType(paramType)) { | ||
| var decoder = annotatedDecoder; | ||
| String decoded; | ||
| if (decoder != null) { | ||
| decoded = ''; | ||
| } else { |
There was a problem hiding this comment.
This part of the code seems to contain some redundant logic, likely from a refactoring.
- The condition
annotatedDecoder != nullon line 113 will always befalsebecause of the early return on lines 99-109 ifannotatedDecoderis notnull. - Consequently,
var decoder = annotatedDecoder;on line 114 will always assignnulltodecoder. - This makes the
if (decoder != null)block on line 116 dead code.
For clarity and maintainability, this logic could be simplified by removing the redundant checks and the dead code path.
| IntRoute({ | ||
| @TypedQueryParameter(name: 'intField') this.intField, | ||
| @TypedQueryParameter(name: 'int_field_with_default_value') | ||
| @TypedQueryParameter<int>(name: 'intField') this.intField, |
There was a problem hiding this comment.
seems like this may be a breaking change?
There was a problem hiding this comment.
I considered it a non-breaking change because existing usages of @TypedQueryParameter(name: 'my-name') will now be interpreted as @TypedQueryParameter<dynamic>S(name: 'my-name') which is okay. <T> only matters for compare, decode, encode.
But yes, ultimately, if the linter rules inference_failure_on_instance_creation is enabled, this will now create a lint that needs to be fixed.
What do you think? Should we flag it as a breaking change, and is it an acceptable one? Or is it okay to not flag it as a breaking change?
| if (decoder != null) { | ||
| decoded = ''; | ||
| } else { | ||
| final ElementAnnotation? annotation = metadata?.firstWhereOrNull(( |
There was a problem hiding this comment.
nit: use a for loop for readability
There was a problem hiding this comment.
Sure, I've changed it into a for loop in Use loop for readability
| String? decoder; | ||
| final String? annotatedDecoder = element.decoder; | ||
|
|
||
| if (annotatedDecoder != null) { |
There was a problem hiding this comment.
Is there a guard to ensure encode, decode, compare must all be provided or none should be provided
There was a problem hiding this comment.
I didn’t add a guard here because I don’t think it’s strictly necessary:
- If a custom type isn’t supported, the builder already fails and suggests adding
encode/decode. compareonly matters when there’s a default value on a non‑supported type.- Users can annotate supported types (e.g.
String) to override encode while still relying on the default decode fromgo_router_builder. I’m not sure what the practical use case for that would be, but it’s technically valid.
So there are scenarios where encode, decode, and compare don’t all need to be provided together. Given that, do you still want me to enforce a guard requiring encode and decode to always be paired?
Closes flutter/flutter#112152
Closes flutter/flutter#110781
Pre-Review Checklist
[shared_preferences]pubspec.yamlwith an appropriate new version according to the pub versioning philosophy, or I have commented below to indicate which version change exemption this PR falls under1.CHANGELOG.mdto add a description of the change, following repository CHANGELOG style, or I have commented below to indicate which CHANGELOG exemption this PR falls under1.///).If you need help, consider asking for advice on the #hackers-new channel on Discord.
Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the
gemini-code-assistbot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.Footnotes
Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. ↩ ↩2 ↩3