💬

【Flutter】riverpodのプロバイダ内で、他のプロバイダの状態を更新するサンプルコード

2023/03/16に公開

はじめに

下記記事の続編です。
https://zenn.dev/konbu33/articles/14040a2cc51a55

上記記事では、Widget の内側で、「riverpod のプロバイダの状態を更新する方法」について記載しました。本記事では、Widget の外側にあたる、「riverpod のプロバイダ内で、他のプロバイダの状態を更新する方法」について記載します。

解説に利用するアプリの動作イメージ

CountUp ボタンを押下すると、記事一覧に記事が追加され、リスト表示される。
といった動作をするアプリを例に解説します。

operation_image

コード図解

下記図の ①〜⑧ を順番に見てもらえれば、動作イメージとコードがリンクするかと思います。
そして、本記事で伝えたい要点は ⑤ と ⑦ です。
⑤ は「プロバイダ内で、他のプロバイダの状態を更新する」コードです。
⑦ は「⑤ を発動させるためには、Widget 内で事前に watch しておく必要がある」コードです。

source_code

応用

本サンプルの counterProvider の部分(契機になる部分)を置き換えることで、応用できるかと考えています。

サンプルコード全体

サンプルコード全体
lib/main.dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

import 'article_page.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Riverpod Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const ArticlePage(),
    );
  }
}

lib/article_page.dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

import 'article_state_list_provider.dart';

// --------------------------------------------------
//
// counterProvider
//
// --------------------------------------------------
final counterProvider = StateProvider((ref) => 0);

// --------------------------------------------------
//
// ArticlePage
//
// --------------------------------------------------
class ArticlePage extends StatelessWidget {
  const ArticlePage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Article Page"),
      ),
      body: Center(
        child: Column(
          children: [
            ArticlePageParts.countUpButtonWidget(),
            ArticlePageParts.articleStateListWidget(),
          ],
        ),
      ),
    );
  }
}

// --------------------------------------------------
//
// ArticlePageParts
//
// --------------------------------------------------
class ArticlePageParts {
  //

  // --------------------------------------------------
  // countUpButtonWidget
  // --------------------------------------------------
  static Widget countUpButtonWidget() {
    final widet = Consumer(builder: (context, ref, child) {
      return ElevatedButton(
        onPressed: () {
          // counterProviderの値をカウントアップする。
          ref.read(counterProvider.notifier).state++;
        },
        child: Text("Count Up : ${ref.watch(counterProvider)}"),
      );
    });

    return widet;
  }

  // --------------------------------------------------
  // articleStateListWidget
  // --------------------------------------------------
  static Widget articleStateListWidget() {
    final widet = Consumer(builder: (context, ref, child) {
      // ArticleStateListの更新をwatch
      final articleStateList = ref.watch(articleStateListProvider);

      // counterProviderの値がカウントアップされた場合、
      // ArticleStateを生成し、ArtcileStateListに追加する処理を実行するためにwatch
      ref.watch(addArticleStateListProvder);

      return articleStateList.isEmpty
          ? const Text("No data")
          : Expanded(
              // ArticleStateListが更新されたらリスト表示する。
              child: ListView.builder(
                itemCount: articleStateList.length,
                itemBuilder: (context, index) {
                  final articleState = articleStateList[index];

                  return ListTile(
                    leading: Text("id : ${articleState.id}"),
                    title: Text(articleState.title),
                  );
                },
              ),
            );
    });

    return widet;
  }
}


lib/article_state_list_provider.dart
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

import 'article_page.dart';

// --------------------------------------------------
//
// ArticleState
//
// --------------------------------------------------
class ArticleState {
  const ArticleState({
    required this.id,
    required this.title,
  });

  final int id;
  final String title;
}

// --------------------------------------------------
//
// ArticleStateList
//
// --------------------------------------------------
class ArticleStateList extends Notifier<List<ArticleState>> {
  
  List<ArticleState> build() {
    return [];
  }

  void add(ArticleState articleState) {
    state = [...state, articleState];
  }
}

// --------------------------------------------------
//
// articleStateListProvider
//
// --------------------------------------------------
final articleStateListProvider =
    NotifierProvider<ArticleStateList, List<ArticleState>>(() {
  return ArticleStateList();
});

// --------------------------------------------------
//
// addArticleStateListProvder
//
// --------------------------------------------------
// counterProvierの値をwatchし、カウントアップされた場合、
// ArticleStateを生成し、AritcleStateListに追加する。
final addArticleStateListProvder = Provider((ref) {
  final articleId = ref.watch(counterProvider);

  // Provider内で他のProviderの状態を更新するサンプルコード①
  unawaited(Future(() {
    final articleState =
        ArticleState(id: articleId, title: "title : unawaited(Future((){}))");
    ref.read(articleStateListProvider.notifier).add(articleState);
  }));

  // Provider内で他のProviderの状態を更新するサンプルコード②
  WidgetsBinding.instance.addPostFrameCallback((_) {
    final articleState =
        ArticleState(id: articleId, title: "title : addPostFrameCallback");
    ref.read(articleStateListProvider.notifier).add(articleState);
  });
});


Discussion