flutter_inappwebview file download error (파일 다운로드 안 될 때)

JuhyunKim·2022년 11월 7일
0

flutter

목록 보기
4/5

이전글
01. flutter_inappwebview로 hybrid app 만들기
02. flutter_inappwebview 파일 업로드, 파일 탐색기 안 열릴 때 해결 방법 (flutter_inappwebview file provider authority)



또 오류 발견.. 이지만 또 flutter_inappwebview 개발자님이 올려주신 글이 있더라! 다행..ㅎ

flutter_inappwebview onDownloadStart Example
https://gist.github.com/pichillilorenzo/ee74e103fdc324f761c5fde7b73bd430



1. flutter_downloader 설치 및 준비


01. flutter_downloader 패키지 설치

flutter_downloader https://pub.dev/packages/flutter_downloader

flutter pub add flutter_downloader 하고 flutter pub get


이후 준비과정이 생각보다 긴데 잘 따라해야한다. flutter_downloader 공식 사이트를 참고했다.



02. provider 추가

저번 파일업로드 때와 비슷한데,
[project]/android/app/src/main/AndroidManifest.xml 파일에 프로젝트의 file download provider, initialization provider, flutter downloader initializer를 추가해줘야한다.

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.jhkim.jhkimvelog">
    <uses-permission android:name="android.permission.INTERNET"/>
   <application
        android:label="jhkim.log"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">
 	    ...
     // 밑의 provider를 추가해준다.
     // android:authorities의 패키지명을 본인 프로젝트 패키지명으로 바꿔줘야한다
     <provider
           android:name="vn.hunghd.flutterdownloader.DownloadedFileProvider"
           android:authorities="com.jhkim.jhkimvelog.flutter_downloader.provider"
           android:exported="false"
           android:grantUriPermissions="true">
           <meta-data
               android:name="android.support.FILE_PROVIDER_PATHS"
               android:resource="@xml/provider_paths"/>
       </provider>
    </application>
</manifest>



03. Localize notification

쉽게 말해서 다운로드할 때 뜨는 알림들을 원하는 언어로 바꿔줄 수 있다.
우선 [project]/android/src/main/res/values 폴더에 "Values Resources File"을 새로 생성하여 strings.xml 이름으로 지정해준다.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="flutter_downloader_notification_started">다운로드 시작</string>
    <string name="flutter_downloader_notification_in_progress">다운로드 진행중</string>
    <string name="flutter_downloader_notification_canceled">다운로드 취소</string>
    <string name="flutter_downloader_notification_failed">다운로드 실패</string>
    <string name="flutter_downloader_notification_complete">다운로드 완료</string>
    <string name="flutter_downloader_notification_paused">다운로드 중지</string>
</resources>

한국어로 사용할 경우 내용은 이런식으로 바꿔주면 되는데 string 내용은 원하는대로 맞춰주면 된다.

<string name="flutter_downloader_notification_started">Download started</string>
<string name="flutter_downloader_notification_in_progress">Download in progress</string>
<string name="flutter_downloader_notification_canceled">Download canceled</string>
<string name="flutter_downloader_notification_failed">Download failed</string>
<string name="flutter_downloader_notification_complete">Download complete</string>
<string name="flutter_downloader_notification_paused">Download paused</string>

영어로 사용하고 싶다면 flutter_downloader 공식사이트에서 준 예시처럼 이대로 사용해도 될 것 같다.



04. apk 다운로드 권한 허용하기

apk 파일을 다운받을 수 있도록 하고싶으면 권한을 따로 추가해줘야한다.
[project]/android/app/src/main/AndroidManifest.xml 파일에 넣어주면 되는데,
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />를 추가해주면 된다.

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.jhkim.jhkimvelog">
    <uses-permission android:name="android.permission.INTERNET"/>
	<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> // this line
   ...
</manifest>



05. useCleartextTraffic 허용해주기

참고링크 https://medium.com/@son.rommer/fix-cleartext-traffic-error-in-android-9-pie-2f4e9e2235e6

이 과정 굉장히 중요하다... 이거 빼먹었다가 한참 헤맸다.

우선 [project]/android/src/main/res/xml 폴더를 만들어준다.

network_security_config.xml

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">your_domain.com</domain>
    </domain-config>
</network-security-config>

위의 내용대로 작성한 network_security_config.xml 파일을 추가해준다.
그리고 이에 대한 내용을 [project]/android/app/src/main/AndroidManifest.xml 에 추가해줘야한다.
android:networkSecurityConfig="@xml/network_security_config"을 추가해주면 된다.

AndroidManifest.xml

<application
        android:label="jhkim.log"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher"
        android:requestLegacyExternalStorage="true"
        android:networkSecurityConfig="@xml/network_security_config"> // this line
  		...
</application>



06. File Downloader Initilaize

import 'package:flutter_downloader/flutter_downloader.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  // Plugin must be initialized before using
  await FlutterDownloader.initialize(
    debug: true, // optional: set to false to disable printing logs to console (default: true)
    ignoreSsl: true // option: set to false to disable working with http links (default: false)
  );

  runApp(/*...*/)
}

flutter_Downloader 사이트에서 그대로 가져왔는데, 저렇게 main에서 initialize 해주면 된다.




2. path_provider, permission_handler 설치 및 권한 요청

파일을 다운받으려면 저장할 경로와 저장공간 권한이 필요하다.

flutter pub add path_provider

flutter pub add permission_handler

추가해주고 flutter pub get 까지 진행해준다.


이제 저장 공간 권한 요청을 추가한다.

main.dart

void main() async{
  WidgetsFlutterBinding.ensureInitialized();
  await FlutterDownloader.initialize(
      debug: true, ignoreSsl: true
  );
  await Permission.storage.request(); // 저장공간 권한 요청 추가

  runApp(const MyApp());
}

저장공간 권한을 얻기 위해 main에서 request를 추가해준다.

그리고 [project]/android/app/src/main/AndroidManifest.xml 에도 android:requestLegacyExternalStorage="true""<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"을 추가해줘야 한다.

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.jhkim.jhkimvelog">
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> // this line
   <application
        android:label="jhkim.log"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher"
        android:requestLegacyExternalStorage="true" // this line
        android:networkSecurityConfig="@xml/network_security_config">
    ...
    </application>
</manifest>

본격적으로 flutter_inappwebview 에서 download를 해보자.



3. flutter_inappwebview onDownloadStartRequest

이제 flutter_downloader를 사용할건인데, 공식사이트의 example 코드를 많이 참고했다.

  final ReceivePort _port = ReceivePort();

  ('vm:entry-point')
  static void downloadCallback(String id, DownloadTaskStatus status, int downloadProgress) {
    final SendPort send = IsolateNameServer.lookupPortByName('downloader_send_port')!;
    send.send([id, status, downloadProgress]);
  }

  
  void initState() {
    super.initState();

	...

    IsolateNameServer.registerPortWithName(_port.sendPort, 'downloader_send_port');
    _port.listen((dynamic data) {
      String id = data[0];
      DownloadTaskStatus status = data[1];
      int progress = data[2];
      setState((){ });
    });

    FlutterDownloader.registerCallback(downloadCallback);
  }

  
  void dispose() {
    IsolateNameServer.removePortNameMapping('downloader_send_port');
    super.dispose();
  }

webview를 사용하는 페이지 안에 넣어주면 될 것 같다.
file downloader는 background isolation으로 동작하기 때문에 이 과정이 꼭 필요하다.

flutter_inappwebview의 InAppWebView widget에는 onDownloadStartRequest 함수가 있는데, 이 부분에서 file downloader를 호출해줘야한다.
(위의 링크에는 onDownloadStart로 적혀있는데, android가 업데이트되면서 onDownloadStartRequest로 바뀐 모양이다. 더 이상 사용되지 않는 함수라고 나온다.)

onDownloadStartRequest: (InAppWebViewController controller, DownloadStartRequest downloadStartRequest) async {
	final directory = await getApplicationDocumentsDirectory();
    var savedDirPath = directory.path;

	await FlutterDownloader.enqueue(
    	url: downloadStartRequest.url.toString(),
        savedDir: savedDirPath,
        saveInPublicStorage: true,
        showNotification: true,
        openFileFromNotification: true,
    );
}

이 부분을 추가해주면 된다.
파일을 저장할 경로인 savedDir은 원하는 경로로 지정해주면 되는데, 일단 기본경로로 진행해줬다.
이 때, 중요한 점은 InAppWebView의 option 중 useOnDownloadStart를 true로 설정해줘야 한다.

InAppWebView(
	initialOptions: InAppWebViewGroupOptions(
    	crossPlatform: InAppWebViewOptions(
        	useOnDownloadStart: true, // this line
            ...

이 부분을 설정해줘야 onDownloadStartRequest 함수가 작동한다.

4. 마무리

download가 정상적으로 되는 것 확인할 수 있다.



아직 flutter도 앱 개발 시장에서 파이를 따지자면 많이 쓰는 편이 아닌데 그 중에 web view를 사용하는 사람도 적어서 그런지.. 생각보다 자료 찾기가 힘들다ㅜㅜ flutter_inappwebview에서 onDownloadStartRequest로 바뀐 것도 함수 설명에 나와서 알아채긴 했지만 찾아봐도 저걸 사용한 예시는 전무한 수준.. 아직 사용하는 사람이 없는 만큼 남들보다 조금 더 빠르게 시니어가 될 수 있는 기회일까 싶기도 하고~~ㅎ flutter가 업데이트하는 행보를 보면 더더 잘 될거라고 생각은 하는데 내 예상보다 속도가 느리다..ㅎ

3개의 댓글

comment-user-thumbnail
2023년 7월 7일

매우 감사합니다.

답글 달기
comment-user-thumbnail
2023년 7월 7일

flutter_downloader 1.10.4 기준 downloadcallback은 (String , DownloadTaskStatus , int ) 에서 (String , int , int ) 로 변경되어야 합니다. 그에 따라 DownloadTaskStatus status = DownloadTaskStatus(data[1]); 해주시면 됩니다.

답글 달기
comment-user-thumbnail
2024년 3월 11일

안녕하세요, 혹시 res/strings.xml 파일 생성하고 나서 따로 설정해줘야하는게 있을까요?? notification 알람에 다운로드가 완성되면 한국어가 아닌 complete 영어가 떠서요ㅜㅜ ... 디바이스 언어 설정도 한국어라서 질문드립니다.

답글 달기