Flutter中用Provider关联DropdownButton与TextFormField遇问题求助
问题概述
使用Provider状态管理时,DropdownButton选中值能正常更新appState.val,但TextFormField无法同步显示该值;添加didUpdateWidget同步控制器后,又出现文本框输入时内容被强制覆盖的问题。需要实现:下拉框选中值自动填充文本框,同时允许用户自由编辑文本框内容。
问题原因
- 初始同步失败:
TextEditingController仅在initState中初始化一次,后续appState.val更新时,控制器文本未同步。 - 输入被覆盖:添加
didUpdateWidget后,每次文本框输入触发onChange更新appState.val,Provider通知组件重建,didUpdateWidget会再次将控制器文本重置为appState.val,形成循环覆盖。
解决方案
在didUpdateWidget中增加判断逻辑:仅当外部状态(下拉框选择)发生变化,且当前控制器文本与新状态值不一致时,才更新控制器文本。避免文本框输入时的循环重置。
修改_MyWidgetState代码如下:
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); // 仅当外部状态变化,且控制器文本与新状态值不一致时更新 if (oldWidget.str != widget.str && textEditingController.text != widget.str) { textEditingController.text = widget.str ?? ""; } } @override Widget build(BuildContext context) { return TextFormField( controller: textEditingController, onChanged: widget.onChange, ); } }
完整可运行代码
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>( value: items.contains(appState.val)? appState.val : null, hint: const Text("Select an item"), items: items.map((String item) { return DropdownMenuItem<String>( value: item, child: Text(item), ); }).toList(), 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 void didUpdateWidget(covariant MyWidget oldWidget) { super.didUpdateWidget(oldWidget); // 仅当外部状态变化,且控制器文本与新状态值不一致时更新 if (oldWidget.str != widget.str && textEditingController.text != widget.str) { textEditingController.text = widget.str ?? ""; } } @override Widget build(BuildContext context) { 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(); } }
内容的提问来源于stack exchange,提问作者Nk C
相关产品推荐
相关产品推荐

