본문 바로가기

Personal Posting/Flutter

Flutter에서의 Permission Handler 사용

이번 포스팅은 아래 링크의 아티클을 그대로 번역하여 정리하였습니다. 원문을 확인하고 싶으시면 아래 링크를 확인해주세요.

출처: https://medium.com/@dudhatkirtan/how-to-use-permission-handler-in-flutter-db964943237e

 

How To Use Permission Handler In Flutter?

Overview

medium.com

 

개요

Flutter의 permission_handler 패키지는 모바일 애플리케이션에서 권한을 관리하는 데 유용한 도구이다. Android와 iOS 플랫폼 모두에 대한 권한을 요청하고 확인하는 과정을 간소화한다. permission_handler를 사용하면 개발자는 카메라, 연락처, 위치 등과 같은 민감한 정보에 쉽게 접근할 수 있는 사용자 권한을 요청할 수 있다. permission_handler 패키지는 권한 처리를 위한 표준화된 API를 제공하여 플랫폼별 코드의 필요성을 줄인다. 또한 현재 권한 상태 확인, 권한 콜백 처리, 사용자에게 권한을 요청하는 대화 표시 등 유용한 기능을 제공한다. 전반적으로 permission_handler 패키지는 Flutter 애플리케이션에서 권한 관리를 쉽게 해줄 수 있다.

 

소개

permission_handler는 개발자가 런타임 권한을 쉽고 효율적으로 처리할 수 있는 Flutter의 인기 패키지다. 카메라, 마이크, 저장 공간, 위치 등과 같은 특정 장치 리소스에 대한 사용자 권한을 요청하는 데 사용된다. permission_handler를 사용하면 개발자는 특정 퍼미션의 상태를 쉽게 확인하고 필요할 때 사용자에게 권한을 요청할 수 있다.
permission_handler는 플랫폼별 Permission API를 랩핑하여 작동하므로 개발자가 플랫폼 간의 차이에 대해 걱정하지 않고 플랫폼별 코드를 작성할 수 있다. 안드로이드와 iOS 플랫폼을 모두 지원한다.
이 패키지는 권한을 요청하고 확인하는 데 사용할 수 있는 사용하기 쉬운 API를 제공한다. 개발자가 권한 요청 및 응답을 처리하는 데 도움이 되는 requestPermission, checkPermissionStatus, openAppSettings 등 여러 가지 방법이 포함되어 있다.
전반적으로 permission_handler는 Flutter에서 권한을 요청하고 확인하는 과정을 간소화하여 개발자가 애플리케이션에서 런타임 권한을 더 쉽게 처리할 수 있도록 도와준다.

 

적용 방법

먼저 아래와 같이 명령어를 입력한다.

flutter pub add permission_handler

 

pubspeck.yaml에 추가한다. (이후 flutter pub get)

dependencies:
    permission_handler: ^12.0.0

 

사용하고자 하는 Flutter 파일에서 import

import 'package:permission_handler/permission_handler.dart';

 

request() 메서드를 사용하여 원하는 기능에 대한 권한을 요청한다:
Future<void> requestPermission() async {
  final permission = Permission.location;

  if (await permission.isDenied) {
    await permission.request();
  }
}
 

이 예제에서는 사용자의 위치에 액세스할 수 있는 권한을 요청한다. Permission.location을 permission_handler 패키지에서 사용할 수 있는 다른 권한 유형으로 대체할 수 있다. 이러한 방식으로 permission_handler는 위치 액세스를 얻는 데 도움이 된다.

 

권한 상태를 확인하기 위해서는 status 프로퍼티를 사용한다.

Future<bool> checkPermissionStatus() async {
  final permission = Permission.location;
  return await permission.status.isGranted;
}

 

이 메서드는 권한 여부에 따라 Future로 true 또는 false를 반환한다.

 

권한 요청 전 사용자에게 설명을 표시할지 여부를 결정하기 위해 shouldShowRequestRationale() 메서드를 사용할 수도 있다:
Future<bool> shouldShowRequestRationale() async {
  final permission = Permission.location;
  return await permission.shouldShowRequestRationale;
}

 

이 메서드는 설명이 표시되어야 하냐 여부에 따라 Future true / false를 반환한다.

 

권한 요청 및 확인

Flutter에서 permission_handler 패키지를 사용하여 권한을 요청하고 확인하려면 다음 단계를 따른다:

import 'package:permission_handler/permission_handler.dart';

 

권한 요청을 위해 request() 메서드를 사용한다.

Future<void> requestPermission() async {
  final permission = Permission.camera;

  if (await permission.isDenied) {
    await permission.request();
  }
}

 

권한 승인 여부 체크를 위해 status 프로퍼티를 사용한다.

Future<bool> checkPermissionStatus() async {
  final permission = Permission.camera;

  return await permission.status.isGranted;
}

 

앞서 설명한 것과 동일하게 권한 승인 여부에 따라 true / false를 반환한다.

 

권한이 영구적으로 거부되는지 확인하려면 (즉, 사용자가 메시지를 받았을 때 "다시 묻지 않음"을 선택한 상황),

isPermanentDenied 속성을 사용한다:

Future<bool> checkPermanentlyDenied() async {
  final permission = Permission.camera;

  return await permission.status.isPermanentlyDenied;
}

 

사용자가 수동으로 권한을 부여하거나 거부할 수 있는 앱 설정 화면을 열려면 openAppSettings() 메서드를 사용한다:

void openSettings() {
  openAppSettings();
}

 

Permission Response 핸들링

Flutter에서 permission_handler 패키지의 권한 응답을 처리하려면 request() 메서드를 사용하여 결과를 확인할 수 있다:

Future<void> requestPermission() async {
  final permission = Permission.camera;

  if (await permission.isDenied) {
    final result = await permission.request();
    if (result.isGranted) {
      // Permission is granted
    } else if (result.isDenied) {
      // Permission is denied
    } else if (result.isPermanentlyDenied) {
      // Permission is permanently denied
    }
  }
}

 

이 예제에서는 카메라 사용 권한을 요청하려고 한다. request() 메서드를 호출한 후, isGranted, isDenied, isPermanentDenied 속성을 사용하여 결과를 확인한다.

 

권한 요청이 승인되면 isGranted 속성이 true가 된다. 허가가 거부되면 isDenied 속성이 true가 된다. 허가가 영구적으로 거부되면 isPermanentDenied 속성이 true가 된다..

 

openAppSettings() 메서드를 사용하여 사용자가 수동으로 권한을 부여하거나 거부할 수 있는 앱 설정 화면을 열 수도 있다:

void openSettings() {
 openAppSettings();
}

 

따라서 permission_handler는 원하는 권한을 요청한 다음 사용자가 권한을 부여했는지 여부를 확인할 수 있는 방법을 제공한다.

Permission Group 사용하기

Flutter의 permission_handler 패키지는 여러 권한 그룹을 제공하므로 한 번에 여러 권한을 요청할 수 있다. permission_handler 패키지에서 제공하는 다양한 권한 그룹은 다음과 같다:

 

Permission Group : PermissionGroup.sms

Description : Permission to send or receive SMS messages.

Permission Group : PermissionGroup.sensors

Description : Permission to access the device sensors.

Permission Group : PermissionGroup.phone

Description : Permission to read or write to the user’s phone state.

Permission Group : PermissionGroup.photos

Description : Permission to read or write to the user’s photos.

Permission Group : PermissionGroup.reminders

Description : Permission to read or write to the user’s reminders.

Permission Group : PermissionGroup.microphone

Description : Permission to access the microphone.

Permission Group : PermissionGroup.mediaLibrary

Description : Permission to read or write to the user’s media library.

Permission Group : PermissionGroup.locationWhenInUse

Description : Permission to access the user’s location only when the app is in the foreground.

Permission Group : PermissionGroup.locationAlways

Description : Permission to access the user’s location, even when the app is in the background.

Permission Group : PermissionGroup.location

Description : Permission to access the user’s location.

Permission Group : PermissionGroup.contacts

Description : Permission to read or write to the user’s contacts.

Permission Group : PermissionGroup.camera

Description : Permission to access the camera.

Permission Group : PermissionGroup.speech

Description : Permission to access the device’s speech recognition system.

Permission Group : PermissionGroup.storage

Description : Permission to read or write to the user’s external storage.

Permission Group : PermissionGroup.calendar

Description : Permission to read or write to the user’s calendar.

 

이러한 Permission Group를 permission_handler 패키지의 request() 메서드와 함께 사용하여 한 번에 여러 권한을 요청할 수 있다. 예를 들어 사용자의 카메라와 마이크에 액세스할 수 있는 권한을 요청하려면 다음과 같은 PermissionGroup.cameraPermissionGroup.microphone 상수를 사용할 수 있다:

 

Future<void> requestPermissions() async {
  Map<Permission, PermissionStatus> status = await [
    PermissionGroup.camera,
    PermissionGroup.microphone,
  ].request();

  if (status[PermissionGroup.camera].isDenied) {
    // Camera permission is denied
  }

  if (status[PermissionGroup.microphone].isDenied) {
    // Microphone permission is denied
  }
}

 

권한 변경 처리

설계된 애플리케이션을 사용할 때 부여된 권한이 변경될 수 있다. 따라서 플러터 애플리케이션이 오작동 없이 이러한 변경 사항을 원활하게 처리할 수 있는지 확인하는 것이 중요하다. 이제 플러터에서 permission_handler를 사용하면서 권한 변경을 처리하는 방법에 대해 알아보자.

 

Flutter의 permission_handler 패키지는 onStatusChange 스트림을 사용하여 권한 변경을 처리하는 방법을 제공한다. 이 스트림은 권한 상태가 변경될 때마다 이벤트를 발생시킨다. 적용 방법은 다음과 같다. (패키지 import 생략)

 

onStatusChange 스트림 구독:

StreamSubscription<PermissionStatus> subscription =
    Permission.camera.status.listen((status) {
  if (status.isDenied) {
    // Camera permission is denied
  } else if (status.isGranted) {
    // Camera permission is granted
  }
});

 

이 예제에서는 카메라 권한을 위해 onStatusChange 스트림을 구독하고 있다. 카메라 권한 상태가 변경될 때마다 스트림에서 이벤트가 발생하므로 이에 따라 처리할 수 있다.

 

권한 변경 처리가 완료되면 메모리 누수를 방지하기 위해 onStatusChange 스트림 구독을 취소해야 한다:

subscription.cancel();

 

플랫폼별 고려 사항

애플리케이션이 실행되는 모든 플랫폼에 대해 따라 사용자 지정 작업이 필요하다. Flutter에서 permission_handler 패키지를 사용할 때는 몇 가지 플랫폼별 고려 사항을 염두에 두어야 한다:

 

IOS:

iOS에서는 앱의 Info.plist 파일에 앱에 특별한 권한이 필요한 이유에 대한 설명을 포함해야 한다. 이유를 제공하지 않으면 권한 대화 상자가 표시되지 않으며 기본적으로 권한이 거부된다.

PermissionGroup.camera와 같은 일부 권한의 경우, 앱의 Info.plist 파일에 사용 설명을 추가하여 앱이 카메라에 액세스해야 하는 이유를 설명해야 할 수도 있다.

사용자의 위치에 접근할 수 있는 권한을 요청할 때, 앱이 사용자의 위치를 필요로 하는 이유를 설명하는 사용 설명서를 제공해야 한다.

 

Android:

Upgrade pre-1.12 Android projects

버전 4.4.0부터 이 플러그인은 Flutter 1.12 Android 플러그인 API를 사용하여 구현되었다. 안타깝게도, 이는 앱 개발자들이 새로운 Android 인프라를 지원하기 위해 앱을 마이그레이션해야 한다는 것을 의미한다. 1.12 이전 Android 프로젝트 마이그레이션 가이드를 따르면 이를 수행할 수 있다. 이 가이드를 따르지 않으면 예상치 못한 동작이 발생할 수 있다. 가장 일반적으로 알려진 오류는 permission_handler가 .request() 메서드를 호출한 후에 돌아오지 않는 것입니다.

 

AndroidX

버전 3.1.0부터 permission_handler 플러그인이 Android 지원 라이브러리의 AndroidX 버전으로 전환되었다. 즉, Android 프로젝트도 AndroidX를 지원하도록 업그레이드해야 한다. 자세한 지침은 여기에서 확인할 수 있다.

 

1. 다음을 "gradle.properties" 파일에 추가한다:

android.useAndroidX=true
android.enableJetifier=true

 

2. "android/app/build.gradle" 파일의 compileSdkVersion 을 33으로 세팅한다.

android {
  compileSdkVersion 33
  ...
}

 

3. Android의 모든 디펜던시 요소를 교체해야 한다. (AndroidX에 대한 종속 요소는 여기에서 확인)

 

AndroidManifest.xml 파일에 권한을 추가한다. 앱을 시작하는 방법에 따라 디버그, 메인, 프로필 버전이 선택된다. 일반적으로 메인 버전에만 권한을 추가하면 된다. 다음은 가능한 모든 권한 목록이 포함된 AndroidManifest.xml 예제를 살펴보기로 한다.

 

Android에서는 앱의 AndroidManifest.xml 파일에 권한 요청을 포함해야 한다.

Android 11 이상을 대상으로 할 때는 패키지 가시성 기능을 사용하여 앱 데이터에 대한 액세스를 제한해야 한다. 즉, 특정 파일이나 디렉터리에 액세스하려면 추가 권한을 요청해야 할 수도 있다.

만약 앱이 Android 6.0(API 레벨 23) 이상을 대상으로 하는 경우 런타임에 권한을 요청해야 하며, 사용자가 해당 기능을 사용하기 전에 권한을 부여해야 한다.

 

사용자 위치 접근 권한을 요청할 때, 앱이 사용자 위치에 접근해야 하는 이유를 설명해야 한다.

AndroidManifest.xml 샘플은 아래와 같다.

 

<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
  package="com.example.YourApp Name">

  <uses-permission android:name="android.permission.CAMERA" />
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

  <application
       android:label="YourApp Name"
       android:name="${applicationName}"
       android:icon="@mipmap/ic_launcher">      
       <activity
           android:name=".MainActivity"
           android:exported="true"
           android:launchMode="singleTop"
           android:theme="@style/LaunchTheme"
           android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
           android:hardwareAccelerated="true"
           android:windowSoftInputMode="adjustResize">
           <!-- Specifies an Android theme to apply to this Activity as soon as
                the Android process has started. This theme is visible to the user
                while the Flutter UI initializes. After that, this theme continues
                to determine the Window background behind the Flutter UI. -->
           <meta-data
             android:name="io.flutter.embedding.android.NormalTheme"
             android:resource="@style/NormalTheme"
             />
           <intent-filter>
               <action android:name="android.intent.action.MAIN"/>
               <category android:name="android.intent.category.LAUNCHER"/>
           </intent-filter>
       </activity>
       <!-- Don't delete the meta-data below.
            This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
       <meta-data
           android:name="flutterEmbedding"
           android:value="2" />
   </application>    
</manifest>

 

Web:

웹 애플리케이션에서 permission_handler 패키지는 모든 권한에 대해 작동하지 않는다. PermissionGroup.cameraPermissionGroup.microphone과 같은 일부 권한은 웹 플랫폼에서 지원되지 않는다. 웹에서 권한을 요청할 때, 브라우저는 사용자가 응답해야 하는 권한 대화를 표시한다. window.navigator.permissions API를 사용하여 현재 권한 상태를 확인할 수 있지만 이 API는 모든 브라우저에서 지원되는 것은 아니다.

 

전반적으로 Flutter 앱에서 permission_handler 패키지를 사용할 때는 이러한 플랫폼별 고려 사항을 염두에 두는 것이 중요하다. 이를 잘 고려하면 지원되는 모든 플랫폼에서 앱이 올바르게 작동하고 최상의 사용자 경험을 제공할 수 있을 것이다. 따라서 permission_handler를 플러터에 사용하는 동안 지금까지 정리한 세부 사항을 염두에 두어야 한다.

 

모범 사례

지금까지 permission_handler를 플러터에 사용하여 애플리케이션에서 권한을 요청하는 방법을 배웠다. 그러나 권한을 요청하기 전에 염두에 두어야 할 다른 측면도 있다. permission_handler의 도움을 받아 권한을 요청할 때 따라야 할 몇 가지 모범 사례는 다음과 같다:

 

필요한 권한만 요청: 앱에 필요하지 않은 권한은 요청하지 않는다. 불필요한 권한을 요청하면 사용자 개인정보 보호와 보안에 해를 끼칠 수 있으며 사용자가 앱을 불신하게 만들 수 있다.

권한이 필요한 이유를 설명: 권한을 요청할 때 앱에 권한이 필요한 이유를 명확하고 간결하게 설명해야한다. 이렇게 하면 사용자가 권한이 필요한 이유를 이해하는 데 도움이 되며, 사용자가 권한을 부여할 가능성이 높아질 수 있다.
권한을 우아하게 처리: 앱은 권한 상태 변경을 우아하게 처리할 수 있어야 한다. 예를 들어, 사용자가 권한을 취소하는 경우 앱은 해당 기능을 비활성화하고 해당 기능이 비활성화된 이유에 대한 명확한 설명을 제공해야 한다.
지원되는 모든 플랫폼에서 앱 테스트: permission_handler 패키지가 지원되는 모든 플랫폼에서 앱을 테스트해야 한다. 여기에는 Android, iOS 및 웹 플랫폼이 포함된다. 테스트를 통해 앱이 올바르게 작동하는지 확인하고 모든 플랫폼에서 최상의 사용자 경험을 제공할 수 있다.
권한 변경을 처리하려면 onStatusChange 스트림을 사용: 권한 상태 변경을 처리하려면 onStatusChange 스트림을 사용한다. 이 스트림은 권한 상태가 변경될 때마다 이벤트를 발생시켜 앱이 실시간으로 변경 사항에 대응할 수 있도록 한다.
적절한 권한 그룹 사용: 요청하는 각 권한에 대해 적절한 권한 그룹(Permission Group)을 사용한다. permission_handler 패키지는 카메라 권한의 경우 PermissionGroup.camera, 위치 권한의 경우 PermissionGroup.location 등 여러 권한 그룹을 제공하여 권한을 요청할 수 있다.
권한 오류 처리: 권한을 요청할 때 앱은 오류를 우아하게 처리할 수 있어야 한다. 예를 들어, 사용자가 권한을 거부하는 경우 앱은 해당 권한이 필요한 이유를 명확하게 설명하고 사용자에게 다시 권한을 부여하도록 요청해야 한다.

 

예제 코드

다음은 카메라를 여는 버튼과 현재 위도와 경도를 출력하는 버튼 두 개가 있는 Flutter 앱의 예제 코드이다. 이 앱은 permission_handler 패키지를 사용하여 카메라와 위치 데이터에 액세스할 수 있는 권한을 요청한다.

 

우선 pubspec.yaml을 수정한다.

dependencies:
  flutter:
    sdk: flutter
  permission_handler: ^12.0.0

 

다음으로 아래와 같이 main.dart를 생성한다.

import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:geolocator/geolocator.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     title: 'Camera and Location Demo',
     home: HomePage(),
     debugShowCheckedModeBanner: false,

   );
 }
}
class HomePage extends StatelessWidget {
 final permissionCamera = Permission.camera;
 final permissionLocation = Permission.location;


 void locationPermissionStatus() async {
   // Request location permission
   final status = await permissionLocation.request();
   if (status == PermissionStatus.granted) {
     // Get the current location
     final position = await Geolocator.getCurrentPosition();
     print('Latitude: ${position.latitude}, Longitude: ${position.longitude}');
   } else {
     // Permission denied
     print('Location permission denied.');
   }
  }

 void cameraPermissionStatus() async {
  // Request camera permission
   final status = await permissionCamera.request();
   if (status.isGranted) {
     // Open the camera
     print('Opening camera...');
   } else {
     // Permission denied
     print('Camera permission denied.');
   }
  }

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       title: Text('Camera and Location Demo'),
     ),
     body: Center(
       child: Column(
         mainAxisAlignment: MainAxisAlignment. centre,
         children: <Widget>[
           ElevatedButton(
             onPressed: cameraPermissionStatus(),
             child: Text('Open Camera'),
           ),
           ElevatedButton(
             onPressed: locationPermissionStatus(),
             child: Text('Get Location'),
           ),
         ],
       ),
     ),
   );
 }
}

 

실행 결과

 

마치며

이 아티클에서는 플러터의 permission_handler에 대해 다음과 같은 점을 알아보았다:

1. permission_handler 패키지는 Flutter 개발자가 애플리케이션에서 런타임 권한을 처리하는 데 유용한 도구다. 권한을 요청하고 확인하는 과정을 간소화하여 개발자가 앱의 다른 측면에 집중할 수 있도록 한다.
2. 이 패키지는 Android와 iOS 플랫폼을 모두 지원하며, requestPermission, checkPermissionStatus, openAppSettings와 같은 메서드를 포함하는 사용하기 쉬운 API를 제공한다.
3. 개발자는 Permission Group을 사용하여 한 번에 여러 권한을 요청할 수도 있다.
4. Android 및 iOS와 같은 다양한 플랫폼의 경우 애플리케이션을 만들 때 몇 가지 구체적인 고려 사항을 염두에 두어야 한다.
5. 설정 프로세스를 따라가면 개발자는 쉽게 권한을 요청하고 확인할 수 있다. 또한 request() 메서드의 결과를 듣고 권한 응답을 처리할 수도 있다.
6. 마지막으로, 개발자들은 permission_handler 패키지에서 제공하는 permission group을 사용하여 여러 권한을 한 번에 요청할 수 있으며, 이를 통해 애플리케이션에서 권한을 더 쉽게 처리할 수 있다.

 

+ 추가) 이 포스팅을 작성하면서 "이 패키지를 이용하면 네이티브 단에서 직접 권한 설정을 할 필요가 없는 것 아닐까?" 라는 의문을 가지고 추가로 정보를 검색해 보았다. 그 해답을 아래에 함께 정리해둔다.

Q) permission_handler만 있으면 네이티브 권한 설정은 필요 없는가?

A) permission_handler 패키지를 설치한다고 해서, Xcode(iOS)나 AndroidManifest(Android)에서의 권한 설정을 완전히 대체할 수 있는 것은 아니다. 즉, permission_handler는 앱 실행 중(런타임)에 권한을 요청하고 관리하는 역할을 하지만, 각 플랫폼의 네이티브 권한 선언(설정) 작업은 여전히 별도로 필요하다.

 

Q) 왜 네이티브 권한 설정이 필요한가?

A) 

Android의 경우, AndroidManifest.xml 파일에 어떤 권한을 사용할지 명시해야 한다. 예를 들어, 카메라 권한이 필요하다면 아래와 같이 추가해야 한다.

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

 

iOS의 경우, Info.plist 파일에 권한 사용 이유(설명)와 함께 해당 권한을 선언해야 한다.

<key>NSCameraUsageDescription</key>
<string>이 앱은 카메라 사용이 필요합니다.</string>

iOS Podfile: 사용할 권한에 따라 Podfile에서 특정 권한을 활성화해야 할 수도 있다.

 

permission_handler의 역할

앱 실행 중에 실제로 사용자에게 권한을 요청하고, 권한 상태를 확인하거나 앱 설정으로 이동시키는 등의 런타임 권한 관리를 담당한다.

네이티브 설정 없이 이 패키지만으로는 권한 요청이 정상적으로 동작하지 않는다.

 

네이티브 권한 설정과 permission_handler의 차이

구분 역할 및 한계
네이티브 권한 설정 - 앱이 어떤 권한을 사용할 것인지 OS에 "선언"만 함
- 실제로 사용자에게 권한을 "요청"하거나, 권한 상태를 "확인"하지 않음
- Android: AndroidManifest.xml, iOS: Info.plist에서 설정 필요
permission_handler - 런타임에 실제로 권한을 "요청"하고, 허용/거부 등 "상태"를 확인
- 권한이 없는 경우 앱 설정 화면으로 이동 등 추가 기능 제공
- Flutter 코드(Dart)에서 플랫폼별 권한 요청을 통합적으로 처리