StreamNotifierを使ってみた
Overview
riverpod2.0には、StreamNotifierなるものがあるらしい?
AsyncNotifierだと、FutureOrにデータ型がなるから、Streamのデータを返せない?
ということは、こちらを使う必要がありそう...
ストリームを作成するビルドを備えた AsyncNotifier のバリアント。
これは、時間の経過とともに値を変更できる StreamProvider と考えることができます。
このプロバイダーを使用するための構文は、プロバイダーの関数が「ref」を受け取らない (ファミリーの場合は引数も受け取らない) という点で、他のプロバイダーとは少し異なります。 代わりに、関連する AsyncNotifier で参照 (および引数) に直接アクセスできます。
これは、時間の経過とともに値を変更できる StreamProvider と考えることができます。 autoDispose またはファミリーを使用すると、通知タイプが変わります。 StreamNotifier を拡張する代わりに、次のいずれかを拡張する必要があります。
- AutoDisposeStreamNotifier for autoDispose
- FamilyStreamNotifier for family
- AutoDisposeFamilyStreamNotifier for autoDispose.family
summary
とりあえずやってみよう!
ViewModelとは言えないが、Streamを返すNotifierを作ってみる。やることは単縦で、StreamでFirestoreのデータを全て取得して、リアルタイムに反映してもらう。
StreamNotifier
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'stream_view_model.g.dart';
class StreamViewModel extends _$StreamViewModel {
Stream<QuerySnapshot<Map<String, dynamic>>> build() {
return getStream();
}
Stream<QuerySnapshot<Map<String, dynamic>>> getStream(){
final snapshot = FirebaseFirestore.instance.collection('user').snapshots();
return snapshot;
}
}
View側には、いつも通りの書き方で表示します。
View
データを全て表示
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:isige_app/stream_view_model.dart';
class StreamView extends ConsumerWidget {
const StreamView({super.key});
Widget build(BuildContext context, WidgetRef ref) {
// AsyncValue<QuerySnapshot<Map<String, dynamic>>>でデータが返ってくる
final streamValue = ref.watch(streamViewModelProvider);
return Scaffold(
appBar: AppBar(
title: const Text('StreamView'),
),
body: Center(
child: streamValue.when(
data: (data) {
return ListView.builder(
itemCount: data.docs.length,
itemBuilder: (context, index) {
final docData = data.docs[index].data();
final name = docData['name'];
final email = docData['email'];
return ListTile(
title: Text(name),
subtitle: Text(email),
);
},
);
},
loading: () => const CircularProgressIndicator(),
error: (error, stackTrace) => Text(error.toString()),
),
),
);
}
}
main.dartでimportして実行する
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:isige_app/firebase_options.dart';
import 'package:isige_app/stream_view.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
home: StreamView(),
);
}
}
データの取得に成功すればこんな感じで表示できます:
thoughts
今回は、StreamNotifierなるものを使って見ました。今の所使っている人たちは見かけないような??
riverpod3.0からは、whenではなくてswitch式で、コードを書くらしく試してみました。時々使うんですけど、失敗することがあって何が原因かはまだわからないですね。今回は成功してるポイです。
おまけ:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:isige_app/stream_view_model.dart';
class StreamView extends ConsumerWidget {
const StreamView({super.key});
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.green,
title: const Text('Switch Stream'),
),
body: const UserStream());
}}
class UserStream extends ConsumerWidget {
const UserStream({super.key});
Widget build(BuildContext context, WidgetRef ref) {
final streamValue = ref.watch(streamViewModelProvider);
switch (streamValue) {
case AsyncData(:final value):
final data = value;
return ListView.builder(
itemCount: data.docs.length,
itemBuilder: (context, index) {
final docData = data.docs[index].data();
final name = docData['name'];
final email = docData['email'];
return ListTile(
title: Text(name),
subtitle: Text(email),
);
},
);
case AsyncError(:final error):
// エラーメッセージを表示するなど、エラー時の処理を書く
return Text('An error occurred: $error');
default:
// データがロード中の場合や、その他のケースで表示するWidgetを返す
return const CircularProgressIndicator();
}
}
}
ちゃんと表示できてるでしよう:
Discussion