πŸ“„ README.md

Flutter Download Button

A highly customizable download button widget for Flutter with progress indication, extensive styling options, and file management capabilities.

Version Flutter License

✨ Features

  • 🎨 Fully Customizable - 30+ customization properties
  • πŸ“Š Real-time Progress - Animated download progress indication
  • πŸ”„ Pause/Resume - Support for canceling downloads
  • πŸ“ File Management - Built-in utilities for file operations
  • 🎯 Easy Integration - Simple controller pattern
  • πŸ“± Pre-built Components - Ready-to-use list item widgets
  • 🎭 Multiple Presets - Material, Cupertino, Minimal, and Rounded styles
  • πŸš€ Smooth Animations - Beautiful transitions between states

πŸ“¦ Installation

Add this to your pubspec.yaml:

dependencies:
flutter_download_button: ^1.0.2

Then run:

flutter pub get

Android Setup

Add in Manifest

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Go to Android/app/main/res

1. Create xml package
2. Create file_paths.xml file

Add this code in file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="external_files" path="." />
    <files-path name="files" path="." />
    <cache-path name="cache" path="." />
    <external-files-path name="external_files_path" path="." />
    <external-cache-path name="external_cache_path" path="." />
</paths>

iOS Setup

Add in Info.plist

	<!-- FILE SHARING CONFIGURATION -->
	<key>UIFileSharingEnabled</key>
	<true/>

	<key>LSSupportsOpeningDocumentsInPlace</key>
	<true/>

	<key>NSPhotoLibraryUsageDescription</key>
	<string>We need access to save and view downloaded files</string>

	<!-- DOCUMENT TYPES -->
	<key>CFBundleDocumentTypes</key>
	<array>
		<dict>
			<key>CFBundleTypeName</key>
			<string>PDF Document</string>
			<key>LSHandlerRank</key>
			<string>Alternate</string>
			<key>LSItemContentTypes</key>
			<array>
				<string>com.adobe.pdf</string>
			</array>
		</dict>
		<dict>
			<key>CFBundleTypeName</key>
			<string>Image</string>
			<key>LSHandlerRank</key>
			<string>Alternate</string>
			<key>LSItemContentTypes</key>
			<array>
				<string>public.image</string>
				<string>public.jpeg</string>
				<string>public.png</string>
			</array>
		</dict>
		<dict>
			<key>CFBundleTypeName</key>
			<string>Text Document</string>
			<key>LSHandlerRank</key>
			<string>Alternate</string>
			<key>LSItemContentTypes</key>
			<array>
				<string>public.text</string>
				<string>public.plain-text</string>
			</array>
		</dict>
	</array>

πŸš€ Quick Start

Create custom UI

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

/// A customizable list item widget that includes a download button.
class CustomizableDownloadListItem extends StatelessWidget {
  /// Creates a new [CustomizableDownloadListItem].
  const CustomizableDownloadListItem({
    super.key,
    required this.title,
    required this.subtitle,
    required this.downloadController,
    this.leading,
    this.onTap,
    this.config = const DownloadButtonConfig(),
    this.titleStyle,
    this.subtitleStyle,
    this.buttonWidth = 96,
  });

  final String title;
  final String subtitle;
  final DownloadController downloadController;
  final Widget? leading;
  final VoidCallback? onTap;
  final DownloadButtonConfig config;
  final TextStyle? titleStyle;
  final TextStyle? subtitleStyle;
  final double buttonWidth;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return ListTile(
      leading: leading ?? const _DefaultIcon(),
      title: Text(
        title,
        overflow: TextOverflow.ellipsis,
        style: titleStyle ?? theme.textTheme.titleLarge,
      ),
      subtitle: Text(
        subtitle,
        overflow: TextOverflow.ellipsis,
        style: subtitleStyle ?? theme.textTheme.bodySmall,
      ),
      trailing: SizedBox(
        width: buttonWidth,
        child: AnimatedBuilder(
          animation: downloadController,
          builder: (context, child) {
            return CustomizableDownloadButton(
              status: downloadController.downloadStatus,
              downloadProgress: downloadController.progress,
              onDownload: downloadController.startDownload,
              onCancel: downloadController.stopDownload,
              onOpen: downloadController.openDownload,
              config: config,
            );
          },
        ),
      ),
      onTap: onTap,
    );
  }
}

class _DefaultIcon extends StatelessWidget {
  const _DefaultIcon();

  @override
  Widget build(BuildContext context) {
    return const AspectRatio(
      aspectRatio: 1,
      child: FittedBox(
        child: SizedBox(
          width: 80,
          height: 80,
          child: DecoratedBox(
            decoration: BoxDecoration(
              gradient: LinearGradient(
                colors: [Colors.red, Colors.blue],
              ),
              borderRadius: BorderRadius.all(Radius.circular(20)),
            ),
            child: Center(
              child: Icon(
                Icons.file_download,
                color: Colors.white,
                size: 40,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

main.dart

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

class MyApp extends StatefulWidget {
  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late RealDownloadController _controller;

  @override
  void initState() {
    super.initState();
    _controller = RealDownloadController(
      fileName: 'sample.pdf',
      downloadUrl: 'https://example.com/file.pdf',
      onOpenDownload: () => DownloadService.openFile('sample.pdf'),
    );
    _controller.checkFileExists();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: SizedBox(
          width: 120,
          child: AnimatedBuilder(
            animation: _controller,
            builder: (context, child) {
              return CustomizableDownloadButton(
                status: _controller.downloadStatus,
                downloadProgress: _controller.progress,
                onDownload: _controller.startDownload,
                onCancel: _controller.stopDownload,
                onOpen: _controller.openDownload,
                config: DownloadButtonConfig.material, // Use preset
              );
            },
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

🎨 Customization

Using Presets

// Material Design
CustomizableDownloadButton(
config: DownloadButtonConfig.material,
// ...
)

// iOS Cupertino
CustomizableDownloadButton(
config: DownloadButtonConfig.cupertino,
// ...
)

// Minimal
CustomizableDownloadButton(
config: DownloadButtonConfig.minimal,
// ...
)

// Rounded
CustomizableDownloadButton(
config: DownloadButtonConfig.rounded,
// ...
)

Custom Configuration

CustomizableDownloadButton(
    config: const DownloadButtonConfig(
    downloadText: 'DOWNLOAD',
    openText: 'OPEN',
    backgroundColor: Color(0xFF9C27B0),
    foregroundColor: Colors.white,
    progressColor: Colors.white,
    downloadIcon: Icons.cloud_download,
    openIcon: Icons.folder_open,
    elevation: 4.0,
    borderRadius: BorderRadius.all(Radius.circular(20)),
    buttonPadding: EdgeInsets.symmetric(vertical: 12, horizontal: 20),
    minWidth: 120.0,
),
    status: controller.downloadStatus,
    downloadProgress: controller.progress,
    onDownload: controller.startDownload,
    onCancel: controller.stopDownload,
    onOpen: controller.openDownload)

πŸ“‹ All Configuration Options

Property Type Default Description
downloadText String 'GET' Text when not downloaded
openText String 'OPEN' Text when downloaded
backgroundColor Color Color(0xFFE5E5EA) Button background
foregroundColor Color Color(0xFF007AFF) Text/icon color
progressColor Color Color(0xFF007AFF) Progress color
progressBackgroundColor Color Color(0xFFE5E5EA) Progress background
fetchingColor Color Color(0xFFE5E5EA) Fetching state color
textStyle TextStyle? null Custom text style
downloadIcon IconData? null Download state icon
openIcon IconData? null Open state icon
stopIcon IconData Icons.stop Stop icon
stopIconSize double 14.0 Stop icon size
buttonShape ShapeBorder? null Custom button shape
progressShape ShapeBorder? null Custom progress shape
transitionDuration Duration 500ms Animation duration
progressStrokeWidth double 2.0 Progress thickness
buttonPadding EdgeInsets 6v, 12h Button padding
minWidth double 96.0 Minimum width
borderRadius BorderRadius? null Border radius
elevation double 0.0 Button elevation
showStopIcon bool true Show stop icon
allowCancel bool true Allow canceling

🎯 Examples

Outlined Button

CustomizableDownloadButton(
config: const DownloadButtonConfig(
backgroundColor: Colors.transparent,
foregroundColor: Color(0xFF2196F3),
progressColor: Color(0xFF2196F3),
buttonShape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8)),
side: BorderSide(color: Color(0xFF2196F3), width: 2),
)),
// ...
)

Button with Icons

CustomizableDownloadButton(
config: const DownloadButtonConfig(
downloadIcon: Icons.cloud_download_outlined,
openIcon: Icons.folder_open_outlined,
backgroundColor: Color(0xFF4CAF50),
foregroundColor: Colors.white),
// ...
)

List Item Usage

CustomizableDownloadListItem(
title: 'Document.pdf',
subtitle: '2.5 MB',
downloadController: controller,
config: DownloadButtonConfig.material,
buttonWidth: 120)

πŸ› οΈ Services

// Check if file exists
final exists = await DownloadService.fileExists('file.pdf');

// Get file path
final path = await DownloadService.getFilePath('file.pdf');

// Open file
final result = await DownloadService.openFile('file.pdf');

// Delete file
await DownloadService.deleteFile('file.pdf');

🌍 Platform Support

  • βœ… Android
  • βœ… iOS
  • βœ… macOS
  • βœ… Windows
  • βœ… Linux
  • βœ… Web

βš™οΈ Platform Setup

Android

Add to AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

iOS

Add to Info.plist:

<key>NSPhotoLibraryUsageDescription</key>
<string>Need to save files</string>

πŸ“š Documentation

For complete documentation, visit pub.flutter-io.cn

🀝 Contributing

Contributions welcome! Please open an issue or PR.

πŸ“„ License

MIT License - see LICENSE file

πŸ’¬ Support


---

## πŸš€ How to Use This Package

### Step 1: Create Project Structure
```bash
mkdir flutter_download_button
cd flutter_download_button
mkdir -p lib/src/{models,controllers,widgets,services}
mkdir -p example/lib

Step 2: Copy All Files

Copy each file from above into its respective location.

Step 3: Initialize

flutter pub get
cd example
flutter pub get

Step 4: Run Example

cd example
flutter run

Step 5: Publish (Optional)

dart pub publish --dry-run
dart pub publish

πŸ“± Usage in Your App

# pubspec.yaml
dependencies:
flutter_download_button: ^1.0.2
import 'package:flutter_download_button/flutter_download_button.dart';

// Use any preset
CustomizableDownloadButton(
config: DownloadButtonConfig.material,
// or create your own custom config
)

βœ… This package includes:

  • βœ… Full customization with 30+ properties
  • βœ… 4 built-in style presets
  • βœ… 8 example implementations
  • βœ… Complete documentation
  • βœ… Type-safe configuration
  • βœ… Ready to publish to pub.flutter-io.cn

Libraries

flutter_download_button
A highly customizable download button widget for Flutter.