Flutter: Bloc/Cubit、Provider、Riverpod の状態管理サンプル(カウンターアプリ)

Flutter: Bloc/Cubit、Provider、Riverpod の状態管理サンプル(カウンターアプリ)

August 11, 2024

Flutter の状態管理ライブラリの3トップ(個人的見解)である「Bloc/Cubit」「Provider」「Riverpod」について、カウンターアプリでのコード例を見ていきましょう。

Bloc/Cubit #

使用ライブラリ:https://pub.dev/packages/flutter_bloc

以下の例では Cubit の方を使用しています。

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => MyCounterState(),
      child: const MaterialApp(
        home: Scaffold(
          body: MyWidget(),
        ),
      ),
    );
  }
}

class MyWidget extends StatelessWidget {
  const MyWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(context.watch<MyCounterState>().state.toString()),
        IconButton(
          onPressed: context.read<MyCounterState>().increment,
          icon: const Icon(Icons.add),
        ),
      ],
    );
  }
}

class MyCounterState extends Cubit<int> {
  MyCounterState() : super(0);

  void increment() {
    emit(state + 1);
  }
}

Provider (with ChangeNotifier) #

使用ライブラリ:https://pub.dev/packages/provider

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => MyCounterState(),
      child: const MaterialApp(
        home: Scaffold(
          body: MyWidget(),
        ),
      ),
    );
  }
}

class MyWidget extends StatelessWidget {
  const MyWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(context.watch<MyCounterState>().count.toString()),
        IconButton(
          onPressed: context.read<MyCounterState>().increment,
          icon: const Icon(Icons.add),
        ),
      ],
    );
  }
}

class MyCounterState extends ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

Riverpod (with StateNotifier) #

使用ライブラリ:https://pub.dev/packages/flutter_riverpod

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const ProviderScope(
      child: MaterialApp(
        home: Scaffold(
          body: MyWidget(),
        ),
      ),
    );
  }
}

class MyWidget extends ConsumerWidget {
  const MyWidget({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Column(
      children: [
        Text(ref.watch(myCounterStateProvider).toString()),
        IconButton(
          onPressed: ref.read(myCounterStateProvider.notifier).increment,
          icon: const Icon(Icons.add),
        ),
      ],
    );
  }
}

final myCounterStateProvider =
    StateNotifierProvider<MyCounterState, int>((ref) => MyCounterState());

class MyCounterState extends StateNotifier<int> {
  MyCounterState() : super(0);

  void increment() {
    state++;
  }
}

補足:Provider や Riverpod は状態管理ライブラリではない #

Provider や Riverpod それ自体はステートを管理するものではなく、依存関係を注入するためのライブラリであり、InheritedWidget を使いやすくしたものと言えるでしょう。

Provider であれば ChangeNotifier と組み合わせで、Riverpod であれば StateNotifier と組み合わせで利用されることが一般的なようですが、この ChangeNotifier や StateNotifier が状態を管理しています。ただ、一般的に Provider や Riverpod は状態管理ライブラリという括りのなかで話題に挙がることが多いため、今回は状態管理ライブラリとしてリストアップしました。

実用例は多くないと思われますが、次のように Riverpod と ChangeNotifier の組み合わせで使うとことも可能です。

Riverpod (with ChangeNotifier)

final myCounterStateProvider =
    ChangeNotifierProvider<MyCounterState>((ref) => MyCounterState());

class MyCounterState extends ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}