无法使用提供程序将 DropdownButton 链接到 flutter 中的 TextFormField

问题描述 投票:0回答:1

问题:我正在尝试将下拉列表的值链接到 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();
  }
}

错误图片:

  1. 初始屏幕

  2. 从下拉列表中选择项目后

初始问题:当我从下拉列表中选择一个值时,它会按预期更新

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. 初始屏幕

  1. 从下拉列表中选择项目后

  2. 输入“a”时

  3. 输入“b”时

期望:我希望下拉值填充文本字段,但允许用户自由编辑字段中的文本而不覆盖输入。

flutter provider textformfield flutter-textformfield flutter-dropdownbutton
1个回答
0
投票

如果您的主要目标是将 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。

© www.soinside.com 2019 - 2024. All rights reserved.