问题:我正在尝试将下拉列表的值链接到 Flutter 中的文本字段。我使用
Provider
进行状态管理。下拉列表中的值正确更新状态,但文本字段未反映更改。
重新创建的步骤:下面是重现该问题的简单代码:
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();
}
}
错误图片:
初始问题:当我从下拉列表中选择一个值时,它会按预期更新
appState.val
,但文本字段不反映该值。
我意识到
initState()
只运行一次。因此,当下拉值发生变化时,我使用 didUpdateWidget()
来更新 TextEditingController
。
使用
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,
);
}
}
新问题:此解决方案修复了文本字段不反映下拉值的问题。然而,新的问题出现了:
当我在文本字段中输入内容时,它会将所有内容替换为我正在输入的字符。似乎每次击键后
TextEditingController
都会重置。
错误图片:
1. 初始屏幕
期望:我希望下拉值填充文本字段,但允许用户自由编辑字段中的文本而不覆盖输入。
如果您的主要目标是将 DropdownMenu 与 TextField 同步,那么下面的代码应该满足您的期望:
class DropdownWithTextField extends StatefulWidget {
const DropdownWithTextField({super.key});
@override
State<DropdownWithTextField> createState() => _DropdownWithTextFieldState();
}
class _DropdownWithTextFieldState extends State<DropdownWithTextField> {
late TextEditingController _controller;
var items = ["Item 1", "Item 2", "Item 3", "Item 4"];
@override
void initState() {
super.initState();
_controller = TextEditingController();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
DropdownMenu(
onSelected: (value) {
setState(() {
_controller.text = value!;
});
},
dropdownMenuEntries: items
.map(
(item) => DropdownMenuEntry(value: item, label: item),
)
.toList()),
TextField(
controller: _controller,
)
],
);
}
}
我注意到您正在使用 Provider 和 didUpdateWidget,但不清楚为什么它们对于这个用例是必要的。管理简单下拉列表和文本字段的状态通常不需要像 Provider 这样的完整状态管理解决方案,也不需要生命周期方法 didUpdateWidget。
如果您有更复杂的状态管理需求,那么澄清这些需求将会很有帮助。然后社区可以引导您找到最合适的解决方案,而不是在相对简单的场景中坚持使用 Provider 或 didUpdateWidget。