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

Resumable, fault-tolerant chunked file uploads for Dart, speaking the resumable.js protocol against a pion/laravel-chunk-upload backend.

example/lib/main.dart

import 'dart:io';

import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:pion_resumable_upload/pion_resumable_upload.dart';

void main() => runApp(const ExampleApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Resumable Upload Example',
      theme: ThemeData(colorSchemeSeed: Colors.indigo, useMaterial3: true),
      home: const UploadPage(),
    );
  }
}

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

  @override
  State<UploadPage> createState() => _UploadPageState();
}

class _UploadPageState extends State<UploadPage> {
  final TextEditingController _urlController = TextEditingController(
    text: 'https://api.example.com/upload',
  );

  UploadClient? _client;
  UploadTask? _task;
  UploadProgress? _progress;
  String? _fileName;
  String? _error;

  UploadTaskState get _state => _progress?.state ?? UploadTaskState.idle;

  Future<void> _pickAndUpload() async {
    final picked = await FilePicker.platform.pickFiles();
    final path = picked?.files.single.path;
    if (path == null) {
      return;
    }

    await _client?.close();

    final client = UploadClient(
      config: UploadConfig(uploadUrl: Uri.parse(_urlController.text.trim())),
    );
    final task = client.createTask(File(path));

    setState(() {
      _client = client;
      _task = task;
      _fileName = picked!.files.single.name;
      _error = null;
      _progress = null;
    });

    task.progress.listen((p) {
      if (mounted) {
        setState(() => _progress = p);
      }
    });

    try {
      await task.start();
    } on ResumableUploadException catch (e) {
      if (mounted) {
        setState(() => _error = e.message);
      }
    }
  }

  @override
  void dispose() {
    _urlController.dispose();
    _client?.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final progress = _progress;
    final task = _task;
    final isUploading = _state == UploadTaskState.uploading;
    final isPaused = _state == UploadTaskState.paused;

    return Scaffold(
      appBar: AppBar(title: const Text('Resumable Upload')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            TextField(
              controller: _urlController,
              decoration: const InputDecoration(
                labelText: 'Upload URL',
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 16),
            FilledButton.icon(
              onPressed: isUploading ? null : _pickAndUpload,
              icon: const Icon(Icons.upload_file),
              label: const Text('Pick a file and upload'),
            ),
            const SizedBox(height: 24),
            if (_fileName != null) Text('File: $_fileName'),
            const SizedBox(height: 8),
            if (progress != null) ...[
              LinearProgressIndicator(value: progress.fraction),
              const SizedBox(height: 8),
              Text(
                '${progress.percentage.toStringAsFixed(1)}%  •  '
                '${progress.completedChunks}/${progress.totalChunks} chunks  •  '
                '${progress.state.name}',
              ),
            ],
            if (_error != null) ...[
              const SizedBox(height: 8),
              Text(_error!, style: const TextStyle(color: Colors.red)),
            ],
            const SizedBox(height: 24),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                OutlinedButton(
                  onPressed: isUploading ? task!.pause : null,
                  child: const Text('Pause'),
                ),
                OutlinedButton(
                  onPressed: isPaused ? () => task!.resume() : null,
                  child: const Text('Resume'),
                ),
                OutlinedButton(
                  onPressed: (isUploading || isPaused) ? task!.cancel : null,
                  child: const Text('Cancel'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}
0
likes
160
points
5
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Resumable, fault-tolerant chunked file uploads for Dart, speaking the resumable.js protocol against a pion/laravel-chunk-upload backend.

Repository (GitHub)
View/report issues

Topics

#upload #resumable #chunked #dio #laravel

License

MIT (license)

Dependencies

crypto, dio, meta, mime, path

More

Packages that depend on pion_resumable_upload