嵌套Provider的notifyListeners()未更新UI

时间:2019-09-25 20:22:14

标签: flutter

我正在使用provider Flutter软件包。

我有一个主列表,主列表的每个项目都有一个子列表。我可以将项目添加到主列表中,并且可以正常工作,但是每次我向子列表添加内容时,UI都不会更新。运行代码并添加一些项目,然后打开它们,然后在其中添加一些项目(这是子列表),您将看到看不到任何更新,但是如果最小化键盘,则UI将会更新。可能是因为我正在使用Provider.of<Collection>(context)访问子列表的对象,而notifyListeners()类的CityName无法运行findById(id),这是我能想到的。 / p>

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider.value(
          value: Collection(),
        ),
        ChangeNotifierProvider.value(
          value: CityName(),
        )
      ],
      child: MaterialApp(
        home: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final _textEditingController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Expanded(
              child: ListView(
                  children: Provider.of<Collection>(context)
                      .cityNameCollection
                      .map((collection) {
                return CollectionCard(collection.title, collection.id);
              }).toList()),
            ),
            Container(
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  Expanded(
                    child: TextField(
                      controller: _textEditingController,
                      autofocus: true,
                    ),
                  ),
                  FlatButton(
                    onPressed: () {
                      Provider.of<Collection>(context)
                          .addCollection(_textEditingController.text);
                      _textEditingController.clear();
                    },
                    child: Text(
                      'ADD',
                    ),
                  ),
                ],
              ),
            ),
          ]),
    );
  }
}

class CollectionCard extends StatelessWidget {
  final String title;
  final String id;

  CollectionCard(this.title, this.id);
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Card(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 20),
          child: Text(title),
        ),
      ),
      onTap: () {
        Navigator.of(context).push(
          MaterialPageRoute(
            builder: (context) => CityNamesList(id),
          ),
        );
      },
    );
  }
}

class CityNamesList extends StatelessWidget {
  final String id;
  CityNamesList(this.id);
  final _textEditingController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    final item = Provider.of<Collection>(context).findById(id);

    return Scaffold(
      appBar: AppBar(
        title: Text(item.title),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Expanded(
            child: ListView(
                children: item.names.map((name) {
              return ListTile(
                title: Text(name),
                trailing: IconButton(
                  icon: Icon(Icons.delete),
                  onPressed: () {
                    item.removeName(name);
                  },
                ),
              );
            }).toList()),
          ),
          Container(
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: <Widget>[
                Expanded(
                  child: TextField(
                    controller: _textEditingController,
                    autofocus: true,
                  ),
                ),
                FlatButton(
                  onPressed: () {
                    item.addName(_textEditingController.text);
                    _textEditingController.clear();
                  },
                  child: Text(
                    'ADD',
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

class Collection with ChangeNotifier {
  List<CityName> cityNameCollection = [];

  void addCollection(value) {
    cityNameCollection
        .add(CityName(title: value, id: DateTime.now().toString()));
    notifyListeners();
  }

  CityName findById(id) {
    return cityNameCollection.firstWhere((a) => a.id == id);
  }
}

class CityName with ChangeNotifier {
  String title;
  String id;
  List<String> names = [];

  CityName({this.title, this.id});

  void addName(value) {
    names.add(value);
    notifyListeners();
  }

  void removeName(value) {
    names.remove(value);
    notifyListeners();
  }
}

1 个答案:

答案 0 :(得分:1)

这里有几个问题

1)您正在Collection()内创建ChangeNotifierProvider.value,这可能导致重新创建它(不仅是现在,而且在代码进行一些更改之后)。而是将ChangeNotifierProviderbuilder一起使用,这将只创建一次值

2)您为ChangeNotifierProvider使用了单个 CityName,但可以有多个CityName实例。因此,所有提供程序都不会始终引用CityName的特定实例

3)不需要传递collection.id,然后通过id查找它,这会使代码复杂化。只需传递集合实例本身

4)CityNamesList并未提供特定的CityName,而是从Collection提供者那里获得的。在这种情况下,将不会重建UI,因为它不会监听CityNamesList的更改。实际上,它监听Collection的更改,但是Collection 不知道它的项何时更改,因为没有人在notifyListeners()上呼叫Collection / strong>在CityName@addName

5)最后,CityName的构造函数字段是可选的(不是必需的),它允许创建虚拟实例并导致错误

以下是解决此问题的示例(为简便起见,没有removeName

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      builder: (context) => Collection(),
      child: MaterialApp(
        home: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final _textEditingController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Expanded(
            child: ListView(
              children: Provider.of<Collection>(context)
                  .cityNameCollection
                  .map((item) => CollectionCard(item))
                  .toList(),
            ),
          ),
          Row(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Expanded(
                child: TextField(
                  controller: _textEditingController,
                  autofocus: true,
                ),
              ),
              FlatButton(
                onPressed: () {
                  Provider.of<Collection>(context)
                      .addCollection(_textEditingController.text);
                  _textEditingController.clear();
                },
                child: Text('ADD'),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

class CollectionCard extends StatelessWidget {
  final CityName item;

  CollectionCard(this.item);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Card(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 20),
          child: Text(item.title),
        ),
      ),
      onTap: () {
        Navigator.of(context).push(
          MaterialPageRoute(
            builder: (context) {
              return ChangeNotifierProvider.value(
                value: item,
                child: CityNamesList(),
              );
            },
          ),
        );
      },
    );
  }
}

class CityNamesList extends StatelessWidget {
  final _textEditingController = TextEditingController();

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

    return Scaffold(
      appBar: AppBar(
        title: Text(item.title),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Expanded(
            child: ListView(
              children: item.names.map((name) {
                return ListTile(
                  title: Text(name),
                );
              }).toList(),
            ),
          ),
          Container(
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: <Widget>[
                Expanded(
                  child: TextField(
                    controller: _textEditingController,
                    autofocus: true,
                  ),
                ),
                FlatButton(
                  onPressed: () {
                    item.addName(_textEditingController.text);
                    _textEditingController.clear();
                  },
                  child: Text('ADD'),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

class Collection with ChangeNotifier {
  List<CityName> cityNameCollection = [];

  void addCollection(String value) {
    cityNameCollection.add(CityName(title: value));
    notifyListeners();
  }
}

class CityName with ChangeNotifier {
  String title;
  List<String> names = [];

  CityName({@required this.title});

  void addName(value) {
    names.add(value);
    notifyListeners();
  }
}