簡體   English   中英

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

[英]How best to use Riverpod to manage items in a list

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

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

按下按鈕時,我想更改該容器的顏色。 我希望使用 Riverpod 提供程序將該顏色存儲在一塊 state 中。

列表外還有另一個按鈕。 當按下它時,它應該改變所有容器的顏色。

在此處輸入圖像描述

感覺就像我需要每個容器的 Change/StateNotifierProvider 一樣。 我為此使用家庭嗎? 我如何將 state 的特定部分綁定到其相關容器?

紅色按鈕如何訪問所有狀態以更改所有狀態的顏色?

作為獎勵,我還希望在其中一個綠色按鈕更改其容器的顏色時通知紅色按鈕

非常感謝

您可以使用家庭,但在這種情況下,由於您的條目數量不固定,這會使事情變得不必要地復雜化。

這是一個用hooks_riverpod編寫的完整可運行示例。 如果您需要我翻譯為不使用鈎子,我也可以這樣做。 請記住,這是故意簡單且有點幼稚,但應該適應您的情況。

首先,model class。 我通常會使用freezed ,但對於這個問題,這超出了 scope 的范圍。

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 應用程序中進行測試。

Alex 的非 hooks 版本與riverpod有點不同^1.0.0

我更改了與版本無關的一件小事:我將提供程序從 class 移到全局 scope,這兩種方法都有效官方文檔在下面顯示了這個版本。

class ContainerListState extends StateNotifier<List<Model>> {
  ContainerListState() : super(const []);
  // No static provider declaration in here
...
}
// Provider moved out here
final containerListProvider = StateNotifierProvider<ContainerListState, List<Model>>((ref) {
  return ContainerListState();
});

ProviderScope 是應用程序仍然能夠訪問提供程序所必需的。

void main() {
  runApp(const ProviderScope(child: MyApp()));
}

關於這個問題從 0.12 到 1.0 的變化:

  1. StatelessWidget不再有context.read() -> 使用ConsumerWidget
  2. WidgetRef refref.watch()而不是ScopedReader watch

非鈎子:

帶河莢 ^1.0.4

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

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

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

  final Model model;

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

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

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

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM