26

I am new to Flutter and working in a flutter web application, My requirement is to create and download a text file. like below.

void getData() {
    List<int> bytes = utf8.encode('this is the text file');
    print(bytes); // Need to download this with txt file.
}

Can anyone help me to achieve this

Spatz
  • 18,640
  • 7
  • 62
  • 66
Chinnu
  • 556
  • 1
  • 5
  • 12

5 Answers5

58

This method is based on manipulations with an HTML document. Some additional packages should be imported:

import 'dart:convert';
import 'dart:html' as html; // or package:universal_html/prefer_universal/html.dart

Code snippet:

final text = 'this is the text file';

// prepare
final bytes = utf8.encode(text);
final blob = html.Blob([bytes]);
final url = html.Url.createObjectUrlFromBlob(blob);
final anchor = html.document.createElement('a') as html.AnchorElement
  ..href = url
  ..style.display = 'none'
  ..download = 'some_name.txt';
html.document.body.children.add(anchor);

// download
anchor.click();

// cleanup
html.document.body.children.remove(anchor);
html.Url.revokeObjectUrl(url);

Here is DartPad demo.

Spatz
  • 18,640
  • 7
  • 62
  • 66
  • 1
    Thanks! It feels a bit hacky but it works. Unfortunately, I don't get the "Save As" dialog in Chrome, it just starts downloading a file. And I have "Ask where to save each file before downloading" setting turned ON. – Oleksii Shliama Feb 15 '20 at 16:09
  • @OleksiiShliama If you look into the FileSaver.js library its actually doing the exact same thing what Spatz has done :) . check [here](https://github.com/eligrey/FileSaver.js/blob/master/src/FileSaver.js#L72). Also the save as dialog appears for me in chrome. Could be a version issue..? – Abhilash Chandran Feb 18 '20 at 11:48
  • 2
    DartPad download button does not download any file? – Abdullah Khan Jun 20 '20 at 13:08
  • @Abdullah Khan thanks! Indeed blocked in chrome because of [new security restrictions](https://www.chromestatus.com/feature/5706745674465280) but still works in Firefox. – Spatz Jun 20 '20 at 14:14
  • @Spatz thanks for the information. Is there any way to download files using flutter web? – Abdullah Khan Jun 20 '20 at 14:33
  • 1
    @Abdullah Khan this code still works - problems only with sandboxed iframe (dartpad uses one internally). – Spatz Jun 20 '20 at 14:53
  • @Spatz Their is a small problem. If an API is returning a file URL in form of bytes(Uint8List). Api can return .jpg, .png, .pdf, .mp3 etc. Now how to download this type of file and how to convert URL to bytes? – Abdullah Khan Jun 20 '20 at 15:26
  • @Abdullah Khan this is beyond the scope of original problem. Feel free to ask new question, but be more specific about api and file formats - it's a very broad topic. – Spatz Jun 20 '20 at 16:03
  • I have the problem using `final bytes = utf8.encode(text)`. "Líder" results in "Líder" on file. I tried `final bytes = text.codeUnits` to get unicode but the results does not convert to caracter, it stay a list of int numbers "[71, 114, 117, 112,..]" on file. Is there a way to resolve this problem? – Muka Nov 03 '20 at 01:02
  • Yeah I think this really only works for ascii text. – Hacker Mar 24 '22 at 12:45
  • Thanks a lot! This works for pdf files too if you can get the pdf content in bytes to be placed in the Blob – Kevin Koo Oct 02 '22 at 16:45
  • Hi @Spatz this answer works. However, I have a question, how do I choose where to download the file? – My Car Nov 01 '22 at 13:18
  • @MyCar for security reason, it can't be done from code. – Spatz Nov 01 '22 at 15:53
16

Got another way to do it, via popular JS library called FileSaver

First, update your ProjectFolder/web/index.html file to include the library and define the webSaveAs function like so:

...

<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.min.js"> 
</script>

<script>
function webSaveAs(blob, name) {
  saveAs(blob, name);
}
</script>

<script src="main.dart.js" type="application/javascript"></script>

...

Then you can call this function from Dart code like so:

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

...

js.context.callMethod("webSaveAs", [html.Blob([bytes]), "test.txt"])
M.javid
  • 6,387
  • 3
  • 41
  • 56
Oleksii Shliama
  • 1,325
  • 10
  • 22
  • there's a small typo in the final line. Here it is with the list literals as well js.context.callMethod('webSaveAs', [ html.Blob(>[bytes]), 'test.txt' ]); (you can get rid of and > if your linter isn't giving you a warning. – William Terrill Mar 25 '21 at 19:40
13

If you are looking to simply save a text file, this method is more straight forward than having to deal with all those conversions:

import 'dart:convert' show utf8;

// ignore: avoid_web_libraries_in_flutter
import 'dart:html' show AnchorElement;

void saveTextFile(String text, String filename) {
  AnchorElement()
    ..href = '${Uri.dataFromString(text, mimeType: 'text/plain', encoding: utf8)}'
    ..download = filename
    ..style.display = 'none'
    ..click();
}

You can change the mimeType and encoding as you see fit. For instance, I had to generate CSV files in a recent project, so I used mimeType: 'text/csv'.

Gregory Conrad
  • 1,324
  • 11
  • 19
  • How can I make this work with any file type? – markhorrocks Sep 11 '22 at 18:12
  • @markhorrocks You can simply just set the filename and the mimeType for the corresponding file type. Note that what I detailed above only works for text-based files. If you are dealing in binary data, you will need to use a different approach (such as replacing the `Uri.dataFromString` above with [`Uri.dataFromBytes`](https://api.dart.dev/stable/2.18.0/dart-core/Uri/Uri.dataFromBytes.html)). – Gregory Conrad Sep 11 '22 at 18:54
4

This solution uses FileSaver.js library and it should open the "saveAs" dialog.

I hope it works as intended:

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

...

final text = 'this is the text file';
final bytes = utf8.encode(text);

final script = html.document.createElement('script') as html.ScriptElement;
script.src = "http://cdn.jsdelivr.net/g/filesaver.js";

html.document.body.nodes.add(script);

// calls the "saveAs" method from the FileSaver.js libray
js.context.callMethod("saveAs", [
  html.Blob([bytes]),
  "testText.txt",            //File Name (optional) defaults to "download"
  "text/plain;charset=utf-8" //File Type (optional)
]);

 // cleanup
 html.document.body.nodes.remove(script);
Seif Karoui
  • 126
  • 5
3

I stumbled upon this and hardly found anything but this worked for me as of the latest flutter version 2.5.0.

you have to add this script in the body of your web/index.html file

<script src="https://cdnjs.cloudflare.com/ajax/libs/amcharts/3.21.15/plugins/export/libs/FileSaver.js/FileSaver.min.js"></script> 

and then just use this method to download any type of file

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

void save(Object bytes, String fileName) {
  js.context.callMethod("saveAs", <Object>[
    html.Blob(<Object>[bytes]),
    fileName
  ]);
}

e.g I wanted to download a JSON response from the server so I called it like this.

save(json.encode(response.data), 'file.json');

With this method you can almost download any type of file, I did use this method to download images and JSON files and it is pretty straightforward.

Mahesh Jamdade
  • 17,235
  • 8
  • 110
  • 131