【初心者向け】Flutter Providerパッケージの基本から応用まで

Flutter Providerとは?基本の理解から始めよう

この記事では、Providerの基本的な概念を紹介します。Flutterの「Provider」は、アプリケーション内の状態管理を簡単に行うためのパッケージです。

アプリケーションが大きくなると、データを効率よく管理し、異なるウィジェットにデータを共有する必要が出てきます。Providerは、このデータの提供者(Provider)と消費者(Consumer)を明確に分け、ウィジェットツリー全体でデータを管理・共有しやすくします。

Zapp

Zapp
とりあえずFlutterを始めてみたいという場合、Zappというサービスを使うことをおすすめします。

オンライン上でFlutterのプログラムを書くことがきるエディターです。最初から基本的な環境構築がされているため、すぐにFlutterを始めることができます。

Zapp

Providerパッケージのセットアップ方法と基本設定

ここでは、Providerパッケージの導入手順と、プロジェクトにセットアップする際の基本的な設定方法を解説します。

パッケージを導入する方法は、主に以下の2つがあります。

A. コマンドでパッケージを追加する

$ flutter pub add provider

B. pubspec.yamlファイルにパッケージを追加する

dependencies:
	provider: ^6.12.0

パッケージの依存関係を追加したら、下記のコマンドでパッケージをインストールします。

$ flutter pub get

パッケージが追加できたら、ChangeNotifierProviderやStreamProviderを使って、アプリ全体で状態管理するプログラムを書いていきます。コード例を使用しながら解説していきます。

ChangeNotifierで簡単に状態管理を実装する方法

FlutterのChangeNotifierクラスを使うと、状態が変更された際にリスナー(消費者)に通知する仕組みを簡単に実装できます。

ここでは、ChangeNotifierを使ってデータの変更を管理し、それをProviderでウィジェットに提供する方法を紹介します。また、notifyListeners()メソッドを使用して、データが変更されたときにウィジェットをリビルドする流れを解説します。

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

void main() {
  runApp(ChangeNotifierProvider(
      create: (_) => Counter(),
      child: MyApp(),
  ));
}

class Counter extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

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

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

	@override
	Widget build(BuildContext context) {
		Counter counter = context.watch<Counter>();

		return MaterialApp(
			title: 'Sample App',
			theme: ThemeData(
				colorSchemeSeed: Colors.indigo,
				useMaterial3: true,
				brightness: Brightness.light,
			),
			darkTheme: ThemeData(
				colorSchemeSeed: Colors.blue,
				useMaterial3: true,
				brightness: Brightness.dark,
			),
			home: Scaffold(
				appBar: AppBar(
					title: Text('Flutter Example App'),
				),
				body: Center(
					child: Column(
						mainAxisAlignment: MainAxisAlignment.center,
						children: <Widget>[
							const Text(
								'You have pushed the button this many times:',
							),
							Text(
								'${counter.count}',
								style: Theme.of(context).textTheme.headlineMedium,
							),
						],
					),
				),
				floatingActionButton: FloatingActionButton(
					onPressed: () => { counter.increment() },
					tooltip: 'Increment',
					child: const Icon(Icons.add),
				),
			),
			debugShowCheckedModeBanner: false,
		);
	}
}

実装すると以下の通りになります。

実装例

複数のProviderを組み合わせた応用テクニック

複雑なアプリケーションでは、複数のデータソースや状態を一度に管理する必要があります。

ここでは、MultiProviderやProxyProviderを使って、複数のProviderを組み合わせた高度な状態管理方法を紹介します。例えば、異なるProvider間で依存関係がある場合、それを効果的に管理するための実装例や、マルチレベルな状態管理をどのように行うかを具体的に解説します。

void main() {
	runApp(MultiProvider(
		providers: [
			ChangeNotifierProvider(create: (_) => Counter()),
			ChangeNotifierProvider(create: (_) => AdditionalProvider()),
		],
		child: const MyApp()
	));
}
ProxyProvider<UserService, GreetingService>(
	update: (
		BuildContext context,
		UserService userService,
		GreetingService greetingService
	) => GreetingService(userService: userService),
)

void main() {
	runApp(ProxyProvider0<Result>(
		update: (context, result) {
			final a = Provider.of<A>(context);
			final b = Provider.of<B>(context);
			return update(context, a, b, result);
		}
	))
}

開発効率を上げる!実践的なProviderの活用例

Providerを上手に活用することで、アプリのパフォーマンスを向上させることができます。例えば、SelectorやConsumerを適切に使うと、必要な部分のみリビルドすることができます。

状態の特定のプロパティにだけ依存するウィジェットを作成できます。これにより、変更が不要な部分のリビルドを避けられます。

Selectorの活用

Selectorを使って特定のプロパティに依存するUIのみリビルドし、余分なリビルドを減らすことができ、パフォーマンスを向上できます。

Selector<MyModel, String>(
  selector: (context, myModel) => myModel.someValue,
  builder: (context, someValue, child) {
    return Text(someValue);
  },
);

Consumerの活用

Consumerを使うことでChangeNotifierに依存するUIのみリビルドすルるとができます。リビルドする必要のない部分を分離することで、パフォーマンスを向上することができます。

Consumer<MyModel>(
  builder: (context, myModel, child) {
    return Column(
      children: [
        child!, // 再描画されない部分
        Text(myModel.someValue), // 再描画される部分
      ],
    );
  },
  child: Text('Static widget'),
);