如何最好地使用 Riverpod 管理列表中的项目

问题描述

我正在努力弄清楚如何在以下场景中使用 Riverpod。

我有一个 ListView,其中的孩子是容器,里面有一个按钮。

按下按钮时,我想更改该容器的颜色。我希望使用 Riverpod 提供程序将该颜色存储在一个状态中。

列表外还有一个按钮。当按下时,它应该改变所有容器的颜色。

enter image description here

感觉就像每个容器都需要一个 Change/StateNotifierProvider。我会为此使用家庭吗?我如何将特定的状态与其关联的容器相关联?

红色按钮如何访问所有状态以更改所有状态?

作为奖励,我还希望在其中一个绿色按钮更改其容器颜色时通知红色按钮

非常感谢

解决方法

您可以使用 family,但在这种情况下,由于您的条目数量不固定,这会使事情不必要地复杂化。

这是一个用 hooks_riverpod 编写的完整可运行示例。如果你需要我翻译成不使用钩子,我也可以这样做。请记住,这是故意简单且有点幼稚,但应该适合您的情况。

首先,模型类。我通常会使用 freezed 但这超出了这个问题的范围。

class Model {
  final int id;
  final Color color;

  Model(this.id,this.color);
}

接下来,StateNotifier:

class ContainerListState extends StateNotifier<List<Model>> {
  ContainerListState() : super(const []);

  static final provider = StateNotifierProvider<ContainerListState,List<Model>>((ref) {
    return ContainerListState();
  });

  void setAllColor(Color color) {
    state = state.map((model) => Model(model.id,color)).toList();
  }

  void setModelColor(Model model,Color color) {
    final id = model.id;
    state = state.map((model) {
      return model.id == id ? Model(id,color) : model;
    }).toList();
  }

  void addItem() {
    // TODO: Replace state.length with your unique ID
    state = [...state,Model(state.length,Colors.lightBlue)];
  }
}

最后,UI 组件(钩子):

class MyHomePage extends HookWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final modelList = useProvider(ContainerListState.provider);
    return Scaffold(
      appBar: AppBar(
        title: Text('ListView of Containers'),actions: [
          IconButton(
            icon: Icon(Icons.add),onPressed: () {
              context.read(ContainerListState.provider.notifier).addItem();
            },),],body: ListView.builder(
        itemCount: modelList.length,itemBuilder: (_,index) {
          return ContainerWithButton(model: modelList[index]);
        },floatingActionButton: RedButton(),);
  }
}

class ContainerWithButton extends StatelessWidget {
  const ContainerWithButton({
    Key? key,required this.model,}) : super(key: key);

  final Model model;

  @override
  Widget build(BuildContext context) {
    return ListTile(
      tileColor: model.color,trailing: ElevatedButton(
        style: ElevatedButton.styleFrom(primary: Colors.lightGreen),onPressed: () {
          context.read(ContainerListState.provider.notifier).setModelColor(model,Colors.purple);
        },child: Text('Button'),);
  }
}

class RedButton extends HookWidget {
  const RedButton({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // Bonus: Red button will be notified on changes
    final state = useProvider(ContainerListState.provider);

    return FloatingActionButton.extended(
      onPressed: () {
        context.read(ContainerListState.provider.notifier).setAllColor(Colors.orange);
      },backgroundColor: Colors.red,label: Text('Set all color'),);
  }
}

非钩子:

class MyHomePage extends ConsumerWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context,ScopedReader watch) {
    final modelList = watch(ContainerListState.provider);
    return Scaffold(
      appBar: AppBar(
        title: Text('ListView of Containers'),);
  }
}

class RedButton extends ConsumerWidget {
  const RedButton({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context,ScopedReader watch) {
    // Bonus: Red button will be notified on changes
    final state = watch(ContainerListState.provider);

    return FloatingActionButton.extended(
      onPressed: () {
        context.read(ContainerListState.provider.notifier).setAllColor(Colors.orange);
      },);
  }
}

我建议将其放入新的 Flutter 应用程序中进行测试。