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

A Flutter plugin for picking files that complies with Google Play's Photo and Video Permissions policy. Uses Android Photo Picker and Storage Access Framework instead of requesting READ_MEDIA permissions.

example/lib/main.dart

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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'File Picker Next Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<PickedFileInfo> _files = [];
  bool _loading = false;

  Future<void> _pickFiles(
    FilePickerType type, {
    List<String>? extensions,
  }) async {
    setState(() => _loading = true);

    try {
      final files = await FilePickerNext.pickFiles(
        type: type,
        allowMultiple: true,
        allowedExtensions: extensions,
      );

      if (files != null) {
        setState(() => _files = files);
      }
    } finally {
      setState(() => _loading = false);
    }
  }

  Future<void> _pickSingleImage() async {
    setState(() => _loading = true);

    try {
      final files = await FilePickerNext.pickFiles(
        type: FilePickerType.image,
        allowMultiple: false,
      );

      if (files != null) {
        setState(() => _files = files);
      }
    } finally {
      setState(() => _loading = false);
    }
  }

  Future<void> _showMediaSourceDialog({bool isVideo = false}) async {
    final file = await FilePickerNext.showMediaSourceDialog(
      context,
      isVideo: isVideo,
    );

    if (file != null) {
      setState(() => _files = [file]);
    }
  }

  Future<void> _saveFirstFileToDownloads() async {
    if (_files.isEmpty) {
      _showSnackBar('No file to save');
      return;
    }

    setState(() => _loading = true);

    try {
      final file = _files.first;
      final bytes = file.bytes ?? await file.file.readAsBytes();

      final path = await FilePickerNext.saveToDownloads(
        fileName: file.name,
        bytes: bytes,
        mimeType: file.mimeType,
      );

      if (path != null) {
        _showSnackBar('Saved to: $path');
      } else {
        _showSnackBar('Failed to save file');
      }
    } finally {
      setState(() => _loading = false);
    }
  }

  void _showSnackBar(String message) {
    ScaffoldMessenger.of(
      context,
    ).showSnackBar(SnackBar(content: Text(message)));
  }

  void _clearFiles() {
    setState(() => _files.clear());
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('File Picker Next Demo'),
        actions: [
          if (_files.isNotEmpty)
            IconButton(
              icon: const Icon(Icons.clear),
              onPressed: _clearFiles,
              tooltip: 'Clear files',
            ),
        ],
      ),
      body: Column(
        children: [
          // Picker Options
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                const Text(
                  'Pick Files:',
                  style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                ),
                const SizedBox(height: 12),
                Wrap(
                  spacing: 8,
                  runSpacing: 8,
                  children: [
                    ElevatedButton.icon(
                      onPressed: _loading ? null : _pickSingleImage,
                      icon: const Icon(Icons.image),
                      label: const Text('Single Image'),
                    ),
                    ElevatedButton.icon(
                      onPressed: _loading
                          ? null
                          : () => _pickFiles(FilePickerType.image),
                      icon: const Icon(Icons.photo_library),
                      label: const Text('Multiple Images'),
                    ),
                    ElevatedButton.icon(
                      onPressed: _loading
                          ? null
                          : () => _pickFiles(FilePickerType.video),
                      icon: const Icon(Icons.video_library),
                      label: const Text('Videos'),
                    ),
                    ElevatedButton.icon(
                      onPressed: _loading
                          ? null
                          : () => _pickFiles(
                              FilePickerType.custom,
                              extensions: ['pdf', 'doc', 'docx'],
                            ),
                      icon: const Icon(Icons.description),
                      label: const Text('Documents'),
                    ),
                    ElevatedButton.icon(
                      onPressed: _loading
                          ? null
                          : () => _pickFiles(FilePickerType.any),
                      icon: const Icon(Icons.folder),
                      label: const Text('Any File'),
                    ),
                  ],
                ),
                const SizedBox(height: 16),
                const Text(
                  'Camera/Gallery:',
                  style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                ),
                const SizedBox(height: 12),
                Wrap(
                  spacing: 8,
                  runSpacing: 8,
                  children: [
                    ElevatedButton.icon(
                      onPressed: _loading
                          ? null
                          : () => _showMediaSourceDialog(isVideo: false),
                      icon: const Icon(Icons.camera_alt),
                      label: const Text('Take Photo'),
                    ),
                    ElevatedButton.icon(
                      onPressed: _loading
                          ? null
                          : () => _showMediaSourceDialog(isVideo: true),
                      icon: const Icon(Icons.videocam),
                      label: const Text('Record Video'),
                    ),
                  ],
                ),
                if (_files.isNotEmpty) ...[
                  const SizedBox(height: 16),
                  ElevatedButton.icon(
                    onPressed: _loading ? null : _saveFirstFileToDownloads,
                    icon: const Icon(Icons.save),
                    label: const Text('Save First File to Downloads'),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.green,
                      foregroundColor: Colors.white,
                    ),
                  ),
                ],
              ],
            ),
          ),
          const Divider(),
          // File List
          Expanded(
            child: _loading
                ? const Center(child: CircularProgressIndicator())
                : _files.isEmpty
                ? const Center(
                    child: Text(
                      'No files picked yet.\nTry picking some files!',
                      textAlign: TextAlign.center,
                      style: TextStyle(fontSize: 16, color: Colors.grey),
                    ),
                  )
                : ListView.builder(
                    padding: const EdgeInsets.all(8),
                    itemCount: _files.length,
                    itemBuilder: (context, index) {
                      final file = _files[index];
                      return Card(
                        child: ListTile(
                          leading: _buildFileIcon(file),
                          title: Text(
                            file.name,
                            maxLines: 1,
                            overflow: TextOverflow.ellipsis,
                          ),
                          subtitle: Text(
                            '${_formatBytes(file.size)}\n${file.path}',
                            maxLines: 2,
                            overflow: TextOverflow.ellipsis,
                            style: const TextStyle(fontSize: 12),
                          ),
                          isThreeLine: true,
                          trailing: IconButton(
                            icon: const Icon(Icons.info_outline),
                            onPressed: () => _showFileDetails(file),
                          ),
                        ),
                      );
                    },
                  ),
          ),
        ],
      ),
    );
  }

  Widget _buildFileIcon(PickedFileInfo file) {
    if (file.isImage) {
      return ClipRRect(
        borderRadius: BorderRadius.circular(4),
        child: Image.file(
          file.file,
          width: 50,
          height: 50,
          fit: BoxFit.cover,
          errorBuilder: (context, error, stackTrace) {
            return const Icon(Icons.image, size: 50);
          },
        ),
      );
    } else if (file.isVideo) {
      return const Icon(Icons.video_file, size: 50, color: Colors.blue);
    } else if (file.isDocument) {
      return const Icon(Icons.description, size: 50, color: Colors.orange);
    } else {
      return const Icon(Icons.insert_drive_file, size: 50, color: Colors.grey);
    }
  }

  String _formatBytes(int bytes) {
    if (bytes < 1024) return '$bytes B';
    if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB';
    if (bytes < 1024 * 1024 * 1024) {
      return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB';
    }
    return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)} GB';
  }

  void _showFileDetails(PickedFileInfo file) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('File Details'),
        content: SingleChildScrollView(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.min,
            children: [
              _detailRow('Name', file.name),
              _detailRow('Extension', file.extension),
              _detailRow('Size', _formatBytes(file.size)),
              _detailRow('MIME Type', file.mimeType),
              _detailRow('Path', file.path),
              _detailRow('Is Image', file.isImage.toString()),
              _detailRow('Is Video', file.isVideo.toString()),
              _detailRow('Is Document', file.isDocument.toString()),
            ],
          ),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Close'),
          ),
        ],
      ),
    );
  }

  Widget _detailRow(String label, String value) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 4),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            label,
            style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
          ),
          const SizedBox(height: 2),
          Text(value, style: const TextStyle(fontSize: 14)),
          const Divider(),
        ],
      ),
    );
  }
}
2
likes
0
points
273
downloads

Publisher

verified publisherswarup.dev

Weekly Downloads

A Flutter plugin for picking files that complies with Google Play's Photo and Video Permissions policy. Uses Android Photo Picker and Storage Access Framework instead of requesting READ_MEDIA permissions.

Repository (GitHub)
View/report issues

Topics

#file-picker #storage-access-framework #photo-picker #document-picker #compliance

License

unknown (license)

Dependencies

flutter, image_picker, plugin_platform_interface

More

Packages that depend on file_picker_next

Packages that implement file_picker_next