はじめに – Providerって何?なぜ必要なの?

Flutterでアプリを作っていると、「データの受け渡しが面倒だな」と感じたことはありませんか?
例えば、ログイン情報やカート内の商品数など、アプリ全体で使いたいデータがあるとき、親から子へ、子から孫へと延々とデータを渡していくのは大変ですよね。
Providerは、そんな悩みを解決してくれる状態管理パッケージです。Googleも公式で推奨しており、Flutterの状態管理では最も人気のある選択肢の一つなんですよ。
Providerが解決してくれる問題
まず、setStateを使った従来の方法には、こんな課題がありました。
従来の方法(setState)の問題点:
- アプリ全体が再描画されてしまう
- 親から子へデータを何度も渡す必要がある
- コードが複雑になりやすい
- パフォーマンスに影響が出る
Providerを使うと、これらの問題がスッキリ解決します。必要な部分だけを更新できるので、アプリの動作も速くなるんです。
Providerの仕組みを理解しよう
Providerは、InheritedWidgetというFlutterの仕組みをラッパー(包み紙)で包んだものです。
簡単に言うと、「データの保管庫を作って、そこから必要なデータを取り出す」というイメージですね。
Providerの3つの主要な要素
Providerを使うには、3つの重要な要素を覚えておけば大丈夫です。
1. ChangeNotifier(変更を通知するクラス)
データを管理するクラスで、データが変わったことを知らせる役割を持っています。
2. ChangeNotifierProvider(データを提供するウィジェット)
アプリ全体にデータを届けるためのウィジェットです。
3. Consumer または context.watch(データを受け取る方法)
実際にデータを使いたい場所で、データを取り出す方法になります。
実際に使ってみよう!手順を詳しく解説
それでは、実際にProviderを使ったカウンターアプリを作りながら、使い方を学んでいきましょう。
ステップ1:Providerパッケージを追加する
まず、pubspec.yamlファイルにProviderパッケージを追加します。
dependencies:
flutter:
sdk: flutter
provider: ^6.0.0
保存すると自動的にインストールされます。もし自動でインストールされない場合は、ターミナルで以下のコマンドを実行してください。
flutter pub get
ステップ2:データを管理するモデルクラスを作る
次に、カウンターの数値を管理するクラスを作ります。
import 'package:flutter/foundation.dart';
class CounterModel extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // 変更をリスナーに通知
}
}
ここがポイント!
ChangeNotifierを継承することで、変更を通知できるようになりますnotifyListeners()を呼ぶと、このデータを使っているウィジェットに「データが変わったよ!」と知らせてくれます- プライベート変数(
_count)とゲッター(get count)を使うことで、外部から直接変更できないようにしています
ステップ3:ChangeNotifierProviderでアプリをラップする
作ったモデルクラスをアプリ全体で使えるようにします。
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => CounterModel(),
child: MyApp(),
),
);
}
ここがポイント!
ChangeNotifierProviderでMyAppをラップすることで、アプリ全体でCounterModelが使えるようになりますcreateパラメータで、新しいCounterModelのインスタンスを作成しています
ステップ4:データを表示する(Consumer を使う方法)
カウンターの値を画面に表示してみましょう。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Provider サンプル'),
),
body: Center(
child: Consumer<CounterModel>(
builder: (context, counter, child) {
return Text(
'カウント: ${counter.count}',
style: TextStyle(fontSize: 24),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// この部分は次のステップで説明します
},
child: Icon(Icons.add),
),
),
);
}
}
ここがポイント!
Consumer<CounterModel>を使って、CounterModelのデータを受け取っていますbuilder関数の中で、counter.countにアクセスできます- データが変わると、この
Consumerの部分だけが自動的に再描画されます
ステップ5:データを更新する(Provider.of を使う方法)
ボタンを押したときにカウンターを増やす処理を追加します。
floatingActionButton: FloatingActionButton(
onPressed: () {
Provider.of<CounterModel>(context, listen: false).increment();
},
child: Icon(Icons.add),
),
ここがポイント!
Provider.of<CounterModel>(context, listen: false)で、CounterModelのインスタンスを取得していますlisten: falseを指定すると、データの変更を監視しません(ボタンを押すだけなので、再描画の必要がないため)listen: trueにすると、データが変わるたびにこの部分も再描画されます
もっと便利な使い方:context.watch と context.read

最近のProviderでは、context.watchとcontext.readという便利な書き方ができます。
context.watch – データの変更を監視する
Widget build(BuildContext context) {
final counter = context.watch<CounterModel>();
return Text('カウント: ${counter.count}');
}
Consumerを使うのと同じ効果がありますが、より簡潔に書けます。データが変わると、この部分が再描画されます。
context.read – データを取得するだけ(監視しない)
onPressed: () {
context.read<CounterModel>().increment();
}
Provider.of(context, listen: false)と同じ意味ですが、こちらの方が読みやすいですね。
複数のProviderを使いたい場合:MultiProvider
アプリが大きくなると、複数のデータを管理したくなります。そんなときはMultiProviderを使いましょう。
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => CounterModel()),
ChangeNotifierProvider(create: (context) => UserModel()),
ChangeNotifierProvider(create: (context) => CartModel()),
],
child: MyApp(),
),
);
}
これで、複数のデータソースをスッキリ管理できます。
よくある間違いと注意点
1. listen パラメータの指定を忘れる
Provider.ofを使うときは、listenパラメータを必ず指定しましょう。
// ❌ これはNG - buildメソッド内で値を更新すると無限ループになる
Provider.of<CounterModel>(context).increment();
// ✅ これが正解
Provider.of<CounterModel>(context, listen: false).increment();
2. Providerの配置場所を間違える
Providerは、それを使いたいウィジェットよりも上位に配置する必要があります。
// ❌ これはNG
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// ここでProviderを使おうとしてもエラーになる
final counter = context.watch<CounterModel>();
return ChangeNotifierProvider(
create: (context) => CounterModel(),
child: Text('${counter.count}'), // エラー!
);
}
}
// ✅ これが正解
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => CounterModel(),
child: MyApp(), // この下でProviderが使える
),
);
}
3. notifyListeners() の呼び忘れ
データを変更したら、必ずnotifyListeners()を呼びましょう。これを忘れると、画面が更新されません。
// ❌ これはNG
void increment() {
_count++;
// notifyListeners()を忘れている!
}
// ✅ これが正解
void increment() {
_count++;
notifyListeners(); // 必ず呼ぶ
}
Providerのメリットをまとめると
- シンプルで分かりやすい – 学習コストが低く、初心者でも使いやすい
- 効率的な再描画 – 必要な部分だけを更新するので、パフォーマンスが良い
- コードの整理がしやすい – データとUIを分離できる
- Googleが推奨 – 公式でも勧められている安心のパッケージ
- 豊富な情報 – ドキュメントやチュートリアルが充実している
さらに学びたい方へ:次のステップ
Providerの基本を理解したら、次はこんなことにチャレンジしてみましょう。
- FutureProvider / StreamProvider – 非同期処理を扱う
- ProxyProvider – 複数のProviderを組み合わせる
- Riverpod – Providerの改良版(より強力で柔軟)
特にRiverpodは、Providerの作者が「Providerの課題を解決するため」に開発した次世代の状態管理ライブラリです。Providerに慣れたら、ぜひ挑戦してみてください。
まとめ
Providerは、Flutterアプリの状態管理を簡単にしてくれる強力なツールです。
今回学んだポイント:
- Providerは、アプリ全体でデータを共有する仕組み
- ChangeNotifierでデータを管理する
- ChangeNotifierProviderでデータを提供する
- Consumerやcontext.watchでデータを使う
- context.readでデータを更新する
最初は少し難しく感じるかもしれませんが、実際に手を動かして試してみると、すぐに慣れますよ。
小さなアプリから始めて、徐々に複雑なアプリにチャレンジしていきましょう!
Providerを使いこなせるようになれば、Flutterアプリ開発がもっと楽しくなるはずです。ぜひ試してみてくださいね。

コメント