嵌套Provider的notifyListeners()没有更新UI

pav*_*rdy 6 flutter

我正在使用提供程序Flutter 包。

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

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

Run Code Online (Sandbox Code Playgroud)

Pav*_*vel 6

这里有几个问题

1)您正在Collection()内部创建ChangeNotifierProvider.value,这可能会导致重新创建它(不仅是现在,而且在代码发生一些更改之后)。相反,使用ChangeNotifierProviderwithbuilder只会创建一次值

2)您使用的是单个 ChangeNotifierProviderfor CityName,而可以有多个CityName实例。因此,不会引用CityName所有提供者的特定实例,而是始终为您提供相同的实例

3)传递collection.id然后通过id查找是没有必要的,并且会使代码变得复杂。只需传递集合实例本身

4)CityNamesList没有具体提供CityName,而是从提供商处获取Collection。在这种情况下,UI 将不会被重建,因为它不监听CityNamesList更改。实际上它监听Collection变化,但Collection 不知道它的项目何时变化,因为没有人notifyListeners() 调用Collection内部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();
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 该错误不是由长列表引起的。当您将“ChangeNotifierProvider”与“builder”一起使用并将**现有**项目传递给构建器时,它会出现。(然后,当 Provider 被处置时,它也会处置构建的项目,以便将来无法使用)。您应该仅在与 Provider 一起创建新项目时使用构建器。使用现有项目“ChangeNotifierProvider.value”创建 Provider 即可 (2认同)