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
3 Answers
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.

- 2,896
- 1
- 27
- 50
-
How can we download if data is coming from API call? – BC TUBE May 04 '23 at 11:27
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.

- 6,352
- 1
- 13
- 38
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.