Share
Embed
Transcript
22 slides
-
About Me Ryo Kuroki 株式会社Relic, 2023 6 月中途入社 Flutter 歴
業務では約3 ヶ月 個人開発で約1 年 IT エンジニア歴 約3 年 主にFE Relic では「coordimate 」というモバイルアプリサービスの FE 開発とPDM っぽいことをしています EXPERIENCE -
発表の対象者と目的 目的 Riverpod ver2.x についてこんな感じなんだ〜始められそう〜と知ってもらう 対象者 🙆 Riverpod は聞いたことあるが、まだ使ったことがない方(特にver2.x )
🙅♀️ Riverpod を熟知されている方、ver2.x もすぐに順応できる方 -
なぜプロバイダが必要なのか アプリの様々な場所からステートにアクセス できるようになる シングルトンや依存性注入、InheritedWidget などを分かりやすく代替することができる ステートを別のプロバイダのステートと簡単 に組み合わせる事が可能 複数のオブジェクトを組み合わせて 1 つのステートにまとめなくていい
テスト容易性を高める パフォーマンス最適化 同じ型のオブジェクトを公開するプロバイダ を 複数宣言できる final cityProvider = Provider((ref) => 'London'); final countryProvider = Provider((ref) => 'England'); -
v2.0 以降のProvider の種類 Provider 概要 NotifierProvider v2.0から登場。メインの機能 AsyncNotifierProvider v2.0から登場。FutureProviderではできない状態の変更が可能 Provider
これまで通り。Repositoryなどの状態を持たないケースで利用 StateProvider NotifierProviderより非常に単純な場合。レガシー? StateNotifierProvider NotifierProviderを推奨と明記されている FutureProvider AsyncNotifierProviderを使うほどでもない、状態の変更を伴わない単純なユースケース StreamProvider FutureProviderのStream版 ChangeNotifier 元々非推奨。ChangeNotifierProviderからの置き換えの手始めに最も簡単。 -
初期値の設定を わかりやすく実装しやすい ここで、 ref.watch(otherProvider) などと、 他のprovider を参照できる 状態を変更するメソッドを UI に公開する
code generation によって Notifier とNotifierProvider が生成され る @riverpod class Todos extends _$Todos { @override List<Todo> build() { return []; } void addTodo(Todo todo) { state = [...state, todo]; } } final todosProvider = NotifierProvider<TodosNotifier, List<Todo>>(() { return TodosNotifier(); }); -
// Riverpod 1系 final fetchUserProvider = FutureProvider.autoDispose.family<User, int>((ref, userId) async
{ final json = await http.get('api/user/$userId'); return User.fromJson(json); }); // Riverpod 2系 @riverpod Future<User> fetchUser(FetchUserRef ref, {required int userId, required int page}) async { final json = await http.get('api/user/$userId/$page'); return User.fromJson(json); } autoDispose がデフォルトでON になった ( 破棄せず保持したい時はkeepAlive:true と明示する) 引数を1 個しか設定できなかったのが、任意で追加できるようになった -
// Riverpod導入前はMultiProviderを利用していたので、削除 MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => AppUser()), ChangeNotifierProvider(create:
(_) => EtcProvider()) MultiProvider の部分を削除 自分の場合は、runApp の配下をProviderScope で囲んだ runApp( child: const ProviderScope( child: MyApp(), )) 導入前 導入後 -
// providerパッケージのChangeNotifierProviderを利用していた class AppUser extends ChangeNotifier { /// ユーザーIDの設定や、ユーザー名の変更メソッドなど }
とりあえずRiverpod のChangeNotifierProvider を利用して、コード変更量の少なさを重視 ※ChangeNotifierProvider が公式に非推奨であることと、その理由は理解する import 'package:flutter_riverpod/flutter_riverpod.dart'; // AppUserクラスは変更なし final appUserNotifierProvider = ChangeNotifierProvider<AppUserNotifier>((ref) { return AppUserNotifier(); }) 導入前 導入後 -
// class UserProfilePage extends StatefulWidget { class UserProfilePage extends ConsumerStatefulWidget
{ とりあえずStatefulWidget→ConsumerStatefulWidget に変更 ※Consumer,ConsumerWidget よりも再ビルドのコストが大きくなりそうなことを理解する // String userId = Provider.of<AppUser>(context).userId; String userId = ref.watch(appUserNotifierProvider).userId; // Provider.of<AppUser>(context, listen: false).setAppUser(id); ref.read(appUserNotifierProvider.notifier).setAppUser(id); ステートの値をUI 側で監視、変更する方法はあまり変わらない Riverpod1.0 を使っていた場合は特に変更なし -
import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'note_list_provider.g.dart'; @riverpod class NoteList extends _$NoteList {
@override // buildの引数に指定すると、generatorの方で認識される FutureOr<List<Note?>> build(String userId) async { return Future.value(_fetchNotes(userId)); } Future<List<Note?>> _fetchNotes(String userId) async { list = []; // Firestoreからの取得などの非同期処理でlistを更新 // AsyncValue.data()で非同期処理が成功した時のデータが取得できるので、状態を更新 state = AsyncValue.data(list); return list; } Future<List<ExchangeNote?>> reloadNotes(String userId) async { // AsyncLoading()を挟めば、Consumer側でLoading画面を表示できる state = const AsyncLoading(); await Future.delayed(const Duration(seconds: 1)); return _fetchNotes(userId); } } -
flutter pub run build_runner build ターミナルでコマンド実行すると、自動で認識してAsyncNotifier とそのProvider が生成される final myNotes
= ref.watch(noteListProvider(userId)) myNotes.when( loading: () => WidgetUtils().createProgressIndicator(), error: (error, stack) => Center( child: Text('エラーが発生しました\n$error'), ), data: (myNotes) { // List表示とか } UI 側での使い方はRiverpod1 系と変わらない FutureValue を監視して、Loading 、エラー時、成功時の値を直感的に実装できる -
信頼性の高い、最新の情報を キャッチアップし続ける 公式で基礎を理解する。 なぜそれを導入しなければいけないのか、 プロダクトの課題とそれに対する解決策が あっているのかを理解する。 「よく使われているもの」に惑わされない。 スピードと品質のバランスをとる 新規事業は常にシビアな時間との勝負。 だからこそ変更容易性を将来も担保できるよう
に、技術的負債を如何に蓄積させないか、新しい 便利な技術に、既存システムをどう順応させてい くかも考えなくてはならない。