响应文本框内容的更改

在某些情境中,我们可能需要在每次文本框的文本内容变化时都调用回调函数。例如,当构建一个有自动填充功能的搜索页面时,我们希望根据用户输入的内容来更新返回的结果。

那么如何每次在文本内容改变时调用回调函数呢?在Flutter中,我们提供了两种选择:

  1. TextFieldTextFormField 绑定 onChanged() 回调

  2. 使用 TextEditingController

1. 给 TextFieldTextFormField 绑定 onChanged() 回调

最简单的方法是给 TextField 绑定 onChanged() 回调。每当文本内容改变时,回调函数会被触发。

在下面的示例中,每次 text 的值改变,会在控制台中打印出当前文本框的值。

在处理用户的输入内容时,很重要的一点是要使用 characters,因为用户的输入内容可能会包含复杂的字符。 characters 会保证每一个显示到用户字符都被计数到。

TextField(
  onChanged: (text) {
    print('First text field: $text (${text.characters.length})');
  },
),

2. 使用 TextEditingController

另外一种更强大但是更复杂的方法是绑定 TextEditingController 作为 TextFieldTextFormFieldcontroller 属性。

你可以通过如下步骤,使用 addListener() 方法来监听控制,实现在文本更改时收到通知:

  1. 创建一个 TextEditingController

  2. TextEditingController 绑定到 text field

  3. 创建一个函数来打印最新值

  4. 监听控制器的变化

创建一个 TextEditingController

创建一个 TextEditingController

// Define a custom Form widget.
class MyCustomForm extends StatefulWidget {
  const MyCustomForm({super.key});

  @override
  State<MyCustomForm> createState() => _MyCustomFormState();
}

// Define a corresponding State class.
// This class holds data related to the Form.
class _MyCustomFormState extends State<MyCustomForm> {
  // Create a text controller. Later, use it to retrieve the
  // current value of the TextField.
  final myController = TextEditingController();

  @override
  void dispose() {
    // Clean up the controller when the widget is removed from the
    // widget tree.
    myController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // Fill this out in the next step.
  }
}

给 text field 绑定 TextEditingController

TextEditingController 必须绑定到 TextField 或者是 TextFormField 才能被正常的使用。一旦绑定,就能够开始监听文本框的变化。

TextField(
  controller: myController,
),

创建一个打印当前值的方法

现在,我们需要一个每当表单项变化都会运行的函数。在下面的示例中,我们会在 _MyCustomFormState 类中创建一个方法,实现打印出文本框当前值。

void _printLatestValue() {
  final text = myController.text;
  print('Second text field: $text (${text.characters.length})');
}

监听控制器的变化

最后,需要监听 TextEditingController 并且在 text 值变化时运行 _printLatestValue() 方法。我们需要使用 addListener() 方法来实现这个功能。

下面的示例会在类 _MyCustomFormState 初始化的时候开始监听变化,dispose 时停止监听。

@override
void initState() {
  super.initState();

  // Start listening to changes.
  myController.addListener(_printLatestValue);
}
@override
void dispose() {
  // Clean up the controller when the widget is removed from the widget tree.
  // This also removes the _printLatestValue listener.
  myController.dispose();
  super.dispose();
}

交互式样例

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Retrieve Text Input',
      home: MyCustomForm(),
    );
  }
}

// Define a custom Form widget.
class MyCustomForm extends StatefulWidget {
  const MyCustomForm({super.key});

  @override
  State<MyCustomForm> createState() => _MyCustomFormState();
}

// Define a corresponding State class.
// This class holds data related to the Form.
class _MyCustomFormState extends State<MyCustomForm> {
  // Create a text controller and use it to retrieve the current value
  // of the TextField.
  final myController = TextEditingController();

  @override
  void initState() {
    super.initState();

    // Start listening to changes.
    myController.addListener(_printLatestValue);
  }

  @override
  void dispose() {
    // Clean up the controller when the widget is removed from the widget tree.
    // This also removes the _printLatestValue listener.
    myController.dispose();
    super.dispose();
  }

  void _printLatestValue() {
    final text = myController.text;
    print('Second text field: $text (${text.characters.length})');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Retrieve Text Input'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            TextField(
              onChanged: (text) {
                print('First text field: $text (${text.characters.length})');
              },
            ),
            TextField(
              controller: myController,
            ),
          ],
        ),
      ),
    );
  }
}