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.