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

How best to use Riverpod to manage items in a list

我正在努力弄清楚如何在以下情况下使用 Riverpod。

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

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

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

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

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

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

非常感谢

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

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

首先,模型 class。我通常会使用 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'),
        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 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);
      },
      backgroundColor: Colors.red,
      label: Text('Set all color'),
    );
  }
}

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