Downloading PDF file in the user download directory on Android is a very common feature, a lot of app uses PDF viewing and downloading feature.

I hoped that flutter plugins could solve my problem but they didn’t. Fortunately, it was working great on iOS but it was not on android. So I decided to use my old java code in Flutter using Method Channel.

First I tried two Flutter plugins one for permission and one for creating a directory.

flutter pub add path_provider
flutter pub add permission_handler

Request permission was very easy and it worked on both iOS and Android

Future<bool> _requestWritePermission() async {
    await Permission.storage.request();
    var r = await Permission.storage.request();

    return r.isGranted;
  }

Since it was working on iOS, I decided to leave that code and separate platform code by importing ‘dart:io’ show Platform. Use this code on your onTap action.

static const channel = MethodChannel('com.flutterframework/test');

bool hasPermission = await _requestWritePermission();

if (hasPermission) {
  if (Platform.isAndroid) {
    final int downloader = await channel.invokeMethod(
      'download',
      <String, String>{'title': title, 'pdf': pdf},
    );
    print(downloader);
  }

  if (Platform.isIOS) {
    var dir = await getApplicationDocumentsDirectory();
    if (dir != null) {
      String saveName = "$title.pdf";
      String savePath = "${dir.path}/$saveName";
      print(savePath);

      try {
        await Dio().download(pdf, savePath,
            onReceiveProgress: (received, total) {
          if (total != -1) {
            var percentage = (received / total * 100);
            print(percentage);
            if (percentage >= 100.0) {
              ScaffoldMessenger.of(context).showSnackBar(
                  snackMessage(
                      'Download completed, please open Files app.'));
            }
          }
        });
        print("File is saved to download folder.");
      } on DioError catch (e) {
        ScaffoldMessenger.of(context).showSnackBar("Download error");
      }
    }
  }
} else {
  ScaffoldMessenger.of(context).showSnackBar(filePermissionError);
}

Finally, the Java code, create a Java class in your main directory.

package your_package;

import android.app.DownloadManager;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;

public class DownloadHelper {
    public static void downloadPDF(Context context, String title, String pdfUrl) {
        try {
            DownloadManager.Request request = new DownloadManager.Request(Uri.parse(pdfUrl));
            request.setDescription("MyApp");
            request.setTitle(title);
            // in order for this if to run, you must use the android 3.2 to compile your app
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                request.allowScanningByMediaScanner();
                request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
            }

            if (Build.VERSION.SDK_INT <= 17) {
                request.setDestinationInExternalFilesDir(context, "Documents", "MyFolder/" + title + ".pdf");
            } else {
                request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "MyFolder/" + title + ".pdf");
            }

            // get download service and enqueue file
            DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
            manager.enqueue(request);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
    }
}

Call that Java code from MainActivity, my MainActivity in Kotlin

class MainActivity: FlutterActivity() {
    private val CHANNEL = "com.flutterframework/test";
    private lateinit var channel: MethodChannel

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
        channel.setMethodCallHandler { call, result ->
            if (call.method == "download") {
                val pdfTitle = call.argument<String>("title")
                val pdfUrl = call.argument<String>("pdf")
                Log.d("TAG", pdfTitle.toString());
                Log.d("TAG", pdfUrl.toString());

                DownloadHelper.downloadPDF(this, pdfTitle, pdfUrl);
            }
        }
    }
}

Download the complete project on GitHub

Spread the love

Leave a comment