5

I'm new in flutter. I am working on a mobile app that displays certain pages of a website, one of the pages has a link through which users/visitors can download a PDF file. This link works in most regular browsers I have tried, but when I click it in my app, it doesn't. I am using "WebviewScaffold" to display the pages. Please how do I solve this problem. Thanks

Willy
  • 51
  • 1
  • 2

3 Answers3

4

A similar issue can be found here and here! I report here the same answer that I gave to the similar issue:

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!

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.

Lorenzo Pichilli
  • 2,896
  • 1
  • 27
  • 50
0

Flutter webview is currently in developers preview and there is a lot of functionalities that are not developed yet, including downloading files. If the download works in regular browsers you might wanna use the url_launcher package instead of a webview to open your url in your phone's browser and download it.

Guillaume Roux
  • 6,352
  • 1
  • 13
  • 38
0

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.

Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
MANISH
  • 2,883
  • 4
  • 11
  • 30