🪂

StreamProviderでListを使う

2022/09/03に公開

RiverpodでListを使うには?

StreamProviderで、Listを使ってみたかったのですが、うまくいかなくて、ZennのRiverpodについて書かれている村松龍之介さんの本を参考にしたらうまく行きました!
以前はできなかったんですけどね😅
当時は今より、文法への理解が浅かったのでしょう😇

参考にした本
https://zenn.dev/riscait/books/flutter-riverpod-practical-introduction

必要なライブラリをインストールしておく

状態管理に必要なriverpodとFreezdeで使うパッケージをインストールする。

pubspec.yaml
name: strem_app
description: A new Flutter project.

# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev

# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1

environment:
  sdk: ">=2.17.6 <3.0.0"

# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2
  flutter_riverpod: ^1.0.4
  firebase_core: ^1.19.1
  cloud_firestore: ^3.3.0
  freezed_annotation: ^2.1.0
  json_annotation: ^4.6.0

dev_dependencies:
  flutter_test:
    sdk: flutter

  # The "flutter_lints" package below contains a set of recommended lints to
  # encourage good coding practices. The lint set provided by the package is
  # activated in the `analysis_options.yaml` file located at the root of your
  # package. See that file for information about deactivating specific lint
  # rules and activating additional ones.
  flutter_lints: ^2.0.0
  build_runner: ^2.2.0
  freezed: ^2.1.0+1
  json_serializable: ^6.3.1

# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

# The following section is specific to Flutter packages.
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  # assets:
  #   - images/a_dot_burr.jpeg
  #   - images/a_dot_ham.jpeg

  # An image asset can refer to one or more resolution-specific "variants", see
  # https://flutter.dev/assets-and-images/#resolution-aware

  # For details regarding adding assets from package dependencies, see
  # https://flutter.dev/assets-and-images/#from-packages

  # To add custom fonts to your application, add a fonts section here,
  # in this "flutter" section. Each entry in this list should have a
  # "family" key with the font family name, and a "fonts" key with a
  # list giving the asset and other descriptors for the font. For
  # example:
  # fonts:
  #   - family: Schyler
  #     fonts:
  #       - asset: fonts/Schyler-Regular.ttf
  #       - asset: fonts/Schyler-Italic.ttf
  #         style: italic
  #   - family: Trajan Pro
  #     fonts:
  #       - asset: fonts/TrajanPro.ttf
  #       - asset: fonts/TrajanPro_Bold.ttf
  #         weight: 700
  #
  # For details regarding fonts from package dependencies,
  # see https://flutter.dev/custom-fonts/#from-packages

デモアプリの構造

今回作成したデモアプリのモデルは、Freezedで作成して、StreamProviderでList型を使って使えるように設定しました。

サンプルコード

こちらのページで、Userモデルを作成します。コードを書いたら、コマンド実行して、コードを自動生成するだけです。

model/user.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter/foundation.dart';

part 'user.freezed.dart';
part 'user.g.dart';


class User with _$User {
  const factory User({
    required String name,
    required String email,
  }) = _User;

  factory User.fromJson(Map<String, dynamic> json) =>
      _$UserFromJson(json);
}

こちらコマンドを実行してファイルを自動生成します。

flutter pub run build_runner build

モデルを使うためのProviderを作成する。今回は全てのデータを取得。

provider/provider.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:strem_app/model/user.dart';

final usersStreamProvider = StreamProvider<List<User>>((ref) {
  final collection = FirebaseFirestore.instance.collection('users');
  // データ(Map型)を取得
  final stream = collection.snapshots().map(
        // CollectionのデータからUserクラスを生成する
        (e) => e.docs.map((e) => User.fromJson(e.data())).toList(),
      );
  return stream;
});

main.dartのページにList型で取得したUserモデルのデータをStreamProviderを使って画面に表示します。

main.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:strem_app/firebase_options.dart';
import 'package:strem_app/provider/provider.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(ProviderScope(child: const MyApp()));
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.cyan,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  
  
  Widget build(BuildContext context, WidgetRef ref) {
    // StreamProviderを読み取る(取得できる型は `AsyncValue<T>`)
    final users = ref.watch(usersStreamProvider);

    return Scaffold(
      appBar: AppBar(
        title: Text('StreamProviderを使う!'),
      ),
      // AsyncValue は `.when` を使ってハンドリングする
      body: users.when(
          // 処理中は `loading` で指定したWidgetが表示される
          loading: () => const CircularProgressIndicator(),
          // エラーが発生した場合に表示されるWidgetを指定
          error: (error, stack) => Text('Error: $error'),
          // 取得した `users` が `data` で使用できる
          data: (users) {
            return ListView.builder(
                itemCount: users.length,
                itemBuilder: (context, index) {
                  final user = users[index];
                  return ListTile(
                    title: Text(user.name),
                    subtitle: Text(user.email),
                  );
                });
          }),
    );
  }
}

スクリーンショット

やってみた感想

数ヶ月前は、RiverpodとFreezedを組み合わせてFirestoreの値を取得するのに苦労しました😱
以前から知っていた方法を理解して使えば短いコードで、List型のデータを取得して表示させることができるのを知り、便利だなと思いました!
RiverpodとFreezedが流行っている理由が分かりましたね😅

Discussion