はじめに
Providerは、Flutterでの状態管理に使われる最もポピュラーなパッケージのひとつです。
RiverpodはこのProviderの改良版で、例えば以下のような点がProviderと違います。
- Provider使用時に出くわしがちな
ProviderNotFoundException
をコンパイル時に検査する - Flutterのフレームワークに依存せずBuildContextなしにProviderをlistenできる
- FlutterのDevToolをデバッグに活用できる
StateNotifierはValueNotifierのパフォーマンスやユーティリティに関して改良を加えたパッケージで、Providerとよく併用されます(Riverpodには同梱されています)。
LocatorMixinは、そのStateNotifierパッケージに含まれるMixinです。
StateNotifier継承クラス(HogeNotifierやHogeControllerなど)において、providerのProviderによってcontextに流されている外部サービスにアクセスする際に、Provider.of/context.read/context.watchを使用したくなると思います。
LocatorMixinをHogeNotifierにmixinすることで、これらのアクセスが簡単に実現できるようになります。具体的には、read/updateをHogeNotifier内で使用することができるようになります。
RiverpodでLocatorMixinを使用すると...
RiverpodでLocatorMixinを使用した場合、コンパイルは通り例外も特にエラーもスローされませんが、LocatorMixinの各メソッド(initState, updateなど)が呼ばれません。
例えば以下の例ではinitState
だけが呼ばれず、それ以外は普通に動作します。
final homeStateProvider = StateNotifierProvider((ref) => HomeStateNotifier());
class HomeStateNotifier extends StateNotifier<HomeState> with LocatorMixin {
HomeStateNotifier() : super(const HomeState());
@override
void initState() {
super.initState();
logs('HomeStateNotifier init state');
_loadTrending();
_loadCategory();
_loadPromotion();
_loadNewServices();
}
class TrendingVendor extends HookWidget {
@override
Widget build(BuildContext context) {
final result = useProvider(
homeStateProvider.state.select((value) => value?.trendingResult));
if (result == null) {
return const CircleLoading();
}
if (result.isFail) {
return FailWidget(result.message);
}
LocatorMixinではなくProviderReferenceを使う
LocatorMixinのread
, watch
ように別のProviderで提供されているインスタンスにアクセスしたい場合は、RiverpodのProviderReferenceを使うと便利です。
final userRepositoryProvider = Provider((ref) => UserRepository());
final userControllerProvider = StateNotifierProvider((ref) {
return UserController(
// userRepositoryProviderからUserRepositoryを取得してUserControllerを作成
repository: ref.watch(userRepositoryProvider),
);
});
final myLocalServiceProvider = StateProvider((ref) => MyLocalService());
final myStateNotifierProvider = StateNotifierProvider((ref) {
final notifier = MyStateNotifier<();
final removeListener = ref.watch(myLocalServiceProvider).addListener((state) {
//MyLocalServiceが更新された
notifier.onUpdateMyLocalService(state);
});
ref.onDispose(removeListener);
return notifier;
});
また、ProviderReferenceをリポジトリに渡すとよりLocatorMixinライクに書くことができます。
final myLocalServiceProvider = Provider((ref) => MyLocalService());
final myChangeNotifierProvider = StateNotifierProvider((ref) => MyStateNotifier(ref));
class MyStateNotifier extends StateNotifier {
MyStateNotifier(this.ref);
final ProviderReference ref;
int count = 43;
void add() {
count++;
notifyListeners();
ref.read(myLocalServiceProvider ).saveLocal(count);
}
void subtract() {
count--;
notifyListeners();
ref.read(myLocalServiceProvider ).saveLocal(count);
}
}