Flutter:使用ChangeNotifier更新TextFormField文本

时间:2020-09-28 08:38:57

标签: flutter dart provider changenotifier

在复杂的情况下,当扩展TextFormField的模型发送notifyListeners()时,我需要更新某些ChangeNotifier的文本。 问题是要更改TextFormField的文本,您必须使用setter TextFormField.text,这意味着要进行重建,因此不能在build方法中使用它。但是要访问模型的Provider,您需要context方法内部的build

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);
      })
    ]));
  }
}

1 个答案:

答案 0 :(得分:1)

您运行的第二个示例在运行时没有任何错误:

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);
      })
    ]));
  }
}

Demo