简体   繁体   English

Flutter:使用 ChangeNotifier 更新 TextFormField 文本

[英]Flutter: Update TextFormField text with ChangeNotifier

In a complex scenario I need to update the text of some TextFormField s when a notifyListeners() is sent by a Model extending ChangeNotifier .在复杂的场景中,当扩展ChangeNotifier的模型发送notifyListeners()时,我需要更新某些TextFormField的文本。 The problem is that to change the text of a TextFormField you have to use the setter TextFormField.text which implies a rebuild, and so you can't use it into the build method.问题是要更改TextFormField的文本,您必须使用 setter TextFormField.text这意味着重建,因此您不能将其用于build方法。 But to access the Provider of the model you need the context which is inside the build method.但是要访问模型的Provider ,您需要build方法中的context

MWE (obviously the button is in another Widget in the real project, and there are more TextFormField s) MWE(很明显按钮在真实项目中的另一个Widget中, TextFormField比较多)

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

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

class MyModel extends ChangeNotifier {
  void updateCounter() {
    ++_counter;
    notifyListeners();
  }

  MyModel() {
    _counter = 1;
  }

  int _counter;
  String get counter => _counter.toString();
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => MyModel(),
      child: MaterialApp(
        title: 'Test',
        home: MyHomePage(),
      ),
    );
  }
}

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

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  var _text1Ctl = TextEditingController();
  var _text2Ctl = TextEditingController();

  @override
  void initState() {
    super.initState();
    final model = MyModel();
    model.addListener(() {
      _text1Ctl.text = model.counter;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
      FlatButton(
        onPressed: () {
          Provider.of<MyModel>(context, listen: false).updateCounter();
        },
        child: Text('Press me'),
      ),
      // 1st attempt
      // Doesn't work because the listener isn't applied to the instance of the model provided by the provider.
      TextFormField(controller: _text1Ctl),
      // 2nd attempt
      // Works but with `Another exception was thrown: setState() or markNeedsBuild() called during build.` because it changes text via controller (which implies a rebuild) during building.
      Consumer<MyModel>(builder: (context, model, child) {
        _text2Ctl.text = model.counter;
        return TextFormField(controller: _text2Ctl);
      })
    ]));
  }
}

Your second example works without any errors when I run it:你的第二个例子在我运行时没有任何错误:

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

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

class MyModel extends ChangeNotifier {
  void updateCounter() {
    ++_counter;
    notifyListeners();
  }

  MyModel() {
    _counter = 1;
  }

  int _counter;
  String get counter => _counter.toString();
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => MyModel(),
      child: MaterialApp(
        title: 'Test',
        home: MyHomePage(),
      ),
    );
  }
}

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

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  var _text2Ctl = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
      FlatButton(
        onPressed: () {
          Provider.of<MyModel>(context, listen: false).updateCounter();
        },
        child: Text('Press me'),
      ),

      // 2nd attempt
      // Works but with `Another exception was thrown: setState() or markNeedsBuild() called during build.` because it changes text via controller (which implies a rebuild) during building.
      Consumer<MyModel>(builder: (context, model, child) {
        _text2Ctl.text = model.counter;
        return TextFormField(controller: _text2Ctl);
      })
    ]));
  }
}

演示

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM