플러터로 하이브리드 앱을 만들려고 하는데, 

아래와 같은 페이지에서 파일 선택이 되지 않는 문제가 발생했다.

크롬 앱에서 접속했을 땐 가능했으니 어떻게든 방법이 있을거라 믿고 오랜 시간 검색하여 방법을 알아냈다.

 

 

일단 사전 설정이 조금 필요한데, 대부분의 것들은 아래 공식문서에 잘 나와있다:

https://pub.dev/packages/permission_handler 

 

permission_handler | Flutter Package

Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API to request and check permissions.

pub.dev

 

위 페이지에서 제공하는 정보에 따르면, 여러분이 해야할 것은 권한 설정이고,

권한 설정을 위해

`android/app/src/main` 밑의 AndroidManifest.xml 파일을 다음과 같이 수정해야 한다.

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

<!-- 여기 아래의 uses-permission 부분들을 추가하면 된다. -->
<!-- 얻고자 하는 권한만 적어줘도 된다. -->

    <!--
    Internet permissions do not affect the `permission_handler` plugin, but are required if your app needs access to
    the internet.
    -->
    <uses-permission android:name="android.permission.INTERNET"/>

    <!-- Permissions options for the `contacts` group -->
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
    <uses-permission android:name="android.permission.GET_ACCOUNTS"/>

    <!-- Permissions options for the `storage` group -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <!-- Permissions options for the `camera` group -->
    <uses-permission android:name="android.permission.CAMERA"/>

    <!-- Permissions options for the `sms` group -->
    <uses-permission android:name="android.permission.SEND_SMS"/>
    <uses-permission android:name="android.permission.RECEIVE_SMS"/>
    <uses-permission android:name="android.permission.READ_SMS"/>
    <uses-permission android:name="android.permission.RECEIVE_WAP_PUSH"/>
    <uses-permission android:name="android.permission.RECEIVE_MMS"/>

    <!-- Permissions options for the `phone` group -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.CALL_PHONE"/>
    <uses-permission android:name="android.permission.ADD_VOICEMAIL"/>
    <uses-permission android:name="android.permission.USE_SIP"/>
    <uses-permission android:name="android.permission.READ_CALL_LOG"/>
    <uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
    <uses-permission android:name="android.permission.BIND_CALL_REDIRECTION_SERVICE"/>

    <!-- Permissions options for the `calendar` group -->
    <uses-permission android:name="android.permission.READ_CALENDAR" />
    <uses-permission android:name="android.permission.WRITE_CALENDAR" />

    <!-- Permissions options for the `location` group -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

    <!-- Permissions options for the `microphone` or `speech` group -->
    <uses-permission android:name="android.permission.RECORD_AUDIO" />

    <!-- Permissions options for the `sensors` group -->
    <uses-permission android:name="android.permission.BODY_SENSORS" />

    <!-- Permissions options for the `accessMediaLocation` group -->
    <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />

    <!-- Permissions options for the `activityRecognition` group -->
    <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />

    <!-- Permissions options for the `ignoreBatteryOptimizations` group -->
    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />

    <!-- Permissions options for the `bluetooth` group -->
    <uses-permission android:name="android.permission.BLUETOOTH" />

    <application
       
       ...

 

했다면 아래 그림처럼 두 파일이 해당 내용들을 포함하는지 확인한다.

(아마 돼 있을 것이다 이미)

 

그리고 permission_handler 를 pub get 하도록 한다. 

글을 쓰는 현재 시점의 버젼은 

permission_handler^8.1.4+2

이다.

 

flutter pub get 해주어 설치해준다.

 

여기까지 따라했다면 준비물은 끝이다.

이제 코드를 쳐보자.

MyHomePage class를 띄운다고 생각해보자.

이 경우 필자는 코드를 다음과 같이 짬으로서 floating button을 누르면 권한을 확인하도록 만들었다.

(WebViewPlugin은 맨 아래에서 설명)

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  var permissionState = false;

  Future<bool> requestCameraPermission(BuildContext context) async {
    // PermissionStatus status = await Permission.storage.request();
    Map<Permission, PermissionStatus> statuses =
        await [Permission.camera, Permission.storage].request();
    // var status = await requestCameraPermission(context);

    if (statuses[Permission.camera]!.isGranted == false ||
        statuses[Permission.storage]!.isGranted == false) {
      // 허용이 안된 경우
      showDialog(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              content: Text("권한 설정을 확인해주세요."),
              actions: [
                ElevatedButton(
                    onPressed: () {
                      openAppSettings(); // 앱 설정으로 이동
                    },
                    child: Text('설정하기')),
              ],
            );
          });
      print("permission denied by user");
      return false;
    }
    print("permission ok");
    return true;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        toolbarHeight: 0,
        backgroundColor: Colors.transparent,
      ),
      body:
          permissionState == true ? WebViewPlugin() : Text('버튼을 눌러 권한을 획득하십시오'),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          var status = await requestCameraPermission(context);
          setState(() {
            this.permissionState = status;
          });
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

 

 

위 코드에서 권한을 얻는 부분은 다음과 같다.

아래에서 openAppSettings는 만약 사용자가 권한을 거절했을 경우 

시스템 설정에서 바꿀 수 있도록 친전히 거기로 보내주는 것이다.

 

  Future<bool> requestCameraPermission(BuildContext context) async {
    // PermissionStatus status = await Permission.storage.request();
    Map<Permission, PermissionStatus> statuses =
        await [Permission.camera, Permission.storage].request();
    // var status = await requestCameraPermission(context);

    if (statuses[Permission.camera]!.isGranted == false ||
        statuses[Permission.storage]!.isGranted == false) {
      // 허용이 안된 경우
      showDialog(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              content: Text("권한 설정을 확인해주세요."),
              actions: [
                ElevatedButton(
                    onPressed: () {
                      openAppSettings(); // 앱 설정으로 이동
                    },
                    child: Text('설정하기')),
              ],
            );
          });
      print("permission denied by user");
      return false;
    }
    print("permission ok");
    return true;
  }

 

Flutter에서 webview를 띄우고 싶을 때 가장 자주 쓰는 pacakge는 webview_flutter이다.

그러나 우리의 상황같이 웹뷰에서 파일선택을 눌렀을 때 파일을 업로드하려면

flutter_webview_plugin을 pub get 해야 한다.

따라서 pubspec.yaml에 다음을 추가해주자.

flutter_webview_plugin^0.4.0 # 글을 쓰는 시점의 버젼

그리고 

아래와 같이 WebViewPlugin를 작성해주자.

WebViewPlugin의 url은 아래 사진처럼 파일을 업로드할 수 있도록 버튼이 마련되어 있는 사이트를 지정하자.

 

 

WebviewPlugin.class

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

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

  @override
  _WebViewPluginState createState() => _WebViewPluginState();
}

class _WebViewPluginState extends State<WebViewPlugin> {
  @override
  Widget build(BuildContext context) {
    return WebviewScaffold(
      url: "http://10.0.2.2:8080/kchoiCustomUploadTest",
      withLocalStorage: true,
    );
  }
}

 

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기
// custom