How to Use Streams to Download a File in Flutter

By | December 22, 2024

In Flutter, Streams are a powerful way to handle asynchronous data like file downloads. When downloading a file, streams can help you process chunks of data in real time, making it easy to show progress to the user.

Here’s a step-by-step guide on how to download a file using streams in Flutter.

Add Required Dependencies

Include the http package in your pubspec.yaml file for handling HTTP requests.

dependencies:
http: ^0.15.0

Run flutter pub get to fetch the package.

Import Required Libraries

import 'dart:async';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart';

Download a File Using Streams

Here’s the complete code to download a file and track its progress using a stream.

import 'dart:io';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart';

class FileDownloadExample extends StatefulWidget {
  @override
  _FileDownloadExampleState createState() => _FileDownloadExampleState();
}

class _FileDownloadExampleState extends State<FileDownloadExample> {
  double _progress = 0.0;
  String _status = "Not started";

  Future<void> downloadFile(String url, String fileName) async {
    setState(() {
      _status = "Starting download...";
      _progress = 0.0;
    });

    try {
      // Get the file path
      final directory = await getApplicationDocumentsDirectory();
      final filePath = '${directory.path}/$fileName';
      final file = File(filePath);

      // Send the HTTP GET request
      final response = await http.Client().send(http.Request('GET', Uri.parse(url)));

      // Get the total file size
      final totalBytes = response.contentLength ?? 0;

      // Open a file for writing
      final output = file.openWrite();

      // Listen to the response stream and write chunks to the file
      int receivedBytes = 0;
      response.stream.listen(
        (chunk) {
          receivedBytes += chunk.length;
          output.add(chunk); // Write the data to the file

          // Update progress
          setState(() {
            _progress = receivedBytes / totalBytes;
            _status = "Downloading... ${(100 * _progress).toStringAsFixed(2)}%";
          });
        },
        onDone: () async {
          await output.close();
          setState(() {
            _status = "Download complete! File saved at $filePath";
          });
        },
        onError: (e) {
          setState(() {
            _status = "Download failed: $e";
          });
        },
        cancelOnError: true,
      );
    } catch (e) {
      setState(() {
        _status = "Error: $e";
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("File Download with Streams")),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            LinearProgressIndicator(value: _progress),
            SizedBox(height: 20),
            Text(_status),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () => downloadFile(
                "https://example.com/sample.pdf", // Replace with your file URL
                "sample.pdf",
              ),
              child: Text("Download File"),
            ),
          ],
        ),
      ),
    );
  }
}

void main() => runApp(MaterialApp(home: FileDownloadExample()));

Code Explanation

1. URL Request:

• The http.Client().send() method fetches the file as a stream.

• The response.stream provides chunks of data as they arrive.

2. Progress Tracking:

• Calculate progress by dividing receivedBytes by totalBytes.

• Update the progress with setState() to reflect it in the UI.

3. File Writing:

• Use File.openWrite() to create an output sink.

• Write each data chunk to the file as it’s received.

4. Error Handling:

• Listen for errors and cancel the stream if needed.

 Key Advantages of Using Streams

• Efficient memory usage as chunks are processed incrementally.

• Real-time progress tracking.

• Handles large files without freezing the UI.

Leave a Reply

Your email address will not be published. Required fields are marked *