カメラ機能においてのpermission_handlerを理解する

![スクリーンショット 2024-06-03 20.58.32（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/d7b95f22-487b-a8e1-72d7-9e9bc7999eb9.png)

久しぶりの投稿です。
色々あって疲れていますが、なんとか余裕ができたので記事を書いていきます。なぜなら温泉に入って元気いっぱいだったからです🙌


# やりたいこと
- カメラ権限を許可するかどうかによって画面出し分けをしたい
- 初期状態はローディング画面を表示させておきたい


# 必要なセットアップ

- `flutter_hooks`・・・カメラ権限周りの状態管理
- `permission_handler`・・・カメラ権限に必要なPackages


```yaml
 dependencies:
  flutter_hooks: ^0.20.5
  permission_handler: ^11.3.1
```

### iOSのセットアップ

```: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)',
	
        ## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]
        'PERMISSION_LOCATION=1',
	
	## dart: PermissionGroup.microphone
        'PERMISSION_MICROPHONE=1',
	
	## dart: PermissionGroup.camera
        'PERMISSION_CAMERA=1'
	
	## dart: PermissionGroup.photos
        'PERMISSION_PHOTOS=1',
      ]
    end
  end
end
```

```plist:ios/Runner/Info.plist    
<key>NSCameraUsageDescription</key>
<string>camera</string>
```
---------------------------------------

### Androidのセットアップ

```properties: gradle.properties
android.useAndroidX=true
android.enableJetifier=true
```

```gradle:android/app/build.gradle
android {
  compileSdkVersion 33
  ...
}
```

```xml:android/app/src/main/AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA" />
```

---------------------------------------

# 実装までの道のり



### AppLifecycleListenerを定義
こちらは株式会社ゆめみで作成しているflutter-mobile-project-templateから引用しました。
詳しくはこちらを閲覧いただけると幸いです。

https://github.com/yumemi-inc/flutter-mobile-project-template

```dart
import 'dart:ui';

import 'package:flutter/material.dart';

/// A widget that listens to the lifecycle of the app.
/// Surround the widget you want to listen to with [CustomAppLifecycleListener].
class CustomAppLifecycleListener extends StatefulWidget {
  const CustomAppLifecycleListener({
    required Widget child,
    VoidCallback? onResume,
    VoidCallback? onInactive,
    VoidCallback? onHide,
    VoidCallback? onShow,
    VoidCallback? onPause,
    VoidCallback? onRestart,
    VoidCallback? onDetach,
    Future<AppExitResponse> Function()? onExitRequested,
    void Function(AppLifecycleState state)? onStateChange,
    super.key,
  })  : _child = child,
        _onResume = onResume,
        _onInactive = onInactive,
        _onHide = onHide,
        _onShow = onShow,
        _onPause = onPause,
        _onRestart = onRestart,
        _onDetach = onDetach,
        _onExitRequested = onExitRequested,
        _onStateChange = onStateChange;

  final Widget _child;
  final VoidCallback? _onResume;
  final VoidCallback? _onInactive;
  final VoidCallback? _onHide;
  final VoidCallback? _onShow;
  final VoidCallback? _onPause;
  final VoidCallback? _onRestart;
  final VoidCallback? _onDetach;
  final Future<AppExitResponse> Function()? _onExitRequested;
  final void Function(AppLifecycleState state)? _onStateChange;

  @override
  State<CustomAppLifecycleListener> createState() =>
      _CustomAppLifecycleListenerState();
}

class _CustomAppLifecycleListenerState
    extends State<CustomAppLifecycleListener> {
  late final AppLifecycleListener _appLifecycleListener;

  @override
  void initState() {
    _appLifecycleListener = AppLifecycleListener(
      onResume: widget._onResume,
      onInactive: widget._onInactive,
      onHide: widget._onHide,
      onShow: widget._onShow,
      onPause: widget._onPause,
      onRestart: widget._onRestart,
      onDetach: widget._onDetach,
      onExitRequested: widget._onExitRequested,
      onStateChange: widget._onStateChange,
    );
    super.initState();
  }

  @override
  void dispose() {
    _appLifecycleListener.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return widget._child;
  }
}
```


### 実装

```dart
import 'package:cores_core/util.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:permission_handler/permission_handler.dart';

class CameraPermissionScreen extends HookWidget {
  const CameraPermissionScreen({super.key});

  @override
  Widget build(BuildContext context) {
    final isInitialized = useState(false);
    final isCameraPermissionGranted = useState(false);

    useEffect(
      () {
        WidgetsBinding.instance.addPostFrameCallback((_) async {
          final currentPermissionStatus = await Permission.camera.status;
          if (currentPermissionStatus.isGranted) {
            isCameraPermissionGranted.value = true;
            isInitialized.value = true;
            return;
          }

          final newPermissionStatus = await Permission.camera.request();
          isCameraPermissionGranted.value = newPermissionStatus.isGranted;
          isInitialized.value = true;
        });
        return;
      },
      [],
    );

    return CustomAppLifecycleListener(
      onResume: () async {
        if (!isInitialized.value) {
          // Note: カメラ使用権限の初回チェックが完了していない場合は何もしない
          return;
        }

        final newPermissionStatus = await Permission.camera.status;
        isCameraPermissionGranted.value = newPermissionStatus.isGranted;
      },
      child: Scaffold(
        body: isInitialized.value
            ? isCameraPermissionGranted.value
                ? Text('カメラ権限オン')
                : Text('カメラ権限オフ')
            : Text('初期状態'),
      ),
    );
  }
}

```


## 最後に
少しでもお役に立てれば嬉しいです🙌