flutter_omni_downloader 0.1.0 copy "flutter_omni_downloader: ^0.1.0" to clipboard
flutter_omni_downloader: ^0.1.0 copied to clipboard

Request-aware Flutter downloader with resumable transfers and background support.

example/lib/main.dart

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_omni_downloader/flutter_omni_downloader.dart';

void main() {
  runApp(const DownloaderExampleApp());
}

class DownloaderExampleApp extends StatelessWidget {
  const DownloaderExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Omni Downloader',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF0B6E4F)),
        useMaterial3: true,
      ),
      home: const DownloaderHomePage(),
    );
  }
}

class DownloaderHomePage extends StatefulWidget {
  const DownloaderHomePage({super.key});

  @override
  State<DownloaderHomePage> createState() => _DownloaderHomePageState();
}

class _DownloaderHomePageState extends State<DownloaderHomePage> {
  final TextEditingController _urlController = TextEditingController(
    text: 'https://speed.hetzner.de/100MB.bin',
  );
  final TextEditingController _tokenController = TextEditingController();

  StreamSubscription<DownloadProgress>? _subscription;
  DownloadProgress? _progress;
  String? _taskId;

  @override
  void initState() {
    super.initState();
    _subscription = UniversalDownloader.progressStream.listen((event) {
      if (!mounted) {
        return;
      }
      setState(() {
        _progress = event;
      });
    });
  }

  @override
  void dispose() {
    _subscription?.cancel();
    _urlController.dispose();
    _tokenController.dispose();
    super.dispose();
  }

  Future<void> _startDownload() async {
    final headers = <String, String>{
      'Accept': '*/*',
    };
    if (_tokenController.text.isNotEmpty) {
      headers['Authorization'] = 'Bearer ${_tokenController.text}';
    }

    final taskId = await UniversalDownloader.start(
      DownloadRequest(
        url: _urlController.text,
        fileName: 'sample_${DateTime.now().millisecondsSinceEpoch}.bin',
        headers: headers,
        enableResume: true,
        enableBackground: true,
        showNotification: true,
      ),
    );

    setState(() {
      _taskId = taskId;
    });
  }

  @override
  Widget build(BuildContext context) {
    final progress = _progress;
    return Scaffold(
      appBar: AppBar(title: const Text('Omni Downloader')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            TextField(
              controller: _urlController,
              decoration: const InputDecoration(
                labelText: 'Download URL',
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 12),
            TextField(
              controller: _tokenController,
              decoration: const InputDecoration(
                labelText: 'Bearer token (optional)',
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 16),
            FilledButton(
              onPressed: _startDownload,
              child: const Text('Start download'),
            ),
            const SizedBox(height: 16),
            if (_taskId != null) ...<Widget>[
              Text('Task: $_taskId'),
              const SizedBox(height: 8),
              Row(
                children: <Widget>[
                  Expanded(
                    child: OutlinedButton(
                      onPressed: () => UniversalDownloader.pause(_taskId!),
                      child: const Text('Pause'),
                    ),
                  ),
                  const SizedBox(width: 12),
                  Expanded(
                    child: OutlinedButton(
                      onPressed: () => UniversalDownloader.resume(_taskId!),
                      child: const Text('Resume'),
                    ),
                  ),
                  const SizedBox(width: 12),
                  Expanded(
                    child: OutlinedButton(
                      onPressed: () => UniversalDownloader.cancel(_taskId!),
                      child: const Text('Cancel'),
                    ),
                  ),
                ],
              ),
            ],
            const SizedBox(height: 24),
            if (progress != null) ...<Widget>[
              LinearProgressIndicator(value: progress.progress / 100),
              const SizedBox(height: 12),
              Text('Status: ${progress.status.name}'),
              Text('Progress: ${progress.progress}%'),
              Text(
                'Bytes: ${progress.downloadedBytes} / ${progress.totalBytes}',
              ),
              Text('Speed: ${progress.speed.toStringAsFixed(2)} MB/s'),
            ],
          ],
        ),
      ),
    );
  }
}