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

Flutter中如何基于DropdownMenu的onSelected更新TextField?

问题描述

我编写了一段Flutter代码,期望UI中“Result”文本旁的TextField能根据DropdownMenu的选项变化自动更新。但当前选择下拉菜单的新选项时,该TextField不会自动更新,仅当修改旁边的非只读TextField时才会变化。我尝试给firstController添加监听器,但并未生效。

代码如下:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

const List<String> list = <String>[
  "A",
  "B",
  "C",
];

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late final TextEditingController firstController;
  late final TextEditingController controllerResult;
  late String dropdownValue = list.first;

  @override
  void initState() {
    super.initState();
    firstController = TextEditingController();
    controllerResult = TextEditingController();
  }

  @override
  void dispose() {
    firstController.dispose();
    controllerResult.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    ComputeResult cr;

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Container(
        padding: const EdgeInsets.all(10.0),
        child: Column(
          children: [
            Row(
              children: [
                Expanded(
                  flex: 5,
                  child: TextField(
                    controller: firstController,
                    decoration: const InputDecoration(
                      border: OutlineInputBorder(),
                      hintText: 'Value',
                    ),
                    keyboardType: TextInputType.number,
                    onChanged: (value) {
                      if (firstController.text.isNotEmpty) {
                        switch (dropdownValue) {
                          case "A":
                            controllerResult.text = "-1";
                            break;
                          case "B":
                            controllerResult.text = "-2";
                            break;
                          case "C":
                            cr = ComputeResult.named03(
                                double.parse(firstController.text));
                            controllerResult.text = cr.value01.toString();
                            break;
                        }
                      } else {
                        controllerResult.text = "";
                      }
                    },
                  ),
                ),
                Expanded(
                  flex: 5,
                  child: DropdownMenu<String>(
                    initialSelection: list.first,
                    onSelected: (String? value) {
                      setState(() {
                        dropdownValue = value!;
                      });
                    },
                    dropdownMenuEntries:
                        list.map<DropdownMenuEntry<String>>((String value) {
                      return DropdownMenuEntry<String>(
                          value: value, label: value);
                    }).toList(),
                  ),
                ),
              ],
            ),
            SingleChildScrollView(
              child: Column(
                children: [
                  Row(
                    children: [
                      Expanded(
                        flex: 5,
                        child: TextField(
                          readOnly: true,
                          controller: controllerResult,
                        ),
                      ),
                      Expanded(
                        flex: 5,
                        child: Text("Result"),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class ComputeResult {
  ComputeResult.named01(this.value01) {
    value02 = value01 + 100;
    value03 = value01 + 110;
  }
  ComputeResult.named02(this.value02) {
    value01 = value02 + 200;
    value03 = value02 + 210;
  }
  ComputeResult.named03(this.value03) {
    value01 = value03 + 300;
    value02 = value03 + 310;
  }
  double value01 = 0;
  double value02 = 0;
  double value03 = 0;
}
解决方案

问题核心是:DropdownMenu的onSelected仅更新了dropdownValue的状态,但没有触发结果计算逻辑——结果计算只绑定在了输入框的onChanged事件上,所以切换下拉选项时不会更新结果。

修复步骤:

  1. 抽离计算逻辑为独立方法:把输入框onChanged里的计算代码提取成_updateResult()方法,实现逻辑复用。
  2. 在Dropdown切换时调用计算方法:更新dropdownValue状态后,立即调用_updateResult()刷新结果。
  3. 简化输入框的onChanged:直接调用抽离后的计算方法即可。

修改后的完整代码:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

const List<String> list = <String>[
  "A",
  "B",
  "C",
];

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late final TextEditingController firstController;
  late final TextEditingController controllerResult;
  late String dropdownValue = list.first;

  @override
  void initState() {
    super.initState();
    firstController = TextEditingController();
    controllerResult = TextEditingController();
  }

  @override
  void dispose() {
    firstController.dispose();
    controllerResult.dispose();
    super.dispose();
  }

  // 抽离的结果计算方法
  void _updateResult() {
    if (firstController.text.isNotEmpty) {
      switch (dropdownValue) {
        case "A":
          controllerResult.text = "-1";
          break;
        case "B":
          controllerResult.text = "-2";
          break;
        case "C":
          final cr = ComputeResult.named03(double.parse(firstController.text));
          controllerResult.text = cr.value01.toString();
          break;
      }
    } else {
      controllerResult.text = "";
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Container(
        padding: const EdgeInsets.all(10.0),
        child: Column(
          children: [
            Row(
              children: [
                Expanded(
                  flex: 5,
                  child: TextField(
                    controller: firstController,
                    decoration: const InputDecoration(
                      border: OutlineInputBorder(),
                      hintText: 'Value',
                    ),
                    keyboardType: TextInputType.number,
                    onChanged: (value) {
                      _updateResult();
                    },
                  ),
                ),
                Expanded(
                  flex: 5,
                  child: DropdownMenu<String>(
                    initialSelection: list.first,
                    onSelected: (String? value) {
                      setState(() {
                        dropdownValue = value!;
                      });
                      _updateResult(); // 切换下拉后立即计算结果
                    },
                    dropdownMenuEntries:
                        list.map<DropdownMenuEntry<String>>((String value) {
                      return DropdownMenuEntry<String>(
                          value: value, label: value);
                    }).toList(),
                  ),
                ),
              ],
            ),
            SingleChildScrollView(
              child: Column(
                children: [
                  Row(
                    children: [
                      Expanded(
                        flex: 5,
                        child: TextField(
                          readOnly: true,
                          controller: controllerResult,
                        ),
                      ),
                      Expanded(
                        flex: 5,
                        child: Text("Result"),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class ComputeResult {
  ComputeResult.named01(this.value01) {
    value02 = value01 + 100;
    value03 = value01 + 110;
  }
  ComputeResult.named02(this.value02) {
    value01 = value02 + 200;
    value03 = value02 + 210;
  }
  ComputeResult.named03(this.value03) {
    value01 = value03 + 300;
    value02 = value03 + 310;
  }
  double value01 = 0;
  double value02 = 0;
  double value03 = 0;
}

这样修改后,无论是切换下拉选项还是修改输入框内容,都会触发结果计算并自动更新“Result”旁的TextField。

内容的提问来源于stack exchange,提问作者rusty

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 18:08:11