flutter - 如何使用 Provider/ChangeNotifier 在状态更改时让 TextFormField flutter ?

标签 flutter flutter-provider

使用 Provider 作为状态管理时,我在更新 TextFormField 内的文本时遇到问题。

我将问题简化为一个抽象问题(我删除了所有杂乱的代码),下面是它的工作原理:

  • AppState 中有一个 someValue
  • 可以通过Form->TextFormField编辑someValue
  • 输入 (onChange) 时,someValue 将反射(reflect)为 AppBar 的标题
  • 可以从外部源更新someValue(在示例中,它是一个更新它的按钮)
  • someValue外部源更新时,它也必须在文本 Form->TextFormField 中更新

最后一个给我带来了问题。考虑以下代码:

AppState.dart

import 'package:flutter/foundation.dart';

class AppState extends ChangeNotifier{
  String someValue = '';

  updateSomeValue(String newValue){
    someValue = newValue;
    notifyListeners();
  }
}

main.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:text_ctrl_issue/app_state.dart';

void main() {
  runApp(ChangeNotifierProvider(create: (_) => AppState(), child: MyApp()));
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

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

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

class _MyHomePageState extends State<MyHomePage> {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  late TextEditingController _controller;

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

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

  @override
  Widget build(BuildContext context) {
    final provider = Provider.of<AppState>(context);

// following line of code makes it possible for text to be changed by button
// and reflected in TextFormField
// but it causes nasty side effect, that when typing, cursor always goes to beginning of the line
    _controller.text = provider.someValue;

    return Scaffold(
      appBar: AppBar(
        title: Text(provider.someValue),
      ),
      body: Center(
        child: Form(
            key: _formKey,
            child: Column(children: [
              TextFormField(
                controller: _controller,
                onChanged: (value) {
                  provider.updateSomeValue(value);
                },
              ),
              ElevatedButton(
                  onPressed: () {
                    provider.updateSomeValue('foo_bar');
                  },
                  child: Text('change text external source'))
            ])),
      ),
    );
  }
}

问题: 当我添加行 _controller.text =provider.someValue; 它修复了单击按钮时更新 TextFormField 的问题,但它创建了新问题,即在 TextFormField 中输入时,它也会被触发,原因文本字段的插入符移动到文本字段的开头。

如何使其工作,以便可以在外部更新 TextFormField 的文本(值),而不会在键入时导致 carret 问题?

编辑 Yeasin Sheikh 使用 addListener 的答案不太有效(这是 hacky),因为:

  1. 它监听每个事件(例如 onFocus 或光标更改)
  2. 它没有考虑 EleveatedButton 与 _controller 处于不同范围的情况(例如位于不同的小部件中)。

最佳答案

通过监听 TextEditingController 来实现此目的的简单方法是,而 TextFormField 是这里的标尺。

class _MyHomePageState extends State<MyHomePage> {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  late TextEditingController _controller;

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  void initState() {
    super.initState();
    _controller = TextEditingController()
      ..addListener(() {
        Provider.of<AppState>(context, listen: false)
            .updateSomeValue(_controller.text);
      });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(context.watch<AppState>().someValue),
      ),
      body: Center(
        child: Form(
          key: _formKey,
          child: Column(
            children: [
              TextFormField(
                controller: _controller,
              ),
              ElevatedButton(
                  onPressed: () {
                    _controller.text = 'foo_bar';
                  },
                  child: Text('change text external source'))
            ],
          ),
        ),
      ),
    );
  }
}

此外,您还可以查看riverpod

关于flutter - 如何使用 Provider/ChangeNotifier 在状态更改时让 TextFormField flutter ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72908734/

相关文章:

firebase - 没有创建 Firebase App '[DEFAULT]' - 在 Flutter 和 Firebase 中调用 Firebase.initializeApp()

firebase - 尽管用户凭据错误,登录仍然有效

flutter - FutureProvider 的用例

flutter - 如何在 Flutter 中正确重用 Provider

firebase - Stream <QuerySnapshot>返回空文档

flutter - 由于它在 Dart 中的类型,该参数的值不能为 'null'

dart - Flutter setState 到另一个类?

flutter - FutureProvider 坏了还是什么?

flutter - 如何检查提供的类是否存在

android - 如果一个文本行很长,如何使盒子中的弹性 flutter 以使每一行居中?