Repository navigation
Fix some type checks in the Actions widget - #180883
Conversation
There was a problem hiding this comment.
Code Review
This pull request provides a solid fix for a subtle type-checking issue within the Actions widget, stemming from Dart's current limitations with contravariant generic types. The approach of widening types to Action<Intent> and then performing safe runtime casts with detailed assertions is robust and developer-friendly. The new _debugCanHandleIntent method is an excellent addition for catching type mismatches during development. The test suite has also been appropriately updated with a valuable regression test. Overall, this is a well-executed and important fix.
| T() => null, | ||
| // The List literal is needed to reify the type I | ||
| // ignore: literal_only_boolean_expressions | ||
| null when <I>[] is List<T> => null, |
There was a problem hiding this comment.
Dang. I would've never thought of this in a million years. Very nice trick 👍
|
Added a test and reverted the public interface change. I originally planned to add this to /// ## Limitations:
///
/// The return type `Action<T>?` may prevent this method from returning
/// perfectly capable `Action`s bound to the given [Intent]. For instance, an
/// `Action<Intent>` can be bound to any `Intent` because its type signature
/// claims it can take any [Intent] object. However this method will not be
/// able to return an `Action<Intent>` unless `T` is exactly `Intent`, because
/// `Action<Intent>` is not a subtype of `Action<T>?`. The method will return
/// null in this case, and assert in debug mode.
///
/// It is recommanded to use `maybeInvoke` instead if , or explicitly set the type
/// parameter `T` to `Intent`, for example:
/// `final action = Actions.maybeFind<Intent>(context, intent);`But neither alternative suggested above replaces
So I'm going to only give that suggestion when people actually run into the problem (as an assert). |
SGTM, thanks for considering this! 👍 |
|
@LongCatIsLooong FYI there are test failures (from triage). |
|
This pull request executed golden file tests, but it has not been updated in a while (20+ days). Test results from Gold expire after as many days, so this pull request will need to be updated with a fresh commit in order to get results from Gold. For more guidance, visit Writing a golden file test for Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. |
|
Hey @LongCatIsLooong is this still something you would like to land? |
|
Added a limitation section to |
| /// If no [Actions] widget surrounds the given context, this function will | ||
| /// assert in debug mode, and throw an exception in release mode. | ||
| /// | ||
| /// {@macro flutter.widgets.actions.findLimitations} |
There was a problem hiding this comment.
I feel a bit iffy about reusing the exact same language here given that find cannot return null, but the limitations section assumes a nullable return type.
| /// return null. | ||
| /// | ||
| /// {@template flutter.widgets.actions.findLimitations} | ||
| /// ## Limitations: |
There was a problem hiding this comment.
What do you think of something like this?
/// ## Limitations
///
/// It is strongly recommended that callers explicitly set the type parameter to
/// `Intent` when the `intent` parameter is not null:
///
/// ```dart
/// Actions.maybeFind<Intent>(context, intent); // GOOD
/// Actions.maybeFind(context, intent); // BAD
/// ```
///
/// If the type parameter is not set to `Intent` when the `intent` parameter is
/// not null, this method might be unable to return a perfectly capable `Action`.
/// For instance, this method cannot return an `Action<Intent>` - an action
/// that can be bound to any intent - unless `T` is exactly `Intent`.
/// This will trigger assertions in debug mode.Summary of changes:
- I moved the recommendation before the explanation. This way you get the actionable advice immediately, and then you can keep reading if you want to learn why this is necessary. Or you can skip the explanation entirely!
- I added a
// BADexample to make it clearer what the recommendation is. I suspect folks might get confused by theIntenttype parameter vs theintentparameter. - I tried to avoid "return null" so that this template also works for
Actions.find. FYI, I expect that in release mode,Actions.findwill throw an exception due to its use of a bang operator on anullvalue.
LMKWYT!
loic-sharma
left a comment
There was a problem hiding this comment.
The code changes look good to me, but I left a suggestion on the docs. Let me know what you think!
| return callingAction!.invoke(intent); | ||
| } | ||
| return invokeAction(intent, context); | ||
| return callingAction?.invoke(intent) ?? invokeAction(intent, context); |
There was a problem hiding this comment.
Well this is a bad change because invoke can return null.
…191052) `Actions.handler` was calling `Actions.maybeFind(context)` without an explicit type argument or the intent, so downward inference resolved `T` to `Intent` and the action lookup used the literal `Intent` type instead of the intent's concrete runtime type. Since apps register actions keyed by their concrete `Intent` subtype, the lookup always missed and `handler` returned `null` for every intent. This regressed in flutter#180883 and first shipped in 3.47.0. The fix forwards the type and intent explicitly: `Actions.maybeFind<T>(context, intent: intent)`. ## Tests Adds regression tests for `Actions.handler`: it now finds and invokes the bound action for the intent's runtime type, and still returns null when the action is disabled or when no matching action is found. The first test fails without the fix. Fixes flutter#191045. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [AI contribution guidelines]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md Co-authored-by: Victor Sanni <[email protected]>
…191052) `Actions.handler` was calling `Actions.maybeFind(context)` without an explicit type argument or the intent, so downward inference resolved `T` to `Intent` and the action lookup used the literal `Intent` type instead of the intent's concrete runtime type. Since apps register actions keyed by their concrete `Intent` subtype, the lookup always missed and `handler` returned `null` for every intent. This regressed in flutter#180883 and first shipped in 3.47.0. The fix forwards the type and intent explicitly: `Actions.maybeFind<T>(context, intent: intent)`. ## Tests Adds regression tests for `Actions.handler`: it now finds and invokes the bound action for the intent's runtime type, and still returns null when the action is disabled or when no matching action is found. The first test fails without the fix. Fixes flutter#191045. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [AI contribution guidelines]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md Co-authored-by: Victor Sanni <[email protected]>
Fixes #180435
Related #180871,
The type parameter
TinAction<T extends Intent>is contravariant which dart doesnt support yet, so unfortunately compile time type check is not enough, also the assert in_castActionis incorrect.Pre-launch Checklist
///).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.