Flutter WEB download option
Asked Answered
M

14

60

I am making flutter web app that should generate a file from user data. And have the option to download the output file.

But I can not find any options/packages which works for flutter web :(

Can someone please help me out?

Melchizedek answered 17/1, 2020 at 8:5 Comment(5)
What kind of data do you want to download CSV, Pdf etc ?Fearsome
Another problem I'm having is using a single code base for the Flutter and Web app. Trying to compile it on Flutter doesn't like the dart:html plugins that the answers below have.Cady
Is this helpful to your case @Suragch?Evite
@TiagoMartinsPeres, Although I am not using Firebase, the source code in the answer's link was interesting. Thank you. I was hoping for a simpler solution, but Flutter Web is still in beta. Hopefully easier cross-platform solutions will be provided in the future.Cady
If your project supports mobile and web, I've explained the solution here: hesam-kamalan.medium.com/…Crimpy
C
62

Simple Code for redirecting to download URL

import 'dart:html' as html;
void downloadFile(String url) {
   html.AnchorElement anchorElement =  new html.AnchorElement(href: url);
   anchorElement.download = url;
   anchorElement.click();
}
Column answered 10/3, 2020 at 9:5 Comment(9)
Since this answer came late in the bounty period I don't have time to try it before the bounty expires. However, I'll assume that it works (but can you confirm that it would download an audio file without just playing it automatically?). I'm choosing this answer because it is easy to understand.Cady
This is specifically for flutter web. Is there any alternative for mobile app?Sanitarian
In Android you can download file from url and save it to your phone storage using IO library.Column
This does not work. The browser opens the file rather than downloading it.Doehne
This worked but it only saves with the filename the url gave ... - on mac, chrome browserGranados
I think you can set the anchorElement.download = fileName to set a name unlike the url.Gusta
for me it worked, thanks a lot! the only point: I had to modify URL for running in release - like if (!isInDebug) {url = "assets/$url} just look on the file structure of the release build and you will get the pointRutilant
Thanks @vishwajit76... it works perfect for Web, but an error appears if you have a cross-platform code because dart:html doesn't support Mobile. What do you recommend in this case?Awesome
For cross-platform, you can use conditional import.see this post - medium.com/globant/support-flutter-cross-platform-b55ea3cccf41Column
L
54

you can use package url_launcher with url_launcher_web

then you can do:

launchUrl(Uri.parse("data:application/octet-stream;base64,${base64Encode(yourFileBytes)}"));

EDIT: you don't need a plugin if you do this

download.dart:

import 'dart:convert';
// ignore: avoid_web_libraries_in_flutter
import 'dart:html';

void download(
  List<int> bytes, {
  String downloadName,
}) {
  // Encode our file in base64
  final _base64 = base64Encode(bytes);
  // Create the link with the file
  final anchor =
      AnchorElement(href: 'data:application/octet-stream;base64,$_base64')
        ..target = 'blank';
  // add the name
  if (downloadName != null) {
    anchor.download = downloadName;
  }
  // trigger download
  document.body.append(anchor);
  anchor.click();
  anchor.remove();
  return;
}

empty_download.dart:

void download(
  List<int> bytes, {
  String downloadName,
}) {
  print('I do nothing');
}

import and use:

import 'empty_download.dart'
if (dart.library.html) 'download.dart';

void main () {
  download('I am a test file'.codeUnits, // takes bytes
      downloadName: 'test.txt');
}
Langsdon answered 29/4, 2020 at 14:42 Comment(6)
thanks for your answer! Could you explain me the purpose of the 'empty_download.dart' ?Amendment
@Amendment That is alternative if dart.library.html is not available. The 1st 2 lines of the last part has to read together:import 'empty_download.dart' if (dart.library.html) 'download.dart';Mako
I have this erros when try to use url_lancher: The method 'launchUrl' isn't defined for the type 'x'. Try correcting the name to the name of an existing method, or defining a method named 'launchUrl'.Rizzo
This should be the accepted answer, works without any external package, and also produces no error when compiling for other platforms (for other platforms, download code needs to be implemented separately).Gertrudis
It's really worked for me. I was struggling with this issue for a long time. Thank you so much.Ramin
Be aware that It works on web but you will not be able to run your mobile app because of the unsupported imports "avoid_web_libraries_in_flutter" (if you have one)Rah
T
35

One way to trigger download is the adapt a common pattern used in "native" javascript, create an anchor element with the download attribute and trigger a click.

import 'dart:convert';
import 'dart:html';

main() {
  File file = // generated somewhere
  final rawData = file.readAsBytesSync();
  final content = base64Encode(rawData);
  final anchor = AnchorElement(
      href: "data:application/octet-stream;charset=utf-16le;base64,$content")
    ..setAttribute("download", "file.txt")
    ..click();
}

Troublesome answered 3/3, 2020 at 13:20 Comment(3)
Wow this one saved me BIG TIME! Thank you so much. This code could instantly download a pdf file I generated in Flutter.Sisneros
There is just an issue with downloading in the mobile chrome version, the file is not stored. Working like a charm on mobile safari.Hennessey
In my case, I need to download images either jpeg or png. In mobile chrome, the file is not recognized as an image. Is there a way to indicate this on htref?Junie
A
15

I've found a solution that let me make an authorized request to get a file (with package http.dart) and then download the file using flutter web (with package dart:html). I don't know if someone other could need this, but I wasn't able to find a solution, so here is the code.

import 'package:http/http.dart';
import 'dart:html' as html;

...

var headers = {
   'Content-Type': 'application/octet-stream',
   'Accept': 'application/octet-stream',
   'Authorization' : 'Bearer [TOKEN HERE]'
};

Response res =
      await get(url, headers: headers); 

if (res.statusCode == 200) {
    final blob = html.Blob([res.bodyBytes]);
    final url = html.Url.createObjectUrlFromBlob(blob);
    final anchor = html.document.createElement('a') as html.AnchorElement
      ..href = url
      ..style.display = 'none'
      ..download = filename;
    html.document.body.children.add(anchor);

    anchor.click();

    html.document.body.children.remove(anchor);
    html.Url.revokeObjectUrl(url);
}
Apologetic answered 26/9, 2020 at 8:45 Comment(0)
B
10

A good workaround is to open the hosted file in a new tab using

import 'dart:html' as html;

openInANewTab(url){
  html.window.open(url, 'PlaceholderName');
}

Fits well for my use case.

Baerman answered 29/2, 2020 at 18:23 Comment(1)
This works for downloading an APK file. It doesn't work for downloading an audio file because the audio file starts playing automatically.Cady
J
4

UPDATE: 17.05.2024

Flutter 3.22 and WebAssembly support announced in Google IO.

The solution above WILL NOT compile to WebAssembly for 'dart:html' is deprecated now. (source:https://docs.flutter.dev/platform-integration/web/wasm#js-interop-wasm). I updated my solution to their proposal using 'package:web':

import 'package:web/web.dart';
import 'package:flutter/foundation.dart';
    
void myPluginDownload(String url) {
    // when building in release the file structure changes ... 
    if (kReleaseMode) {
        url = "assets/$url";
    }
    HTMLAnchorElement()
      ..href = url
      ..download = url
      ..click();
}

Cheers!

Joinery answered 17/5, 2024 at 8:19 Comment(1)
@Richard nice formatting :) Didn't know about that one.Joinery
B
2

I've used the followig to download file from a URL,

import 'dart:html' as html;

void downloadFileFromDownloadableLink(String url, String fileName) {
  html.AnchorElement anchorElement = html.AnchorElement(href: url);
  anchorElement.download = fileName;
  anchorElement.click();
}
Berlinda answered 25/8, 2022 at 17:59 Comment(0)
M
1

Import html

import 'dart:html' as html;

Now Give link to your asset PDf file and name

html.window.open("assets/resume/fayyaz_resume.pdf", "fayyaz_resume.pdf");
Marked answered 20/12, 2023 at 7:30 Comment(0)
H
0

Response to bounty:

the case of an audio file it just starts playing rather than downloading it

I've done same thing with video, but I'm sure it will work with audio as well. I assume, your generated audio is an array or blob

    import 'dart:js' as JS;
    import 'dart:html' as HTML;

    const mimeType = 'audio/wav'; // TODO: Pick a right format

    void downloadFile(List chunks) async {
        final blob = HTML.Blob(chunks, mimeType);
        final objectUrl = await HTML.Url.createObjectUrlFromBlob(blob);
        final a = HTML.AnchorElement();
        a.href = item.url;
        a.download = "my_audio_file_${DateTime.now()}.wav";
        HTML.document.body.append(a);
        a.click();
        // Wait for click event to be processed and cleanup
        await Future.delayed(Duration(milliseconds: 100));
        a.remove();
        HTML.Url.revokeObjectUrl(item.videoObjectUrl);
    }
Haskell answered 4/3, 2020 at 10:10 Comment(3)
What if the audio file is url is http://www.example.com/my_audio.mp3?Cady
is a.download the desired custom file name?Granados
@Granados yes, it isHaskell
G
0

For me, vishwajit76's answer work for me. If you want the file to have a custom name once is downloaded then do the following

 `void downloadFile(String url) {
        html.AnchorElement anchorElement = html.AnchorElement(href: url);
        anchorElement.download = "myDocument.pdf"; //in my case is .pdf
        anchorElement.click();
      }`
Glorianna answered 29/4, 2022 at 6:2 Comment(0)
P
0

All other solutions are correct but this solutions may disclose your storage path structure. Because downloaded file name includes complete path name of the file. If your path has uid number of firebase users, this may also cause a security breach. This may be an extreme example but it is a good practice to use uid numbers as on file path to create a more secure storage. You can create a temporary download folder as (Download/useremail) on root and copy the file intended to download inside of (Download/useremail) as below node.js code by firebase functions. This function is called when user click the download button from client. You can delete the temporary download folder later.

const functions = require("firebase-functions");
const {Storage} = require("@google-cloud/storage");

const storage = new Storage();

exports.downloadFile = functions.https.onCall(async (data, context)=> {
  await storage.
      bucket("my-bucket-name").
      file(data.filePath).
      copy(storage.
          bucket("my-bucket-name").
          file(`Download/${data.useremail}/${data.fileName}`));
});
Portage answered 22/5, 2022 at 15:2 Comment(0)
C
0

This is how I implemented file download for flutter web. Note that it infers the mime type from the filename or contents which allows downloading any file type.

Create a download file named download.dart with the following contents.

import 'dart:convert';
import 'package:logger/logger.dart';
import 'package:http/http.dart' as http;
import 'package:universal_html/html.dart' as universal_html;
import 'package:mime/mime.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

var logger = Logger(
  printer: PrettyPrinter(),
);

Future<void> downloadFile(
    {required String url, required String fileName}) async {
  const storage = FlutterSecureStorage();

  String? token = await storage.read(key: 'jwt');

  try {
    Map<String, String> headers = {
      'Content-Type': 'application/octet-stream',
      'Accept': 'application/octet-stream',
      "Authorization": "Bearer $token",
    };

    final dataType = lookupMimeType(fileName);

    final http.Response r = await http.get(Uri.parse(url), headers: headers);

    final data = r.bodyBytes;

    final base64data = base64Encode(data);

    final a =
        universal_html.AnchorElement(href: 'data:$dataType;base64,$base64data');

    a.download = fileName;

    a.click();

    a.remove();
  } catch (e) {
    logger.d(e);
  }
}

Call it like this.

import 'package:myproject/ui/shared/download.dart' as download;

-----

bool firstPress = true;

-----

final downloadFileButton = Expanded(
  child: FilledButton(
    child: Container(
      padding: const EdgeInsets.all(12.0),
      child: const Text(
        'Download File',
        maxLines: 2,
        overflow: TextOverflow.ellipsis,
        textAlign: TextAlign.center,
      ),
    ),
    onPressed: () {
      if (firstPress) {
        firstPress = false;

        final url = 'my download url';

        final fileName = 'my filename';

        download.downloadFile(url: url, fileName: fileName);
      }
    },
  ),
);
Carbonari answered 12/10, 2023 at 21:47 Comment(0)
S
-1

( For Flutter Web only )

( Note: url is just path of your file in assets, Example: /assets/documents/fileName.pdf )

While working on project:

import 'dart:html';

void downloadFile(String url) {
   AnchorElement anchorElement = AnchorElement(href: url);
   anchorElement.download = "fileName";
   anchorElement.click();
}

Above code will only work in development and not in release version (Hosted project). Hosted project will give you .html file to download.

Release version:

import 'dart:html' as html;
import 'dart:ui_web' as ui;

void downloadFile(String url) {
    AnchorElement anchorElement =
    AnchorElement(href: ui.AssetManager().getAssetUrl(url));
    anchorElement.download = "fileName";
    anchorElement.click();
}

Above code will only work in release version and not in development version.

Explanation: The solution for the web is to use the AssetManager to retrieve the correct URL of the asset 'assets/documents/fileName.pdf'. Since flutter build web put all the assets/ folders into another assets folder in the tree.

So solution is to use first code while building and testing and use second code when it is time to build the web app.

If you want know more about this issue, here is the GitHub link: https://github.com/flutter/flutter/issues/126752#issuecomment-1550481365

Shoshana answered 3/1, 2024 at 13:51 Comment(0)
A
-3

You could also use the dio plugin, which supports this sort of thing out of the box. So you could just do:

response = await dio.download('url-to-downloadable', 'path-to-save-to');

And here's their API docs for the same.

Aliform answered 9/6, 2021 at 11:11 Comment(3)
This article is about flutter web. I don't think this works on web. Please correct my if I'm wrong.Firenze
What makes you say it isn't ?Aliform
In the documentation they say "Download the file and save it in local". So I think it requires local file acces to write the file and that is not possible in web.Firenze

© 2022 - 2025 — McMap. All rights reserved.