Reputation: 1
Problem: I'm trying to link the value of a dropdown to a text field in Flutter. I'm using Provider
for state management. The value from the dropdown updates the state correctly, but the text field is not reflecting the changes.
Steps to Recreate: Below is a simple code that reproduces the issue:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ErrorRecreationScreen extends StatefulWidget {
const ErrorRecreationScreen({super.key});
@override
State<ErrorRecreationScreen> createState() => _ErrorRecreationScreenState();
}
class _ErrorRecreationScreenState extends State<ErrorRecreationScreen> {
@override
Widget build(BuildContext context) {
return Consumer<AppState>(builder: (context, appState, child){
return Scaffold(
appBar: AppBar(title: const Text("Error Recreation Screen"),),
body: Column(
children: [
const Section1(),
const SizedBox(height: 10,),
const Card(child: Section2()),
Text("val=${appState.val}"),
],
),
);
});
}
}
class Section1 extends StatefulWidget {
const Section1({super.key});
@override
State<Section1> createState() => _Section1State();
}
class _Section1State extends State<Section1> {
List<String> items = ["Item 1", "Item 2", "Item 3", "Item 4"];
@override
Widget build(BuildContext context) {
return Consumer<AppState>(builder: (context, appState, child){
return Column(
children: [
Center(
child: DropdownButton<String>(
// The value that is currently selected
value: items.contains(appState.val)? appState.val : null,
hint: const Text("Select an item"),
// Dropdown items generated from the list
items: items.map((String item) {
return DropdownMenuItem<String>(
value: item,
child: Text(item),
);
}).toList(),
// When an item is selected
onChanged: (String? newValue) {
appState.setVal(newValue);
},
),
),
],
);
});
}
}
class Section2 extends StatefulWidget {
const Section2({super.key});
@override
State<Section2> createState() => _Section2State();
}
class _Section2State extends State<Section2> {
@override
Widget build(BuildContext context) {
return Consumer<AppState>(builder: (context, appState, child){
return Column(
children: [
MyWidget(str: appState.val, onChange: appState.setVal)
],
);
});
}
}
class MyWidget extends StatefulWidget {
final String? str;
final void Function(String?) onChange;
const MyWidget({super.key, required this.str, required this.onChange});
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late TextEditingController textEditingController;
@override void initState() {
super.initState();
textEditingController = TextEditingController(text: widget.str);
}
@override
Widget build(BuildContext context) {
print("#[build-MyWidget-call] val=${widget.str}");
return TextFormField(
controller: textEditingController,
onChanged: widget.onChange,
);
}
}
class AppState extends ChangeNotifier{
String val = "";
void setVal(String? val){
if(val == null) return;
this.val = val;
notifyListeners();
}
}
Images of Error:
Initial Problem: When I select a value from the dropdown, it updates appState.val
as expected, but the text field does not reflect this value.
I realized that initState()
only runs once. So, I used didUpdateWidget()
to update the TextEditingController
when the dropdown value changes.
Updated Code Using didUpdateWidget()
:
class _MyWidgetState extends State<MyWidget> {
late TextEditingController textEditingController;
@override void initState() {
super.initState();
textEditingController = TextEditingController(text: widget.str);
}
@override void didUpdateWidget(covariant MyWidget oldWidget) {
super.didUpdateWidget(oldWidget);
textEditingController.text = widget.str ?? "";
}
@override
Widget build(BuildContext context) {
print("#[build-MyWidget-call] val=${widget.str}");
return TextFormField(
controller: textEditingController,
onChanged: widget.onChange,
);
}
}
New Problem: This solution fixes the issue of the text field not reflecting the dropdown value. However, a new problem arises:
When I type in the text field, it replaces all the content with the character I am typing. It seems like the TextEditingController
is resetting after each keystroke.
Error Images:
Expectation: I want the dropdown value to populate the text field but allow users to freely edit the text in the field without overwriting input.
Upvotes: 0
Views: 48