You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中用Provider关联DropdownButton与TextFormField遇问题求助

解决Flutter中DropdownButton与TextFormField的Provider状态同步问题

问题概述

使用Provider状态管理时,DropdownButton选中值能正常更新appState.val,但TextFormField无法同步显示该值;添加didUpdateWidget同步控制器后,又出现文本框输入时内容被强制覆盖的问题。需要实现:下拉框选中值自动填充文本框,同时允许用户自由编辑文本框内容。

问题原因

  1. 初始同步失败:TextEditingController仅在initState中初始化一次,后续appState.val更新时,控制器文本未同步。
  2. 输入被覆盖:添加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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 13:37:06