👏

Flutter でよく使われるパッケージ

2023/07/27に公開

はじめに

私は普段 Flutter を使用したアプリ開発をしております。
今回は Flutter 開発でよく使用される便利なパッケージを簡単に紹介していきます。
内容は初学者の方に向けたものなっていますので、すでに Flutter で開発をされている方には既知のものばかりだと思います!笑

それでは早速、各パッケージについてみていきましょう。

状態管理パッケージ

riverpod

https://pub.dev/packages/riverpod

Flutter といえば!というぐらい有名なパッケージですね。
状態管理ができるパッケージです。
**Provider をグローバルに定義して使用します。
記事執筆の2023年7月時点では下記の種類があります。

  • ✅ Provider
  • ✅ (Async)NotifierProvider
  • 👀 StateNotifierProvider
  • ✅ FutureProvider
  • ✅ StreamProvider
  • ✅ StateProvider
  • 👀 ChangeNotifierProvider
riverpod 使用例

簡単な例としてcounterProvider という int を保持する Provider を作成し、データの更新と表示を行います。
処理の流れは以下です。

  1. floatingActionButton が押される
  2. counterProvider の保持している値がインクリメントされる
  3. View が更新される
// プロバイダーをグローバルに定義する
final counterProvider = StateProvider<int>((ref) => 0);

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

  
  Widget build(BuildContext context, WidgetRef ref) {
    final counter = ref.watch(counterProvider);
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // Provider の値を更新
          ref.read(counterProvider.notifier).update((state) => state + 1);
        },
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

riverpod は状態管理だけでなく、Dependency injection(依存性の注入)ができ、テストする際にも役立ちます。
こちらのサイトで軽く触れられていますが、ProviderScopeoverrides を使用して DI を実現します。
https://pub.dev/documentation/flutter_riverpod/latest/flutter_riverpod/ProviderScope-class.html

コード生成系パッケージ

freezed

https://pub.dev/packages/freezed

便利なコードジェネレーターです。
アプリ内で使用する「モデル」クラスを定義する際に、freezed を使用して定義・生成することで簡単に以下の恩恵が受けられます。

  • イミュータブルなクラスを生成
  • オブジェクトのクローンを作成する copyWith メソッドを実装
  • シリアル化 / デシリアル化(toJson / fromJson)を実装

これらの機能を持ったクラスを普通に定義した場合数百行かかります。
下記の画像がイメージしやすく、左が普通に定義、右が freezed を使用して定義した例です。
一目瞭然で「モデル」クラスを定義するのが楽になります。

先ほど紹介した riverpod パッケージではイミュータブルな値の状態を保持します。
そのため copyWith メソッドが大変便利です。(必要不可欠レベルで)
詳しくは下記を確認してください。
https://docs-v2.riverpod.dev/docs/concepts/why_immutability

flutter_gen

https://pub.dev/packages/flutter_gen

リソースを管理するためのパッケージです。
主に、アセット(画像やフォント)や色などをコードで利用できるようにするパッケージです。

具体的には、設定ファイル (pubspec.yaml もしくは flutter_gen.yaml) を元に、リソースへの参照をコード生成することで、タイポの心配なくリソースを使用することができます。

flutter_gen 設定例

まずは、pubspec.yaml にアセットを追加します。

pubspec.yaml
flutter:
  assets:
    - assets/images/profile.jpg

次に、コマンドを実行します。

ターミナル
flutter pub get
flutter pub run flutter_gen:generate

これにより gen フォルダが生成され、その中にリソースへの参照が含まれたコードが作成されます。

flutter_gen 使用例

生成されたコードを使うと、アセットの使用が簡単になります。
例えば、以下のように画像を表示できます。

Widget build(BuildContext context) {
  // 使用方法1
  return Assets.images.profile.image();
}

Widget build(BuildContext context) {
  // 使用方法2
  return Image.asset(Assets.images.profile.path);
}

このようにしてアセットを参照することで、存在しないアセット名を参照するミスを防ぐことができます。

ローカルデータストレージパッケージ

shared_preferences

https://pub.dev/packages/shared_preferences

Flutter のキーバリューストレージのためのパッケージです。Android の SharedPreferences と iOS の NSUserDefaults にアクセスするためのラッパーとなっています。

このパッケージを使うと、一部のデータをデバイスに永続的に保存し、アプリを閉じてもそのデータを保持することができます。例えば、ユーザの設定や、ログイン情報などの永続化が必要な情報を保存するのに適しています。

ただし、大量のデータや複雑なデータ構造を保存するためには適しておらず、そのようなケースではデータベースを使用することを検討するべきです。

shared_preferences 使用例

この例では、counter の値を SharedPreferences に保存し、アプリを再起動した際もその値を保持しています。initState メソッド内で SharedPreferences を読み込み、counter の値を設定しています。そして、フローティングアクションボタンを押すたびに counter の値を増やし、それを SharedPreferences に保存しています。

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

class SharedPreferencesDemo extends StatefulWidget {
  SharedPreferencesDemo({Key? key}) : super(key: key);

  
  _SharedPreferencesDemoState createState() => _SharedPreferencesDemoState();
}

class _SharedPreferencesDemoState extends State<SharedPreferencesDemo> {
  int counter = 0;

  
  void initState() {
    super.initState();
    _loadCounter();
  }

  _loadCounter() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    setState(() {
      counter = (prefs.getInt('counter') ?? 0);
    });
  }

  _incrementCounter() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    counter++;
    prefs.setInt('counter', counter);
    setState(() {});
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(child: Text('Pressed $counter times.')),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

コード整理系パッケージ

import_sorter と import_path_converter

https://pub.dev/packages/import_sorter

https://pub.dev/packages/import_path_converter

今回は二つのパッケージを紹介します。
どちらも import 文を統一してくれるものです。
下記のような要望があれば使用するぐらいで絶対必要なものではないです。

  • 絶対パスと相対パスが混在していて気持ちが悪い。
  • プロジェクト内で統一したい

import_sorter は名前の通り import 文を並び替えてくれるパッケージです。

並び替えのビフォーアフター
並び替え前
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/physics.dart';
import 'package:flutter/painting.dart';
import 'package:intl/intl.dart';
import 'package:mdi/mdi.dart';
import 'package:provider/provider.dart';
import 'anotherFile.dart';
import 'package:example_app/anotherFile2.dart';
import 'dart:async';
import 'dart:io';
import 'dart:js';
並び替え後
// Dart imports:
import 'dart:async';
import 'dart:io';
import 'dart:js';

// Flutter imports:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:flutter/physics.dart';

// Package imports:
import 'package:intl/intl.dart';
import 'package:mdi/mdi.dart';
import 'package:provider/provider.dart';

// Project imports:
import 'package:example_app/anotherFile2.dart';
import 'anotherFile.dart';

import_path_converter は import 文を 相対パス <-> 絶対パス に自動変換するパッケージです。

import 分を絶対パスに変換
変換前
import 'common/constants.dart' ;
import '../app.dart';
並び替え後
import 'package:example/common/constants.dart';
import 'package:example/app.dart';

もしプロジェクトメンバーで使用している IDE が VSCode で統一されているのであれば、これらのパッケージを導入せず Lint ルールと VSCode の設定で同様のことが可能です。
詳しくはこちらの記事が参考になります。

https://zenn.dev/k9i/articles/34dab7d09b29a1

今回紹介した二つのパッケージはどちらもコマンドで動作します。
私は面倒くさがりなので lefthook を導入して、git commit 前に自動的にコマンドを実行してくれるように設定しています。
lefthook については記事の本筋とズレるのでこちらの記事に委譲します。
https://zenn.dev/10_tofu_01/articles/flutter_formatter_linter_lefthook

その他便利パッケージ

flutter_native_splash

https://pub.dev/packages/flutter_native_splash

アプリを起動する時に表示されるスプラッシュスクリーンを簡単に実装、カスタマイズできるパッケージです。
pubspec.yaml ファイルに設定を記述し、コマンドを実行するだけで、Android、iOS、Web、それぞれのスプラッシュスクリーンの実装が簡単にできます。

flutter_native_splash 使用例

pubspec.yaml ファイルにスプラッシュスクリーンの設定を記述します。

pubspec.yaml
flutter_native_splash:
  color: "#42a5f5"
  image: assets/images/splash.png

次に、コマンドを実行します。

ターミナル
flutter pub get
flutter pub run flutter_native_splash:create

たったのこれだけです!

flutter_launcher_icons

https://pub.dev/packages/flutter_launcher_icons

アプリのランチャーアイコンを簡単に生成できるパッケージです。
ランチャーアイコンとはアプリのアイコンのことで、タップするとアプリが起動するアイコンなので launcher icon と呼ばれています。

このパッケージを使用すると、一度に複数の解像度のアイコンを生成でき、アンドロイドとiOSの両方で使用できます。

flutter_launcher_icons 使用例

pubspec.yaml ファイルに flutter_launcher_icons の設定を記述します

pubspec.yaml
dev_dependencies:
  flutter_launcher_icons: "^0.9.2"

次に、pubspec.yaml に flutter_icons セクションを追加して、ランチャーアイコンを設定します:

pubspec.yaml
flutter_icons:
  android: "launcher_icon"
  ios: true
  image_path: "assets/icon/icon.png"

image_path で指定した画像がランチャーアイコンに設定されます。
次のコマンドを実行してランチャーアイコンを生成します:

ターミナル
flutter pub get
flutter pub run flutter_launcher_icons:main

これで、アプリのランチャーアイコンが指定した画像に置き換わります。

url_launcher

https://pub.dev/packages/url_launcher

パッケージ名の通り、URL を起動するためのパッケージです。
標準のブラウザ、メールアプリ、電話アプリなどさまざまな形で URL を起動することができます。
主な使用例としては、ウェブサイトを開いたり、メールの送信画面を開いたりする際に使用します。
その他にもカスタム URL スキームを使用して、他のアプリケーションを起動することも可能です。

url_launcher 使用例

例えば、ウェブブラウザでウェブページを開くには、次のように launch 関数を使用します。

import 'package:url_launcher/url_launcher.dart';

void _launchURL() async {
  const url = 'https://flutter.dev';
  if (await canLaunch(url)) {
    await launch(url);
  } else {
    throw 'Could not launch $url';
  }
}

同様に、メールアプリを開くには、次のように mailto: スキームを使います。

void _launchEmail() async {
  const email = 'mailto:example@example.com';
  if (await canLaunch(email)) {
    await launch(email);
  } else {
    throw 'Could not launch $email';
  }
}

これにより、宛先を example@example.com に指定した状態でメールアプリが開きます。

upgrader

https://pub.dev/packages/upgrader

ユーザーが古いアプリを使用している場合に、アップデートを促してくれるパッケージです。
簡単な実装で以下のことをしてくれます。

  1. ダイアログを表示
  2. アップデートの内容を伝える
  3. アプリストアに遷移させる
upgrader 使用例

UpgradeAlert を使用するだけで機能します。

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Upgrader Example',
      home: UpgradeAlert(
          child: Scaffold(
        appBar: AppBar(title: Text('Upgrader Example')),
        body: Center(child: Text('Checking...')),
      )),
    );
  }
}

in_app_review

https://pub.dev/packages/in_app_review

アプリ内でユーザーにレビューを依頼できるパッケージです。
Android の In-App Review と iOS の SKStoreReviewController にアクセスするためのラッパーとなっています。
こちらの画像を見ていただくと想像しやすいと思います。

Alt text

in_app_review 使用例
import 'package:in_app_review/in_app_review.dart';

final InAppReview inAppReview = InAppReview.instance;

if (await inAppReview.isAvailable()) {
    inAppReview.requestReview();
}

以下の点からこの API を頻繁に使用することは推奨されていません。

  • ポップアップが表示されなくなる
  • ユーザーの体験が悪くなる

詳しくは下記をご覧ください。
https://developer.apple.com/design/human-interface-guidelines/ios/system-capabilities/rateds-and-reviews/
https://developer.android.com/guide/playcore/in-app-review#when-to-request

webview_flutter

https://pub.dev/packages/webview_flutter

Android 及び iOS で WebView ウィジェットを提供するパッケージです。
WebView とはアプリ上にブラウザのような機能を持たせ、アプリ内で Web サイトを表示できるようにする機能のことです。
アプリから離脱せずに Web サイトを閲覧できるので UX が良いです!

webview_flutter 使用例
class WebViewExample extends StatefulWidget {
  const WebViewExample({super.key});

  
  State<WebViewExample> createState() => _WebViewExampleState();
}

class _WebViewExampleState extends State<WebViewExample> {
  late final WebViewController controller;

  
  void initState() {
    super.initState();

    controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..loadRequest(Uri.parse('https://flutter.dev'));
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Flutter Simple Example')),
      body: WebViewWidget(controller: controller),
    );
  }
}

まとめ

今回は Flutter でよく使用される便利なパッケージをごく一部紹介しました。

Flutter ではまだまだたくさんのパッケージがあります。
全部を網羅することは不可能ですが、pub.dev 内を空文字で検索し、並び替えを MOST LIKES に変更して上から見ていくと、よく使われている便利なパッケージがたくさん見つかって勉強になります。

以上です!

GitHubで編集を提案

Discussion