Add WidgetStatesController support to ExpansionTile#181238
Conversation
There was a problem hiding this comment.
Code Review
This pull request adds WidgetStatesController support to ExpansionTile, which is a valuable enhancement for observing and controlling its interactive states. The implementation is straightforward and includes appropriate tests. My main feedback is on the example code provided in the documentation, which should be updated to correctly manage the lifecycle of the WidgetStatesController to prevent memory leaks and follow best practices.
| /// class ExpansionTileStatesExample extends StatelessWidget { | ||
| /// ExpansionTileStatesExample({super.key}); | ||
| /// | ||
| /// final WidgetStatesController controller = WidgetStatesController(); | ||
| /// | ||
| /// @override | ||
| /// Widget build(BuildContext context) { | ||
| /// controller.addListener(() { | ||
| /// if (controller.value.contains(WidgetState.hovered)) { | ||
| /// debugPrint('Tile is hovered'); | ||
| /// } | ||
| /// }); | ||
| /// | ||
| /// return MaterialApp( | ||
| /// home: Scaffold( | ||
| /// body: ExpansionTile( | ||
| /// title: const Text('Settings'), | ||
| /// statesController: controller, | ||
| /// ), | ||
| /// ), | ||
| /// ); | ||
| /// } | ||
| /// } |
There was a problem hiding this comment.
The example ExpansionTileStatesExample should be a StatefulWidget to properly manage the lifecycle of the WidgetStatesController. WidgetStatesController is a ChangeNotifier and needs to be disposed to avoid memory leaks. Currently, it's created in a StatelessWidget, which doesn't have a dispose method.
Additionally, adding a listener inside the build method can lead to adding the same listener multiple times, as build can be called repeatedly. The listener should be added in initState.
/// class ExpansionTileStatesExample extends StatefulWidget {
/// const ExpansionTileStatesExample({super.key});
///
/// @override
/// State<ExpansionTileStatesExample> createState() => _ExpansionTileStatesExampleState();
/// }
///
/// class _ExpansionTileStatesExampleState extends State<ExpansionTileStatesExample> {
/// late final WidgetStatesController _controller;
///
/// @override
/// void initState() {
/// super.initState();
/// _controller = WidgetStatesController();
/// _controller.addListener(() {
/// if (_controller.value.contains(WidgetState.hovered)) {
/// debugPrint('Tile is hovered');
/// }
/// });
/// }
///
/// @override
/// void dispose() {
/// _controller.dispose();
/// super.dispose();
/// }
///
/// @override
/// Widget build(BuildContext context) {
/// return MaterialApp(
/// home: Scaffold(
/// body: ExpansionTile(
/// title: const Text('Settings'),
/// statesController: _controller,
/// ),
/// ),
/// );
/// }
/// }There was a problem hiding this comment.
@iamvikashtiwari This looks like a real problem that should be addressed
There was a problem hiding this comment.
Oh I missed this. For code snippet, maybe we should make it shorter? I didn't see a whole class example in code snippet previously. Maybe check the other use cases as examples.
There was a problem hiding this comment.
It looks like other WidgetStatesController fields don't provide a code example. However, it seems useful to have at least one example of this pattern.
I'd consider:
- Move this code example to be a full sample:
examples/api/lib/widgets/widget_state/widget_states_controller.0.dart - Add unit tests for this sample
- Add this code example to
WidgetStatesController's docs directly using a{@tool dartpad}(instead of a{@tool snippet}).
There was a problem hiding this comment.
Gentle ping just in case this comment is missed:)!
QuncCccccc
left a comment
There was a problem hiding this comment.
We might need to fix the failed tests and also the comments from the closed PR:) Thanks!
| ); | ||
| }); | ||
|
|
||
| testWidgets('ExpansionTile forwards statesController to ListTile', (tester) async { |
There was a problem hiding this comment.
Run dart format packages/flutter/test/material/expansion_tile_test.dart to fix linux analyzer.
There was a problem hiding this comment.
Thanks for pointing this out!
I’ve fixed the formatting issue in
packages/flutter/test/material/expansion_tile_test.dart
by running dart format and pushed the update.
| }); | ||
|
|
||
| testWidgets('ExpansionTile forwards statesController to ListTile', (tester) async { | ||
| final controller = WidgetStatesController(); |
There was a problem hiding this comment.
We need to dispose controller when we finish testing. Adding addTearDown(controller.dispose); after this line should fix the failed "Windows framework_tests_libraries_leak_tracking" test.
There was a problem hiding this comment.
Thanks for the suggestion!
Added addTearDown(controller.dispose); to properly dispose the
WidgetStatesController in the test. This should resolve the
Windows leak tracking failure.
| expect(listTile.statesController, controller); | ||
| }); | ||
|
|
||
| testWidgets('ExpansionTile forwards statesController to ListTile', (tester) async { |
There was a problem hiding this comment.
We have 2 identical "ExpansionTile forwards statesController to ListTile" tests now. I think originally this is "ExpansionTile forwards null statesController to ListTile".
| }); | ||
|
|
||
| testWidgets('ExpansionTile forwards statesController to ListTile', (tester) async { | ||
| final controller = WidgetStatesController(); |
There was a problem hiding this comment.
| final controller = WidgetStatesController(); | |
| final controller = WidgetStatesController(); | |
| addTearDown(controller.dispose); |
| // the default value to true. | ||
| final bool internalAddSemanticForOnTap; | ||
|
|
||
| /// {@tool snippet} |
There was a problem hiding this comment.
We should add a summary sentence so that IDEs like VS Code show useful code completion information if you type myExpansionTile.statesController. Consider something like:
| /// {@tool snippet} | |
| /// The controller that notifies when the widget's [WidgetState]s change. | |
| /// | |
| /// {@tool snippet} |
|
Gentle ping for this comment here. If you think this is too much, I'm okay with removing the code snippet completely as well since I think this example looks simple. |
|
Thanks! Since the usage is straightforward, I’ve removed the snippet to keep the documentation concise. |
justinmc
left a comment
There was a problem hiding this comment.
LGTM 👍 . Always good to have more WidgetStatesController support.
|
Thanks everyone for the reviews and guidance! |
|
autosubmit label was removed for flutter/flutter/181238, because The base commit of the PR is older than 7 days and can not be merged. Please merge the latest changes from the main into this branch and resubmit the PR. |
bbffc1f to
0824901
Compare
This PR adds support for providing a `WidgetStatesController` to `ExpansionTile` and forwards it to the backing `ListTile`. This allows callers to observe and control hovered, pressed, and focused states of the tile header. The change includes updated documentation with a snippet example and adds test coverage for both provided and null `statesController` cases. Fixes flutter#180161 ## Pre-launch Checklist - [x] I read the Contributor Guide and followed the process outlined there for submitting PRs. - [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. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making. - [ ] I followed the breaking change policy and added Data Driven Fixes where supported. - [ ] All existing and new tests are passing.
This PR adds support for providing a `WidgetStatesController` to `ExpansionTile` and forwards it to the backing `ListTile`. This allows callers to observe and control hovered, pressed, and focused states of the tile header. The change includes updated documentation with a snippet example and adds test coverage for both provided and null `statesController` cases. Fixes flutter#180161 ## Pre-launch Checklist - [x] I read the Contributor Guide and followed the process outlined there for submitting PRs. - [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. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making. - [ ] I followed the breaking change policy and added Data Driven Fixes where supported. - [ ] All existing and new tests are passing.
This PR adds support for providing a `WidgetStatesController` to `ExpansionTile` and forwards it to the backing `ListTile`. This allows callers to observe and control hovered, pressed, and focused states of the tile header. The change includes updated documentation with a snippet example and adds test coverage for both provided and null `statesController` cases. Fixes flutter#180161 ## Pre-launch Checklist - [x] I read the Contributor Guide and followed the process outlined there for submitting PRs. - [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. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making. - [ ] I followed the breaking change policy and added Data Driven Fixes where supported. - [ ] All existing and new tests are passing.
This PR adds support for providing a
WidgetStatesControllertoExpansionTileand forwards it to the backing
ListTile. This allows callers to observe andcontrol hovered, pressed, and focused states of the tile header.
The change includes updated documentation with a snippet example and adds
test coverage for both provided and null
statesControllercases.Fixes #180161
Pre-launch Checklist
///).