7

I am Using the flutter_webview_plugin Package for displaying Web Page. On the Webpage, we had a feature to download files. the logic for the Downloading File is Similar to the below code

 filename = "my-file.txt"
 content = 'any string generated by django'
 response = HttpResponse(content, content_type='text/plain')
 response['Content-Disposition'] = 'attachment;filename={0}'.format(filename) 
 print(response)
 return response

It is working fine when it is called directly in the browser but had no effect when it comes to Flutter Webview. Is there anything to be done manually to process it? Even I am open to change the Package. I tried flutter_inappwebview also but no difference. I don't want to Launch an URL to any browser I just want to download directly from the application. is it possible to do so?

Hardik
  • 373
  • 2
  • 14
Varun
  • 1,658
  • 1
  • 7
  • 11

3 Answers3

1

I had a similar problem while trying to implement a download feature that downloads and saves a CSV from the backend to the local app location. There are a few reasons why url_launcher wouldn't work for my case some of them are

  • The downloads here will be authenticated (using JWT in the backend) which requires a header value of access-token which cannot be set in url_launcher since the headers will already be set when the user launches the URL in the launch view.
  • The return type is not a web view file rather it is a byte-stream data from the backend. If this is your logic then url_launcher will just show a white page and crash right away since the byte stream cannot be opened in web view.

Hence I would like to recommend this strategy of writing files as bytes into the device using dart:io functions

the dependencies we would need are

  • permission_handler: ^10.2.0
  • api_cache_manager: ^1.0.1 (only if you are using the local sqlite storage)

Backend logic:

const functionToGetCSV = async (req, res) => {
    try {
        const data = await inventory.findAll({raw: true});
        sendCSV(res, "Backend Data" + dateForFilename(), data);
        return;
    } catch (error) {
        logger.error("server error", { error });
        return res.status(500).send({ message: "Server Error. Try again." });
    }
}

Frontend logic: I had the following export option in the three-dot button which will initiate the download process

  1. Export buttonenter image description here This gets executed as soon as the export is clicked

     SelectedItem(BuildContext context, int item) {
     if (item == 2) {
       _launchUrl();
     } else if (item == 3) {
       _logout();
     }}
    
  2. The launch URL logic

     Future<void> _launchUrl() async {
     String baseUrl = 'http://${Global.endpoint}';
     var url = Uri.parse('$baseUrl/api/csv/get-inventory');
     var token = await APICacheManager().getCacheData("login_details");
     var token1 = json.decode(token.syncData);
     var client = http.Client();
     final response = await client.get(url, headers: <String, String>{
       'Access-Token': token1["data"]["token"],
     });
     if (await _requestPermission(Permission.storage)) {
       try {
         // ignore: await_only_futures
         DateTime datetime = await DateTime.now();
         // ignore: unnecessary_new
         File file = await new File(
                 'storage/emulated/0/ERP/${datetime.hour.toString()}${datetime.minute.toString()}${datetime.second.toString()}get-inventory.csv')
             .create(recursive: true);
         await file.writeAsBytes(response.bodyBytes,
             flush: true, mode: FileMode.write);
         final snackBar = SnackBar(
             backgroundColor: const Color.fromARGB(255, 36, 198, 30),
             content: Text(
                 'File Saved! ${file.path}'));
         ScaffoldMessenger.of(context).showSnackBar(snackBar);
       } catch (err) {
         final snackBar = SnackBar(
             backgroundColor: const Color.fromARGB(255, 198, 30, 30),
             content: Text("Error saving file! ${err.toString()}"));
         ScaffoldMessenger.of(context).showSnackBar(snackBar);
       }
     } else {
       _launchUrl();
     }}
    

Make sure:

  1. To keep the await for DateTime.Now() as I found in some emulators there might be a delay in calling the DateTime.Now() (ps: yes it was weird as the bug vanished after I gave await for this function)

  2. To keep the new File and .create(recursive:true); since this would create com.package_name folder in case you don't have one by default

  3. To add this request permission function

     Future<bool> _requestPermission(Permission permission) async {
     var res = await permission.request();
     if (res == PermissionStatus.granted) {
       return true;
     } else {
       return false;
     }}
    
  4. you will be able to see your downloads saved here Internal storage has your app folder Inside that you can find your saved files

  5. Do let me know if this works for you as this will be very easy for downloaded authenticated files.

0

I suggest you try this package, flutter_inappwebview, for better performance:

  final GlobalKey webViewKey = GlobalKey();

  InAppWebViewController? webViewController;
  InAppWebViewGroupOptions options = InAppWebViewGroupOptions(
      crossPlatform: InAppWebViewOptions(
        useShouldOverrideUrlLoading: true,
        mediaPlaybackRequiresUserGesture: false,
      ),
      android: AndroidInAppWebViewOptions(
        useHybridComposition: true,
      ),
      ios: IOSInAppWebViewOptions(
        allowsInlineMediaPlayback: true,
      ));

...

                      InAppWebView(
                        key: webViewKey,
                        initialUrlRequest:
                        URLRequest(url: Uri.parse("https://inappwebview.dev/")),
                        initialOptions: options,
Jim
  • 6,928
  • 1
  • 7
  • 18
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

InAppWebView(
            // ....
            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();
            },
          ),
Dimas
  • 39
  • 3
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community May 12 '22 at 20:34