Flutter - How to download files in WebView?
Asked Answered
B

4

17

I am using flutter_webview_plugin: ^0.3.8 but I have the same problem with webview_flutter: ^0.3.13.

In webview, I want to make use of a website which triggers a file download on successful completion of a captcha. However, I complete the captcha and nothing happens, no download.

Is there something in Flutter like a webview download listener ("webview.setDownloadListener")? I only need this for Android.

If not, is there a way of downloading files from a webview in Flutter?

Bowne answered 14/9, 2019 at 17:12 Comment(3)
Check out this answer if you are owner of code at html and js side - #56248042Gosh
Here did you find a solution, even I'm facing the same issue. Tried using different packages as well but no use, Need help!Lavin
No, in the end I just had to use webview natively.Bowne
K
7

A similar issue can be found here!

You can use my plugin flutter_inappwebview, which is a Flutter plugin that allows you to add inline WebViews or open an in-app browser window and has a lot of events, methods, and options to control WebViews. It can recognize downloadable files in both Android (using setDownloadListener) and iOS platforms!

I report here the same answer that I gave to the similar issue:

To be able to recognize downloadable files, you need to set the useOnDownloadStart: true option, and then you can listen the onDownloadStart event!

Also, for example, on Android you need to add write permission inside your AndroidManifest.xml file:

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

Then, you need to ask permission using the permission_handler plugin. Instead, to effectively download your file, you can use the flutter_downloader plugin.

Here is a complete example using http://ovh.net/files/ (in particular, the http://ovh.net/files/1Mio.dat as URL) to test the download:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';

Future main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await FlutterDownloader.initialize(
      debug: true // optional: set false to disable printing logs to console
  );
  await Permission.storage.request();
  runApp(new MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {
  InAppWebViewController webView;

  @override
  void initState() {
    super.initState();
  }

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('InAppWebView Example'),
        ),
        body: Container(
            child: Column(children: <Widget>[
          Expanded(
              child: InAppWebView(
            initialUrl: "http://ovh.net/files/1Mio.dat",
            initialHeaders: {},
            initialOptions: InAppWebViewGroupOptions(
              crossPlatform: InAppWebViewOptions(
                debuggingEnabled: true,
                useOnDownloadStart: true
              ),
            ),
            onWebViewCreated: (InAppWebViewController controller) {
              webView = controller;
            },
            onLoadStart: (InAppWebViewController controller, String url) {

            },
            onLoadStop: (InAppWebViewController controller, String url) {

            },
            onDownloadStart: (controller, url) async {
              print("onDownloadStart $url");
              final taskId = await FlutterDownloader.enqueue(
                url: url,
                savedDir: (await getExternalStorageDirectory()).path,
                showNotification: true, // show download progress in status bar (for Android)
                openFileFromNotification: true, // click on notification to open downloaded file (for Android)
              );
            },
          ))
        ])),
      ),
    );
  }
}

Here, as you can see, I'm using also the path_provider plugin to get the folder where I want to save the file.

Kilt answered 31/5, 2020 at 13:6 Comment(3)
Hi @Lorenzo Pichilli, this doesn't save files to the downloads folder. How do I go about doing that?Requiescat
This isn't related to the flutter_inappwebview plugin. What I wrote here was just an example. If you need to specify another directory, check the path_provider plugin documentation! However, it seems that the method getDownloadsDirectory is not supported on Android and iOS (see the official doc)Kilt
Okay got it! Thanks LorenzoRequiescat
A
2

just add this code to your AndroidManifest.xml

   <provider
            android:name="vn.hunghd.flutterdownloader.DownloadedFileProvider"
            android:authorities="${applicationId}.flutter_downloader.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
    </provider>

and add this code to your AndroidManifest.xml

it works to me

Ascendancy answered 19/4, 2021 at 1:44 Comment(2)
what is in your xml/provider_paths? Are you using any other plugins except the one for webview?Kallman
You can create xml file under res folder and then add this code in it. <?xml version="1.0" encoding="utf-8"?> <paths> <files-path name="share" path="external_files"/> </paths>Compeer
H
2

Here is the step-by-step solution to download files in the flutter web view app. Flutter web view does not allow downloading files and images directly same as a browser. Because your web view app should have storage access permission.

You may be using the webview_flutter or flutter_inappwebview plugin solution will be the same for both you will be getting a callback of the download request.

We will be using the following plugins to achieve download functionality in the flutter web view app :

android_path_provider (to access file storage path in android)

flutter_downloader (to listen to the download event and start download)

permission_handler ( for handling permissions)

path_provider ( for accessing storage path /directory)

Now please add the latest versions of the above plugins to your pubspec.yaml file or else run the following command in your terminal :

flutter pub add android_path_provider
flutter pub add flutter_downloader
flutter pub add permission_handler
flutter pub add path_provider

Initialize flutter FlutterDownloader in main.dart file :

    await FlutterDownloader.initialize(
      debug: false, // 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)
  );

Inside android> app >src > main > AndroidManifest.xml : Add these permissions to the manifest file :

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Inside tag of the manifest file add the following code :

 <provider
       android:name="vn.hunghd.flutterdownloader.DownloadedFileProvider"
       android:authorities="${applicationId}.flutter_downloader.provider"
       android:exported="false"
       android:grantUriPermissions="true">
       <meta-data
           android:name="android.support.FILE_PROVIDER_PATHS"
           android:resource="@xml/provider_paths"/>
   </provider>

now create a download_helper_functions.dart file and create these functions in that file :

late String localPath;

Future<void> prepareSaveDir() async {
  localPath = (await findLocalPath())!;
  final savedDir = Directory(localPath);
  bool hasExisted = await savedDir.exists();
  if (!hasExisted) {
    savedDir.create();
  }
  return ;
}

Future<String?> findLocalPath() async {
  var externalStorageDirPath;
  if (Platform.isAndroid) {
    try {
      externalStorageDirPath = await AndroidPathProvider.documentsPath;
    } catch (e) {
      final directory = await getExternalStorageDirectory();
      externalStorageDirPath = directory?.path;
    }
  } else if (Platform.isIOS) {
    externalStorageDirPath =
        (await getApplicationDocumentsDirectory()).absolute.path;
  }
  return externalStorageDirPath;
}

now inside you webview widget declaration or InAppWebView widget we have a callback onDownloadStart or onDownloadStartRequest so add this code to the download callback :

 onDownloadStartRequest: (controller, DownloadStartRequest request) async {
            //todo download catelog here
            FlutterDownloader.registerCallback(downloadCallback);
            final platform = Theme.of(context).platform;
            bool value = await _checkPermission(platform);
            if(value){
              await prepareSaveDir();
              {
                final taskId = await FlutterDownloader.enqueue(
                  url: request.url.toString(),
                  savedDir: localPath,
                  showNotification: true,
                  saveInPublicStorage: true,// show download progress in status bar (for Android)
                  openFileFromNotification: true, // click on notification to open downloaded file (for Android)
                );
              }
            }
          },

and now inside your dart where you have declared webview create these two functions :

  Future<bool> _checkPermission(platform) async {
if (Platform.isIOS) return true;
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
if (platform == TargetPlatform.android &&
    androidInfo.version.sdkInt! <= 28) {
  final status = await Permission.storage.status;
  // final status2 = await Permission.manageExternalStorage.status;
  if (status != PermissionStatus.granted) {
    final result = await Permission.storage.request();
    // final result2 = await Permission.manageExternalStorage.request();
    if (result == PermissionStatus.granted) {
      return true;
    }
  } else {
    return true;
  }
} else {
  return true;
}
return false;

}

static void downloadCallback( String id, DownloadTaskStatus status, int progress) { final SendPort send = IsolateNameServer.lookupPortByName('downloader_send_port')!; send.send([id, status, progress]); }

Now rebuild your app and test. download files will work now.

Hope this will help.

Feel free to ask doubt (if any in the comment section).

It takes a lot of time and effort to write solutions please upvote if this helps.

Hanghangar answered 29/3, 2023 at 10:36 Comment(3)
I haven't tried this yet, but was wondering if there are changes that needs to be done on ios for capabilities or permissions same as android?Pelaga
I do not have onDownloadStart inside my WebView widget, and I am using "webview_flutter: ^3.0.4"Merlinmerlina
Tive mudar para InAppWebView.Detonation
P
0

it works for me

require plugin https://pub.dev/packages/url_launcher

add this code to your project to download file from flutter webview

onDownloadStart: (controller, url,) async {
                    // print("onDownloadStart $url");
                    final String _url_files = "$url";
                    void _launchURL_files() async =>
                        await canLaunch(_url_files) ? await launch(_url_files) : throw 'Could not launch $_url_files';
                    _launchURL_files();
                  },
Persnickety answered 12/5, 2022 at 10:19 Comment(1)
where do you add this code? flutter webview does have this api call. I only see: onPageStarted: (String url) {}, onPageFinished: (String url) {}, onWebResourceError: (WebResourceError error) {}, onNavigationRequest:Salientian

© 2022 - 2024 — McMap. All rights reserved.