안드로이드 / iOS 앱을 만들다보면 여러가지 권한을 사용해야 할 때가 있다.
카메라, 저장장치, 위치정보, 메세지, 연락처 등등 여러 정보를 기기에서 받아서 활용하는 경우가 많다.
만약 적절한 권한이 없는 경우 디버그 모드에서는 동작하는데 컴파일 후 장치에서 실행하면 오류와 함께 종료되는 경우가 많다.
다행히도 flutter 에 적절한 패키지가 있다. 패키지가 있을 경우에는 정말 사용하기 편하다.
Permission Handler 5.0.1+1
현시점에서 버전은 5.0.1+1 이다.
pub.dev/packages/permission_handler
사용전 설정
안드로이드
- pre 1.12 android project 로 업그레이드 후 사용
- gladdle.properties 파일에 아래와 같이 추가
android.useAndroidX=true
android.enableJetifier=true
- 'android/app/build.gradle' 파일에서 android sdk 버전 29로 설정
android {
compileSdkVersion 28
...
}
iOS
Info.plist 에 권한 추가
아래 링크에서 모든 종류의 권한 예시 확인 가능
사용법
pubspec.yaml 파일 depency 에 추가해준다.
permission_handler: ^5.0.1+1
사용하려는 dart 파일에 import 추가 후 사용
import 'package:permission_handler/permission_handler.dart';
권한 상태 확인(및 종류)
권한 상태는 undetermined, granted, denied, restricted 가 있으며 안드로이드는 추가로 permanentlyDenied 가 있다.
예를 들어 카메라 권한 상태가 궁금하면 Permission.camera.status 로 확인하면 된다.
var status = await Permission.camera.status;
if (status.isUndetermined) {
// We didn't ask for permission yet.
}
// You can can also directly ask the permission about its status.
if (await Permission.location.isRestricted) {
// The OS restricts access, for example because of parental controls.
}
권한 요청
아래와 같이 Permission.contacts.request() 로 요청하면 된다.
return 값은 undetermined, granted, denied, restricted 와 같은 값이다.
if (await Permission.contacts.request().isGranted) {
// Either the permission was already granted before or the user just granted it.
}
// You can request multiple permissions at once.
Map<Permission, PermissionStatus> statuses = await [
Permission.location,
Permission.storage,
].request();
print(statuses[Permission.location]);
사용예시
실제로 사용할 때는 await 가 있기 때문에 async 로 감싸진 클래스(또는 함수) 에서 다뤄야 한다.
위젯 생명주기중 하나인 initState 에서는 Future 클래스가 호출이 안된다.
이럴때는 WidgetsBinding.instance.addPostFrameCallback((_) { 요기 } 에다가 넣어주면 된다.
아래와 같은 함수를 만들어놓고 불러오면 된다.
여기서 카메라 권한 확인용으로 만든 camPermissionIsGranted 변수를 사용했다.
처음엔 false 로 사용 불가능으로 해놓고 권한이 확인되면 true 로 변경하자.
bool camPermissionsGranted = false;
Future<bool> CamPermissionIsGranted() async {
var _status = await Permission.camera.status.isGranted;
camPermissionsGranted = _status;
return _status;
}
권한이 있을때와 없을때 widget 을 어떻게 구현하는지가 처음에 햇갈렸다.
FutureBuilder로 구현해봤는데 뭔가 배보다 배꼽이 큰거같아서 다시 검색해보니 더 쉽고 깔끔한 방법이 있다.
예를들어 Scaffold의 body 에 구현한다고 하면 아래 ? : 연산자를 쓰면 쉽다.
그리고 권한 확인이 되면 camPermissionIsGranted 의 값을 바꾸고 동시에 setState로 위젯을 다시 build 하면 된다.
(조건) ? (true인 경우 실행) : (false인 경우 실행)
return Scaffold(
body: camPermissionsGranted
? Container(child: 퍼미션 있을경우 실행)
: Container(child: 퍼미션 없을 경우 실행)
아래 예제 맨 아랫줄에서 보면 setState까지 구현된 권한 요청 함수를 볼 수 있다.
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
void main() => runApp(MyApp());
/// Example Flutter Application demonstrating the functionality of the
/// Permission Handler plugin.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.settings),
onPressed: () async {
var hasOpened = openAppSettings();
debugPrint('App Settings opened: ' + hasOpened.toString());
},
)
],
),
body: Center(
child: ListView(
children: Permission.values
.where((Permission permission) {
if (Platform.isIOS) {
return permission != Permission.unknown &&
permission != Permission.sms &&
//permission != Permission.storage &&
permission != Permission.ignoreBatteryOptimizations &&
permission != Permission.accessMediaLocation;
} else {
return permission != Permission.unknown &&
permission != Permission.mediaLibrary &&
permission != Permission.photos &&
permission != Permission.reminders;
}
})
.map((permission) => PermissionWidget(permission))
.toList()),
),
),
);
}
}
/// Permission widget which displays a permission and allows users to request
/// the permissions.
class PermissionWidget extends StatefulWidget {
/// Constructs a [PermissionWidget] for the supplied [Permission].
const PermissionWidget(this._permission);
final Permission _permission;
@override
_PermissionState createState() => _PermissionState(_permission);
}
class _PermissionState extends State<PermissionWidget> {
_PermissionState(this._permission);
final Permission _permission;
PermissionStatus _permissionStatus = PermissionStatus.undetermined;
@override
void initState() {
super.initState();
_listenForPermissionStatus();
}
void _listenForPermissionStatus() async {
final status = await _permission.status;
setState(() => _permissionStatus = status);
}
Color getPermissionColor() {
switch (_permissionStatus) {
case PermissionStatus.denied:
return Colors.red;
case PermissionStatus.granted:
return Colors.green;
default:
return Colors.grey;
}
}
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(_permission.toString()),
subtitle: Text(
_permissionStatus.toString(),
style: TextStyle(color: getPermissionColor()),
),
trailing: IconButton(
icon: const Icon(Icons.info),
onPressed: () {
checkServiceStatus(context, _permission);
}),
onTap: () {
requestPermission(_permission);
},
);
}
void checkServiceStatus(BuildContext context, Permission permission) async {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text((await permission.status).toString()),
));
}
Future<void> requestPermission(Permission permission) async {
final status = await permission.request();
setState(() {
print(status);
_permissionStatus = status;
print(_permissionStatus);
});
}
}
추가로 안드로이드의 경우 권한 수락을 완전히 거부할 경우 있는데 그럴때는 앱정보에 들어가서 직접 권한을 줘야 한다.
아래 루틴을 requestPermission 에 넣어주면 좋다.
if (await Permission.speech.isPermanentlyDenied) {
// 유저가 완전히 권한을 거부하였을 경우 앱설정으로 진입
// 유저가 알아보기 쉽게 도움말을 띄우면 더 좋음
openAppSettings();
}
이상 끝.
'Flutter 프로그래밍 > 플러그인 사용' 카테고리의 다른 글
앱스토어 심사중 AppTrackingTransparency 문제 발생 (1) | 2020.11.07 |
---|---|
Failed to load ad: 0 - Device ID 얻어오기 (0) | 2020.10.31 |