Flutter連携はコンテナ所有を明確にする。
ProviderScope は内部生成コンテナを自動破棄し、外部注入コンテナは呼び出し側で破棄します。Consumer系APIは Riverpod 互換の書き味を保ちます。
ProviderScope の所有モデル
コンテナを誰が作るかで dispose 責務が変わります。
内部コンテナ
ProviderScope(child: ...) は自動 dispose
外部コンテナ
ProviderScope(container: c, ...) は c.dispose() が必要
Uncontrolled
UncontrolledProviderScope は dispose しない
注意
Widgetテストで外部注入コンテナを使う場合、未破棄のまま終了するとタイマー由来のリーク原因になります。
Consumer の使い分け
すべて WidgetRef を扱えます。UI構造に合わせて選びます。
アプリ起点
runApp(const ProviderScope(child: MyApp()));
用途
Consumer: 小さな領域だけリアクティブにしたい場合。
ConsumerWidget: build(context, ref) で完結する場合。
ConsumerStatefulWidget: ローカル状態と ref の両方が必要な場合。
例: ConsumerStatefulWidget
ローカルUI状態と Provider状態を同時に扱う典型例です。
home_page.dart
class HomePage extends ConsumerStatefulWidget {
const HomePage({super.key});
@override
ConsumerState<HomePage> createState() => _HomePageState();
}
class _HomePageState extends ConsumerState<HomePage> {
bool expanded = false;
@override
Widget build(BuildContext context) {
final user = ref.watch(currentUser);
return Column(
children: [
Text('$user'),
Switch(
value: expanded,
onChanged: (v) => setState(() => expanded = v),
),
],
);
}
}
Consumer 実装は更新通知をポストフレームへ送るため、build中の setState 競合を減らします。
再描画が必要な値だけ ref.watch し、副作用は build 外に分離します。
更新処理はコールバック内で ref.invoke / ref.refreshValue を実行します。