Flutter Providerを使った状態管理の基本

Flutter

はじめに

Flutterは、シンプルなUI構築ができる強力なフレームワークですが、複雑なアプリケーションを構築する際には「状態管理」が重要なテーマになります。状態管理とは、アプリケーションの状態(データやUIの状態)を管理し、必要に応じて変更を適用することです。

本記事では、Flutterでの状態管理を簡単かつ効率的に行うために使用されるライブラリ、Providerについて解説します。初心者向けに、Providerを使った状態管理の基礎的な概念と実装方法をステップバイステップで説明します。


Providerとは?

Providerは、Googleが公式に推奨するFlutterの状態管理ライブラリの一つで、アプリケーションの状態(データやビジネスロジック)をシンプルに管理できるようにするものです。InheritedWidgetをベースにしており、下記のような特徴を持ちます。

Providerの特徴

  • シンプルで使いやすい:公式サポートであり、ドキュメントも充実している。
  • リビルドの効率化:必要な部分だけをリビルドするため、パフォーマンスに優れる。
  • 柔軟な状態管理:グローバルな状態管理にも、局所的な状態管理にも対応可能。

Providerの導入手順

Providerのインストール

まず、ProviderをFlutterプロジェクトに追加する必要があります。pubspec.yamlファイルに以下を追加し、インストールを行います。

YAML
dependencies:
  flutter:
    sdk: flutter
  provider: ^6.0.0

その後、以下のコマンドでパッケージをインストールします。

Bash
flutter pub get

Providerを使った基本的な実装

ここでは、簡単なカウンターアプリを使ってProviderの使い方を説明します。このアプリでは、ボタンを押すたびにカウントが増加し、その状態をProviderで管理します。


Providerの基本的な使い方

モデルクラスの作成

まず、アプリケーションの状態を管理するためのモデルクラスを作成します。ここでは、カウントの値を管理するCounterModelクラスを作成します。

  • ChangeNotifierを継承していることで、状態が変更された際にUI側へ通知を送ることができます。
  • int get count => _count;
    • これはgetterメソッドです。外部のクラスやウィジェットが_countの値を取得できるようにしています。この方法で、_countに直接アクセスすることなく、安全にカウンターの値を取得できます。
  • notifyListeners()を呼び出すことで、Providerを介して依存しているウィジェットに再ビルドを促します。
Dart
import 'package:flutter/material.dart';

class CounterModel with ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners(); // 状態が変わったことを通知する
  }
}

Providerで状態を管理

次に、main.dartファイルでProviderを使ってCounterModelをアプリ全体に提供します。

    Dart
    import 'package:flutter/material.dart';
    import 'package:provider/provider.dart';
    import 'counter_model.dart'; // モデルクラスのインポート
    
    void main() {
      runApp(
        MultiProvider(
          providers: [
            ChangeNotifierProvider(create: (_) => CounterModel()),
          ],
          child: MyApp(),
        ),
      );
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: CounterScreen(),
        );
      }
    }
    

    ChangeNotifierProviderを使って、CounterModelをアプリ全体に提供します。これにより、アプリ内のどこでもCounterModelにアクセスし、状態を操作することができるようになります。

    UIに状態を反映する

    次に、UI部分でCounterModelの状態を取得し、UIに反映させます。

    • Provider.of<T>(context)
      • Providerを使用して、ツリー内のどこかで提供されたモデル(ここではCounterModel)を取得します。
      • Tには、取得したいデータ型(ここではCounterModel)が指定されます。
    Dart
    import 'package:flutter/material.dart';
    import 'package:provider/provider.dart';
    import 'counter_model.dart';
    
    class CounterScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final counter = Provider.of<CounterModel>(context);
    
        return Scaffold(
          appBar: AppBar(
            title: Text('Provider Counter App'),
          ),
          body: Center(
            child: Text(
              'Count: ${counter.count}',
              style: TextStyle(fontSize: 40),
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              counter.increment(); // カウントを増やす
            },
            child: Icon(Icons.add),
          ),
        );
      }
    }

    ここでは、Provider.of<CounterModel>(context)を使って、CounterModelの状態にアクセスしています。ボタンを押すとincrementメソッドが呼ばれ、カウントが増加し、notifyListenersによってUIが再ビルドされます。


    Providerの使い方を深める

    Consumerを使った状態の取得

    Provider.ofの代わりに、Consumerを使って状態を取得し、UIに反映させることもできます。Consumerは必要な箇所だけを再ビルドし、パフォーマンスを向上させます。

    • Consumer<CounterModel>は、Providerを利用してCounterModelの状態に依存しているウィジェットにデータを供給します。ここでは、Consumerを使って、CounterModelcountプロパティの変化を監視し、Textウィジェットにその値を表示しています。
    • builder: (context, counter, child)counterは、CounterModelのインスタンスです。このbuilder関数は、モデルの状態が変更されるたびに呼ばれます。
    Dart
    class CounterScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Provider Counter App'),
          ),
          body: Center(
            child: Consumer<CounterModel>(
              builder: (context, counter, child) {
                return Text(
                  'Count: ${counter.count}',
                  style: TextStyle(fontSize: 40),
                );
              },
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              Provider.of<CounterModel>(context, listen: false).increment();
            },
            child: Icon(Icons.add),
          ),
        );
      }
    }

    listen: false を使ってリビルドを防ぐ

    Provider.ofで状態を取得する際、listen: falseオプションを使うことで、そのウィジェットがリビルドされることを防ぐことができます。たとえば、FloatingActionButtonが押されたときにカウントが増えるだけで、ボタン自体はリビルドしないようにする場合です。


    Providerの応用:複数の状態を管理する

    Providerを使って、複数の状態を管理することも簡単です。たとえば、カウンターに加えて、ユーザー情報を管理するための別のモデルを追加できます。

    Dart
    import 'package:flutter/material.dart';
    
    class UserModel with ChangeNotifier {
      String _name = "Guest";
    
      String get name => _name;
    
      void updateName(String newName) {
        _name = newName;
        notifyListeners();
      }
    }

    複数のChangeNotifierProviderを使って、これらの状態をアプリ全体に提供します。

    Dart
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => CounterModel()),
        ChangeNotifierProvider(create: (_) => UserModel()),
      ],
      child: MyApp(),
    );
    

    これにより、異なる画面やウィジェットで異なる状態にアクセスし、それぞれを管理することができます。


    今回のサンプルコード

    lib/counter_model.dart

    Dart
    import 'package:flutter/material.dart';
    
    class CounterModel with ChangeNotifier {
      int _count = 0;
    
      int get count => _count;
    
      void increment() {
        _count++;
        notifyListeners(); // 状態が変わったことを通知する
      }
    }
    

    lib/main.dart

    Dart
    import 'package:flutter/material.dart';
    import 'package:provider/provider.dart';
    import 'counter_model.dart'; // モデルクラスのインポート
    
    void main() {
      runApp(
        MultiProvider(
          providers: [
            ChangeNotifierProvider(create: (_) => CounterModel()),
          ],
          child: MyApp(),
        ),
      );
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: CounterScreen(),
        );
      }
    }
    
    class CounterScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Provider Counter App'),
          ),
          body: Center(
            child: Consumer<CounterModel>(
              builder: (context, counter, child) {
                return Text(
                  'Count: ${counter.count}',
                  style: TextStyle(fontSize: 40),
                );
              },
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              Provider.of<CounterModel>(context, listen: false).increment();
            },
            child: Icon(Icons.add),
          ),
        );
      }
    }

    まとめ

    Providerは、Flutterでの状態管理を簡素化し、効率的に管理できる強力なツールです。この記事では、Providerの基本的な使い方や、ChangeNotifierを使った状態管理の実装、Consumerlisten: falseを使ったパフォーマンス改善の方法を紹介しました。

    この記事のポイント

    • Providerを使った状態管理の導入と基本操作
    • ChangeNotifierを使って状態の変更を通知し、UIを再ビルド
    • Consumerlisten: falseでパフォーマンス最適化

    初心者でも理解しやすいProviderの使い方を学んで、より複雑なFlutterアプリケーションの状態管理に役立ててください。今後は、さらに高度な状態管理パターン(例えば、RiverpodやReduxなど)にも挑戦してみてください!

    コメント

    タイトルとURLをコピーしました