【问题标题】:Flutter Provider: proper way to split notifyingFlutter Provider:拆分通知的正确方法
【发布时间】:2021-01-04 20:34:51
【问题描述】:

我正在尝试使用Provider 拆分小部件的触发更新。 我正在使用最新的Flutter 版本。在应用程序中,我还使用context.select()context.watch()context.read()

现在主要目标。我到底在说什么。所以我有一些通知:

class ExpenseNotifier with ChangeNotifier {
  List<Category> _selectedCategories = [];
  Expense _currentExpense;
  int _removeId;
  Expense _editExpense;
}

现在,ExpenseNotifier 有几个消费者。当某些事情发生变化时,所有消费者都会更新。除了一种情况:当_editExpense 更新时,只有一个消费者应该被更新。问题是 Expense 类已经存在于通知程序中,因此所有连接到 _currentExpense 的消费者也会对 _editExpense 更新做出反应...

我现在正在使用选择器。像这样:

context.select<ExpenseNotifier, Expense>((notifier) => notifier.currentExpense);

但由于某种原因,小部件似乎也会对 _editExpense 更新做出反应...

这种情况的正确解决方案是什么?是否有可能(不定义新类型)在ExpenseNotifier 内实现它?

也许,这样的事情应该可以工作:

class EditExpense {
  final Expense expense;
  EditExpense(this.expense);
}

所以在这种情况下需要包装类。如果我错了,请纠正我

【问题讨论】:

  • 可能最简单的解决方案是让一些消费者忽略 editExpense 字段

标签: flutter flutter-provider


【解决方案1】:

我发现你的问题很有趣,所以我认为值得研究一下。我给出了一种一般性的答案,但我认为你会从中受益。

首先向数据类添加方法,只更新必填字段, 像这样:

class DataClass with ChangeNotifier {
  String firstString = " ";
  String secondString = " ";

  void updateFirst(String newString) {
    firstString = newString;
    notifyListeners();
  }

  void updateSecond(String newString) {
    secondString = newString;
    notifyListeners();
  }
}


现在是重构的时候了,你必须创建两个拥有自己构建方法的类(或方法)(你也可以定义两个方法并将BuildContext传递给它们):

class StringOne extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("StringOne build method is called");
    return Column(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: <Widget>[
        Column(
          children: [
            Text(context.select((DataClass value) => value.firstString)),
            Container(
              height: 100,
              width: 100,
              child: TextField(
                onChanged: (text) {
                  context.read<DataClass>().updateFirst(text);
                },
              ),
            )
          ],
        )
      ],
    );
  }
}

class StringTwo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("StringTwo build method is called");
    return Column(
      children: <Widget>[
        Column(
          children: [
            Text(context.select((DataClass value) => value.secondString)),
            Container(
              height: 100,
              width: 100,
              child: TextField(
                onChanged: (text) {
                  context.read<DataClass>().updateSecond(text);
                },
              ),
            ),
          ],
        )
      ],
    );
  }
}

最后在描述 UI 的其他类中有这些类:

class ProviderExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListView(
      children: [StringOne(), StringTwo()],
    );
  }
}

您可能会说它会增加冗长,实际上,重构通常会使代码更冗长,但它也使代码更清晰且易于维护。在您的情况下,它还可以防止不必要的构建。

Console :

I/flutter ( 1469): StringTwo build method is called
I/flutter ( 1469): StringTwo build method is called
I/flutter ( 1469): StringTwo build method is called
I/flutter ( 1469): StringTwo build method is called
I/zygote64( 1469): Increasing code cache capacity to 1024KB
I/flutter ( 1469): StringOne build method is called
I/chatty  ( 1469): uid=10140(com.example.stack_overflow) 1.ui identical 7 lines
I/flutter ( 1469): StringOne build method is called
I/flutter ( 1469): StringOne build method is called


【讨论】:

  • 您好,感谢您的回复! :) 我想我有这样的东西,对我来说这是行不通的。意味着两个小部件都更新了
  • 好吧,如果你做过类似的事情,那么一次只能调用一个构建。如果您分享更多代码,可能会对您有所帮助。
猜你喜欢
  • 2021-01-08
  • 1970-01-01
  • 2021-07-16
  • 1970-01-01
  • 2021-09-29
  • 2021-05-31
  • 2021-04-29
  • 2022-10-14
  • 2023-04-01
相关资源
最近更新 更多