Abu Sayed 9th May 2023

Introduction

Downloading files is a common feature in most apps, and it can be useful in scenarios where the user needs to access content offline or share the downloaded file with others.

Overview

In this tutorial, we’ll explore how to download any file in Flutter using the Dart programming language. We’ll use the path_provider package to get the path to the download directory and the HttpClient class to initiate the download process.

UI Explanation

The code starts by importing the necessary packages, including dart:io for file-related operations, and flutter/material.dart for the UI components.

We then define a StatefulWidget named FileDownloaderScreen that represents our main screen. This widget has two fields, downloadedFile and downloadMessage, which we’ll use to track the downloaded file and the download status, respectively.

Next, we override the build method to define our UI. We first get the screen’s dimensions using the MediaQuery class, and then we create a SafeArea and Scaffold widget to contain our UI components.

<code class="language-dart">
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';

void main() => runApp(const MaterialApp(home: FileDownloaderScreen()));

class FileDownloaderScreen extends StatefulWidget {
  const FileDownloaderScreen({Key? key}) : super(key: key);

  @override
  State createState() => _FileDownloaderScreenState();
}

class _FileDownloaderScreenState extends State {
  File? downloadedFile;
  String downloadMessage = "Press download";

  @override
  Widget build(BuildContext context) {
    double width = MediaQuery.of(context).size.width;
    double height = MediaQuery.of(context).size.height;
    double fontSize = MediaQuery.of(context).textScaleFactor;

    return SafeArea(
      child: Scaffold(
        body: SizedBox(
          width: width,
          height: height,
          child: Column(
            children: [
              ///
              /// downloaded image builder
              Container(
                width: width,
                height: height * 0.6,
                color: Colors.teal.shade100,
                alignment: Alignment.center,
                child: Container(
                  alignment: Alignment.center,
                  width: width * 0.8,
                  height: height * 0.4,
                  child: downloadedFile != null ? Image.file(downloadedFile!) : Text(downloadMessage),
                ),
              ),

              SizedBox(
                height: height * 0.1,
              ),

              ///
              /// download now button
              InkWell(
                onTap: () {
                  String url = "https://docs.flutter.dev/assets/images/dash/dash-fainting.gif";
                  String filename = "my_file_name";
                  downloadFile(
                    url: url,
                    filename: filename,
                  );
                },
                child: Container(
                  width: width * 0.8,
                  height: height * 0.05,
                  color: Colors.teal.shade100,
                  alignment: Alignment.center,
                  child: Text(
                    'Download',
                    style: TextStyle(
                      fontSize: fontSize * 20,
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }


  ///
  /// download file function
  Future downloadFile({
    required String url,
    required String filename,
  }) async {
    try {
      HttpClient client = HttpClient();
      List downloadData = [];

      Directory downloadDirectory;

      if (Platform.isIOS) {
        downloadDirectory = await getApplicationDocumentsDirectory();
      } else {
        downloadDirectory = Directory('/storage/emulated/0/Download');
        if (!await downloadDirectory.exists()) downloadDirectory = (await getExternalStorageDirectory())!;
      }

      String filePathName = "${downloadDirectory.path}/$filename";
      File savedFile = File(filePathName);
      bool fileExists = await savedFile.exists();

      if (fileExists && mounted) {
        ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("File already downloaded")));
      } else {
        client.getUrl(Uri.parse(url)).then(
          (HttpClientRequest request) {
            setState(() {
              downloadMessage = "Loading";
            });
            return request.close();
          },
        ).then(
          (HttpClientResponse response) {
            response.listen((d) => downloadData.addAll(d), onDone: () {
              savedFile.writeAsBytes(downloadData);
              setState(() {
                downloadedFile = savedFile;
              });
            });
          },
        );
      }
    } catch (error) {
      setState(() {
        downloadMessage = "Some error occurred -> $error";
      });
    }
  }
}

</code>

Inside the Scaffold widget, we define a Column widget that contains two components: a container for displaying the downloaded file, and a button for initiating the download process.

The container has a fixed height and width and displays either the downloaded file or the download message, depending on whether a file has been downloaded or not. We use the Image.file() method to display the downloaded file if it exists, or a Text widget with the downloadMessage if it doesn’t.

The button is defined using an InkWell widget, which provides a visual feedback when the user taps on it. When the user taps the button, we call the downloadFile() method, passing in the URL of the file to download and a filename to save it as.

Download File Function Clarification

The downloadFile() method is where the actual download happens. We start by creating an instance of the HttpClient class, which we’ll use to make the download request. We then create an empty list downloadData to store the downloaded file’s bytes.

Next, we use the Platform class to determine whether the app is running on an iOS or Android device, and get the appropriate download directory using the getApplicationDocumentsDirectory() or getExternalStorageDirectory() method, respectively.

Once we have the download directory, we check if the file we want to download already exists by calling the exists() method on a File object that represents the file. If the file exists, we display a SnackBar widget to inform the user that the file has already been downloaded.

If the file doesn’t exist, we use the HttpClient class to initiate the download process. We call the getUrl() method on the HttpClient instance, passing in the URL of the file to download. This method returns a Future object, which we can then use to set additional request headers, such as cookies or authentication tokens.

In our case, we don’t need any additional headers, so we simply call the close() method on the HttpClientRequest object, which returns a Future object. We then listen for data events on the response object, which gives us access to the downloaded file’s bytes. We append each chunk of data to the downloadData list using the addAll() method, and then we use the File class’s writeAsBytes() method to write the downloaded bytes to a new file with the specified filename.

Flutter based iOS & Android App Development

Codeboxr’s Flutter mobile app development services offer a comprehensive range of offerings to cater to different stages of the app development lifecycle. From consultation and architecture design to UI/UX design, app development, testing, deployment, and ongoing maintenance and support, Codeboxr provides end-to-end services to ensure a seamless and successful app development experience.

Learn More

Finally, we set the downloadedFile field to the newly created File object, which triggers a rebuild of the UI. The downloaded file is then displayed in the container widget we defined earlier.

Flutter Package by Codeboxr

Do you know we developed a flutter package for bKash payment in flutter. Try that and let us know your feedback.



Blog Ref: 33955