🐬

Flutter×Riverpodで通知の許可を毎回チェックし、設定画面に飛ばす方法

2024/10/08に公開

Flutterアプリ内で通知が許可されているかどうかを確認して、されていない場合デフォルトの設定アプリに飛ばす方法を紹介します

Flutterにおける通知許可(Permission_handler)の取得と設定方法

flutter_permission_handlerは、Flutterアプリで様々な権限(カメラ、位置情報、通知など)を管理するためのパッケージです。これを利用することで、ユーザーに権限をリクエストし、アプリが正しい権限を持っているかを確認することができます。
https://pub.dev/packages/permission_handler

1. パッケージのインストール

まず、flutter_permission_handlerパッケージをプロジェクトに追加します。

flutter pub add permission_handler

2. iOSでの設定

2.1 Info.plistの編集

iOSでは、使用する権限に対応するキーをios/Runner/Info.plistに追加する必要があります。通知権限をリクエストする場合は、以下のように記述します。

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>
<key>UIBackgroundModes</key>
<array>
    <string>fetch</string>
    <string>remote-notification</string>
</array>
//この下二行を追加
<key>NSUserTrackingUsageDescription</key>
<string>このアプリは、通知を送信するために通知の権限を必要とします。</string>

2.2 Podfileの編集

ios/Podfileに以下のコードを追加

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
    target.build_configurations.each do |config|
      config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
        '$(inherited)',
        'PERMISSION_NOTIFICATIONS=1',  # 通知の権限を使用する場合
      ]
    end
  end
end

3. Androidでの設定

3.1 AndroidManifest.xmlの編集

Androidでは、app/src/main/AndroidManifest.xmlに権限を追加します。通知権限をリクエストする場合は、以下のように記述します。

<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

ターゲットSDKバージョンを設定します。Android 13以降では、ユーザーに通知権限をリクエストする必要があるため、ターゲットSDKバージョン33を指定します。
android/app/build.gradle

android {
  compileSdkVersion 33
  ...
}

android/gradle.propertiesに以下を記載

android.useAndroidX=true
android.enableJetifier=true

xml

4. 通知許可のリクエストと許可の確認

通知許可のリクエスト

import 'package:permission_handler/permission_handler.dart';

void requestNotificationPermission() async {
  PermissionStatus permissionStatus = await Permission.notification.status;
  if (permissionStatus.isDenied) {
    Permission.notification.request();
  }
}

許可の確認

void checkNotificationPermission() async {
  PermissionStatus permissionStatus = await Permission.notification.status;
  if (permissionStatus.isGranted) {
    // 通知権限が許可されています
  } else if (permissionStatus.isDenied) {
    // 通知権限が拒否されています
  }
}

アプリのライフサイクルを監視方法

Flutterでは、アプリのライフサイクルを監視することで、アプリの状態がどのタイミングで変わったかを把握することができます。これを活用すると、例えば、アプリがバックグラウンドに移行した際や、再開された際に適切な処理を行うことが可能です。

今回は、通知の許可状態をリアルタイムで監視するために、アプリのライフサイクルを監視し、Widgetを開くたびに権限の状態を確認する方法を紹介します。Flutterにはアプリケーションの実行状態を示すAppLifecycleStateが存在し、それを利用してアプリのライフサイクルの変更を検出できます。
この記事では、RiverpodのProviderを使用してアプリのライフサイクルを監視する方法について解説します。

ライフサイクルの変更を監視するコード

まずは、ライフサイクルの状態を監視するためのProviderを定義します。このコードでは、FlutterのWidgetsBindingObserverを使用してライフサイクルの変更を検出し、RiverpodのProviderを通じて状態を管理しています。

import 'package:flutter/widgets.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'app_lifecycle_state_provider.g.dart';

@Riverpod(keepAlive: true)
AppLifecycleState appLifecycleState(AppLifecycleStateRef ref) {
  final observer = _AppLifecycleObserver((value) => ref.state = value);

  final binding = WidgetsBinding.instance..addObserver(observer);
  ref.onDispose(() => binding.removeObserver(observer));

  return AppLifecycleState.resumed;
}

class _AppLifecycleObserver extends WidgetsBindingObserver {
  _AppLifecycleObserver(this._didChangeState);

  final ValueChanged<AppLifecycleState> _didChangeState;

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    _didChangeState(state);
    super.didChangeAppLifecycleState(state);
  }
}

extension AppLifecycleStateExtension on AppLifecycleState {
  bool get isResumed => this == AppLifecycleState.resumed;
}

Widget単位でのライフサイクル監視

次に、実際にウィジェット内でライフサイクルの状態を監視する方法です。以下のコードを使うことで、アプリのライフサイクルが変わるたびにコンソールにログを出力するようにしています。

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // appLifecycleProviderの状態変更を監視
    ref.listen<AppLifecycleState>(
        appLifecycleProvider,
        (previous, next) => debugPrint('Previous: $previous, Next: $next'),
      );
    return const xxxx();
  }

参考記事
ライフサイクル監視に関してより詳しく知りたい場合は、以下の記事も参考にしてみてください。

アプリのライフサイクルを監視するProvider

ライフサイクルを利用して通知状態を監視する

以下のコードでは、Riverpodを使用してアプリのライフサイクルを監視し、通知権限を確認しています。通知が許可されていない場合、ユーザーに設定アプリへのリンクを表示します。

class Sample extends HookConsumerWidget {
  const Sample({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // useStateはbuildメソッド内で定義する
    final checkNotification = useState(false);

    // アプリのライフサイクルを監視
    ref.listen<AppLifecycleState>(appLifecycleStateProvider, (previous, next) {
      if (next == AppLifecycleState.resumed) {
        // フォアグラウンドになったら通知権限を確認
        checkNotificationPermission(checkNotification);
      }
    });

    // useEffectで初期化時に通知をリクエストし、権限を確認
    useEffect(() {
      // 通知をリクエスト
      requestNotificationPermission();
      // 通知権限を確認
      checkNotificationPermission(checkNotification);
      return null;
    }, []);

    return Scaffold(
      appBar: AppBar(
        title: const Text("サンプル"),
      ),
      body: checkNotification.value
          ? const Text("通知が許可されています")
          : Column(
              children: [
                const Text("通知が許可されていません"),
                TextButton(
                  onPressed: () {
                    // 設定アプリへとぶ
                    openAppSettings();
                  },
                  child: const Text("設定へ"),
                ),
              ],
            ),
    );
  }

  // 通知権限をリクエスト
  Future<void> requestNotificationPermission() async {
    PermissionStatus permissionStatus = await Permission.notification.status;
    if (permissionStatus.isDenied) {
      await Permission.notification.request();
    }
  }

  // 通知権限を確認
  Future<void> checkNotificationPermission(ValueNotifier<bool> checkNotification) async {
    PermissionStatus permissionStatus = await Permission.notification.status;
    if (permissionStatus.isGranted) {
      checkNotification.value = true;
    } else if (permissionStatus.isDenied) {
      checkNotification.value = false;
    }
  }
}

コードの流れ

1.通知の状態管理: useStatecheckNotificationを使って通知が許可されているかを管理します。
2.ライフサイクルの監視: アプリがフォアグラウンドに戻った時(AppLifecycleState.resumed)、通知権限を確認します。
3.初期化時の権限リクエスト: useEffect内で初期化時に通知の許可をリクエストし、状態を確認します。
4.設定アプリへの誘導: 通知が許可されていない場合、ユーザーに設定アプリへ移動するためのボタンを表示します。

まとめ・感想

最後まで読んでいただきありがとうございました。
今回の実装で、Flutterアプリ内で通知権限をシンプルに管理できる方法がうまく整ったと感じています。Riverpodを使ってリアルタイムに権限状態を監視し、通知が未許可の場合でも設定アプリに自然に誘導できる点が便利です。通知権限の確認からリクエストまで一連の流れで管理できるため、今後の開発にも役立ちそうです。

Discussion