-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Converted GuessInput to a StatefulWidget in Stateful widgets tutorial #13653
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
MuthuGCodes
wants to merge
9
commits into
flutter:main
Choose a base branch
from
MuthuGCodes:mg-GuessInput-issue
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+175
−9
Open
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
dc66eea
Converted GuessInput to a StatefulWidget in the Stateful widgets tuto…
MuthuGCodes 7306467
Merge branch 'main' into mg-GuessInput-issue
MuthuGCodes 9269a89
Merge branch 'main' into mg-GuessInput-issue
MuthuGCodes 00ccd14
Remove workspace.code-workspace
MuthuGCodes 9e52d59
Use code-excerpt tag for GuessInput StatefulWidget snippet in tutorial
MuthuGCodes 4ea885b
Merge branch 'main' into mg-GuessInput-issue
MuthuGCodes bdd3399
Merge branch 'main' into mg-GuessInput-issue
MuthuGCodes 329026a
Merge branch 'main' into mg-GuessInput-issue
MuthuGCodes 8150ada
Merge branch 'main' into mg-GuessInput-issue
MuthuGCodes File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -212,6 +212,97 @@ needs to repaint the screen, and the user wouldn't see any updates. | |
|
|
||
| [`setState`]: {{site.api}}/flutter/widgets/State/setState.html | ||
|
|
||
| ### Convert `GuessInput` to a stateful widget | ||
|
|
||
| When `GamePage` rebuilds after calling `setState`, | ||
| all of its child widgets are rebuilt as well. | ||
| Because `GuessInput` was originally created as a `StatelessWidget`, | ||
| every rebuild creates a new `GuessInput` instance, | ||
| along with a new `TextEditingController` and `FocusNode`. | ||
| This causes the text input field to lose focus after submitting a guess | ||
| and leaves unused controllers without proper disposal. | ||
|
|
||
| To keep focus on the text field between guesses and | ||
| manage controller lifecycles properly, | ||
| convert `GuessInput` into a `StatefulWidget`: | ||
|
|
||
| 1. Change `GuessInput` to extend `StatefulWidget` instead of `StatelessWidget`. | ||
| 1. Create a companion `_GuessInputState` class extending `State<GuessInput>`. | ||
| 1. Move `_textEditingController`, `_focusNode`, `_onSubmit()`, and `build()` | ||
| into `_GuessInputState`. | ||
| 1. Implement `dispose()` to clean up `_textEditingController` and `_focusNode`. | ||
|
|
||
| Your modified `GuessInput` widget should look like this: | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For code excerpts in the tutorial we follow these steps:
Following this flow will ensure the code snippets in our .MD files are always up to date with what is in the /examples dir. |
||
| <?code-excerpt "fwe/birdle/lib/step5_main.dart (GuessInput)"?> | ||
| ```dart | ||
| class GuessInput extends StatefulWidget { | ||
| const GuessInput({super.key, required this.onSubmitGuess}); | ||
|
|
||
| final void Function(String) onSubmitGuess; | ||
|
|
||
| @override | ||
| State<GuessInput> createState() => _GuessInputState(); | ||
| } | ||
|
|
||
| class _GuessInputState extends State<GuessInput> { | ||
| final TextEditingController _textEditingController = TextEditingController(); | ||
| final FocusNode _focusNode = FocusNode(); | ||
|
|
||
| @override | ||
| void dispose() { | ||
| _textEditingController.dispose(); | ||
| _focusNode.dispose(); | ||
| super.dispose(); | ||
| } | ||
|
|
||
| void _onSubmit() { | ||
| widget.onSubmitGuess(_textEditingController.text.trim()); | ||
| _textEditingController.clear(); | ||
| _focusNode.requestFocus(); | ||
| } | ||
|
|
||
| @override | ||
| Widget build(BuildContext context) { | ||
| return Row( | ||
| mainAxisAlignment: MainAxisAlignment.center, | ||
| children: [ | ||
| SizedBox( | ||
| width: 250, | ||
| child: Padding( | ||
| padding: const EdgeInsets.all(8.0), | ||
| child: TextField( | ||
| maxLength: 5, | ||
| decoration: const InputDecoration( | ||
| border: OutlineInputBorder( | ||
| borderRadius: BorderRadius.all(Radius.circular(35)), | ||
| ), | ||
| ), | ||
| controller: _textEditingController, | ||
| autofocus: true, | ||
| focusNode: _focusNode, | ||
| onSubmitted: (input) { | ||
| _onSubmit(); | ||
| }, | ||
| ), | ||
| ), | ||
| ), | ||
| IconButton( | ||
| padding: EdgeInsets.zero, | ||
| icon: const Icon(Icons.arrow_circle_up), | ||
| onPressed: _onSubmit, | ||
| ), | ||
| ], | ||
| ); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| By converting `GuessInput` to a `StatefulWidget`, | ||
| `_GuessInputState` persists across parent rebuilds, | ||
| keeping focus on the text field after each guess is submitted | ||
| and properly disposing of resources when the widget is unmounted. | ||
|
|
||
| ### Review | ||
|
|
||
| <SummaryCard> | ||
|
|
@@ -225,20 +316,20 @@ items: | |
| When a widget's appearance or data needs to change during its lifetime, | ||
| you need a `StatefulWidget`. The widget itself stays immutable, but | ||
| its companion `State` object holds mutable data and triggers rebuilds. | ||
| - title: Converted GamePage to a StatefulWidget | ||
| - title: Converted GamePage and GuessInput to StatefulWidgets | ||
| icon: swap_horiz | ||
| details: >- | ||
| You refactored `GamePage` to be stateful by | ||
| creating a companion `_GamePageState` class, moving the | ||
| `build` method and mutable properties to it, and | ||
| You refactored `GamePage` and `GuessInput` to be stateful by | ||
| creating companion `State` classes, moving mutable properties and | ||
| lifecycle management (like `dispose`) to them, and | ||
| implementing `createState()`. | ||
| Your IDE's support for quick assists can automate this conversion. | ||
| - title: Made your app respond to user input with setState | ||
| icon: refresh | ||
| details: >- | ||
| Calling `setState` tells Flutter to rebuild the UI of a widget. | ||
| When a user submits a guess, you call `setState` to update the game state, | ||
| and the grid automatically reflects the new data. | ||
| and the grid automatically reflects the new data while maintaining text field focus. | ||
| Your app is now truly interactive! | ||
| </SummaryCard> | ||
|
|
||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The explanation states that a new
GuessInputinstance is created on every rebuild because it was aStatelessWidget. However, in Flutter, widget instances are immutable and are recreated on almost every rebuild regardless of whether they are stateful or stateless.The actual issue is that the
TextEditingControllerandFocusNodewere declared as fields of theGuessInputwidget class itself. Because the widget is recreated on every parent rebuild, these controllers were also re-instantiated, causing the loss of focus/state and memory leaks.By converting to a
StatefulWidget, these controllers are moved to the persistentStateobject (_GuessInputState), which survives widget recreation.Consider clarifying this distinction to help learners better understand Flutter's widget vs. state lifecycle.