Flutter Provider:拆分通知的正确方法

Flutter Provider: proper way to split notifying

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

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

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

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

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

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

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

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

可能,像这样的东西应该有效:

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

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

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

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

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

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

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


现在是重构的时候了,你必须制作两个 classes(或方法),它们将有自己的构建方法(你也可以定义两个方法并将 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 中有了这些 classes:

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