(flutter) 将状态发送到其他小部件

(flutter) send state to other widget

在我的 Flutter 应用程序中,我想重新使用接收用户输入的自定义对话框。如果我在 class 中执行 showDialog() 并在另一个 class 中使用自定义 AlertDialog,则一切正常。但是,这迫使我每次都重新键入按钮的 onpressed 和 showdialog 部分。我更愿意将它也放在另一个可重用的 class 中并将用户输入传递给它(并在 return 中获得具有所有功能的按钮)。这样做的问题是加载页面时用户输入不存在,所以我需要知道按下按钮时的更新状态。在那里我卡住了......猜测它应该与传递小部件的状态有关,但在这里迷路了。

我创建了一个最小的应用程序来重现问题,这是代码:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  @override
  Widget build(BuildContext context) {
    var myTextFieldController = TextEditingController();

    return Scaffold(
      appBar: AppBar(title: const Text('Dialog test')),
      body: ListView(
        padding: const EdgeInsets.all(20),
        children: [
          TextField(controller: myTextFieldController),
          TestButton().getButton(context, myTextFieldController.text),
        ],
      ),
    );
  }
}

class TestButton {
  Widget getButton(BuildContext context, myText) {
    return ElevatedButton(
      child: const Text('Show Dialog'),
      onPressed: () {
        showDialog(
            context: context,
            builder: (builder) {
              return AlertDialog(
                title: Text(myText), // is always empty
              );
            });
      },
    );
  }
}

AlertDialog 的文本仍然为空,尽管 TextField 中有文本。我怎样才能让它工作?谢谢!

您可以按照以下步骤操作:

  1. 创建一个新的 String 变量来存储 TextField 中的值。
  2. 更新 TextField 的 onChanged 属性 中的 String 值并调用 setState。
  3. 将创建的新变量传递给 getButton 而不是 myTextFieldController.text

代码:

class _HomeScreenState extends State<HomeScreen> {

  var myTextFieldController = TextEditingController();
  String textValue =  "";

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(title: const Text('Dialog test')),
      body: ListView(
        padding: const EdgeInsets.all(20),
        children: [
          TextField(
            controller: myTextFieldController,
            onChanged: (v){
              textValue = v;
              setState((){});
            }
          ),
          TestButton().getButton(context, textValue),
        ],
      ),
    );
  }

为什么我用myTextFieldController.text时传递的值总是空的?

这是因为 myTextFieldController.text 的第一个值为 ""(空)的值是传递给 getButton 的值,并且在 getButton TextField 个更改。

像这样调用您的 getbutton 方法.........

TestButton().getButton(context, myTextFieldController.value.text);

有效.....访问 textfieldcontroller 的 value 属性将解决此问题。

这是因为最初当您调用 TestButton().getButton 时,一个新的小部件是 built,初始值为 myTextFieldController.text,它是空的。现在,当您插入文本时,myTextFieldController.text 的值会发生变化,但 flutter 仍在使用最初为 built 的小部件,因此对话框文本不会更新。强制 rebuild 的方法是在 myTextFieldController.text 更改时使用 setState。但最好将小部件和对话框彼此分离。现在负责显示对话框的小部件是一个简单的 ElevatedButton。如果它有一个相当复杂的 ui 需要在其他地方使用,那么最好实现一个新的小部件,而不是让 class 你在它的方法中使用一个小部件。如果您从 showDialog 中分离小部件,则不再需要调用 setState。下面的代码有两个按钮来展示差异。第二个只是工作,第一个虽然你必须取消注释 TextField:

onChanged
class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  var myTextFieldController = TextEditingController();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Dialog test')),
      body: ListView(
        padding: const EdgeInsets.all(20),
        children: [
          TextField(
            controller: myTextFieldController,

            /// Uncomment the following 3 lines for the first button to work
            // onChanged: (val) {
            //   // setState(() {});
            // },
            ///
          ),
          TestButton().getButton(context, myTextFieldController.text),
          ElevatedButton(
            onPressed: () => TestButton.showTestDialogue(context, myTextFieldController.text),
            child: const Text('Show Test Dialog'),
          ),
        ],
      ),
    );
  }
}

class TestButton {
  Widget getButton(BuildContext context, myText) {
    return ElevatedButton(
      child: const Text('Show Dialog'),
      onPressed: () {
        showDialog(
          context: context,
          builder: (builder) {
            return AlertDialog(
              title: Text(myText), // is always empty
            );
          },
        );
      },
    );
  }

  static Future showTestDialogue(BuildContext context, myText) {
    return showDialog(
      context: context,
      builder: (builder) {
        return AlertDialog(
          title: Text(myText),
        );
      },
    );
  }
}