easy_media_picker 0.0.1
easy_media_picker: ^0.0.1 copied to clipboard
A cross-platform media picker for image, video, audio and documents.
example/lib/main.dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:easy_media_picker/easy_media_picker.dart';
import 'package:video_player/video_player.dart';
import 'package:file_picker/file_picker.dart';
void main() => runApp(const ExampleApp());
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) =>
const MaterialApp(debugShowCheckedModeBanner: false, home: DemoHome());
}
class DemoHome extends StatefulWidget {
const DemoHome({super.key});
@override
State<DemoHome> createState() => _DemoHomeState();
}
class _DemoHomeState extends State<DemoHome> {
final EasyMediaPicker picker = EasyMediaPicker();
PickResult? single;
List<PickResult> multiple = [];
String? selectedFolder;
List<FileSystemEntity> folderFiles = [];
VideoPlayerController? _videoController;
@override
void dispose() {
_videoController?.dispose();
super.dispose();
}
Widget buildGroupTitle(String title) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Text(
title,
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
);
}
Future<void> _loadVideo(File file) async {
_videoController?.dispose();
_videoController = VideoPlayerController.file(file);
await _videoController!.initialize();
_videoController!.setLooping(true);
await _videoController!.play();
setState(() {});
}
Widget _singlePreview() {
if (single == null) {
return const Text("No file selected", style: TextStyle(fontSize: 16));
}
if (single!.type == PickFileType.image && single!.file != null) {
return Column(
children: [
Image.file(single!.file!, width: 220, height: 220, fit: BoxFit.cover),
const SizedBox(height: 8),
Text(single!.name),
],
);
}
if (single!.type == PickFileType.video && single!.file != null) {
if (_videoController == null || !_videoController!.value.isInitialized) {
return const CircularProgressIndicator();
}
return Column(
children: [
SizedBox(
width: 320,
height: 200,
child: AspectRatio(
aspectRatio: _videoController!.value.aspectRatio,
child: VideoPlayer(_videoController!),
),
),
const SizedBox(height: 8),
Text(single!.name),
],
);
}
return Column(
children: [
Icon(
single!.type == PickFileType.audio
? Icons.audiotrack
: Icons.insert_drive_file,
size: 80,
),
const SizedBox(height: 8),
Text("${single!.type.name}: ${single!.name}"),
],
);
}
Widget _multiplePreview() {
if (multiple.isEmpty) return const SizedBox.shrink();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 12),
Text("Multiple items (${multiple.length}):"),
const SizedBox(height: 8),
SizedBox(
height: 220,
child: ListView.builder(
itemCount: multiple.length,
itemBuilder: (c, i) {
final f = multiple[i];
final icon = f.type == PickFileType.image
? Icons.image
: f.type == PickFileType.video
? Icons.videocam
: f.type == PickFileType.audio
? Icons.audiotrack
: Icons.insert_drive_file;
return ListTile(
leading: Icon(icon),
title: Text(f.name),
subtitle: Text(f.extension),
onTap: () async {
// Show any single item when tapped
if (f.type == PickFileType.video && f.file != null) {
single = f;
await _loadVideo(f.file!);
setState(() {});
} else {
single = f;
_videoController?.dispose();
setState(() {});
}
},
);
},
),
),
],
);
}
Widget _folderPreview() {
if (selectedFolder == null) {
return const Text("No folder selected", style: TextStyle(fontSize: 16));
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey.shade200,
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
const Icon(Icons.folder, size: 28, color: Colors.amber),
const SizedBox(width: 10),
Expanded(
child: Text(
selectedFolder!,
style: const TextStyle(fontSize: 15),
),
),
],
),
),
const SizedBox(height: 12),
const Text(
"Folder Contents:",
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
SizedBox(
height: 220,
child: ListView.builder(
itemCount: folderFiles.length,
itemBuilder: (context, index) {
final entity = folderFiles[index];
final name = entity.path.split("/").last;
final icon = entity is Directory ? Icons.folder : _fileIcon(name);
return ListTile(
leading: Icon(icon, size: 28, color: Colors.blueGrey),
title: Text(name),
subtitle: Text(entity.path),
);
},
),
),
],
);
}
Future<void> _handlePickImage() async {
single = await picker.pickImage(const ImagePickOptions());
multiple = [];
_videoController?.dispose();
setState(() {});
}
Future<void> _handleCaptureImage() async {
single = await picker.captureImage();
multiple = [];
_videoController?.dispose();
setState(() {});
}
Future<void> _handlePickVideo() async {
single = await picker.pickVideo(const VideoPickOptions());
multiple = [];
if (single?.file != null) await _loadVideo(single!.file!);
setState(() {});
}
Future<void> _handleCaptureVideo() async {
single = await picker.captureVideo();
multiple = [];
if (single?.file != null) await _loadVideo(single!.file!);
setState(() {});
}
Future<void> _handlePickImagesMultiple() async {
multiple = await picker.pickMultipleImages();
single = null;
_videoController?.dispose();
setState(() {});
}
Future<void> _handlePickVideosMultiple() async {
multiple = await picker.pickMultipleVideos();
single = null;
_videoController?.dispose();
setState(() {});
}
Future<void> _handlePickAnyFile() async {
multiple = await picker.pickAnyFile(allowMultiple: true);
single = null;
_videoController?.dispose();
setState(() {});
}
Future<void> _handlePickFolder() async {
final folderPath = await FilePicker.platform.getDirectoryPath();
if (folderPath == null) {
setState(() {
selectedFolder = null;
folderFiles = [];
});
return;
}
selectedFolder = folderPath;
// β
Pick all files inside selected folder using FilePicker SAF
final result = await FilePicker.platform.pickFiles(
allowMultiple: true,
initialDirectory: folderPath, // THIS is the fix!
type: FileType.any,
);
if (result == null) {
setState(() => folderFiles = []);
return;
}
// Convert to FileSystemEntity-like structure
folderFiles = result.paths.whereType<String>().map((p) => File(p)).toList();
setState(() {});
}
Future<void> _handlePickMediaMixed() async {
multiple = await picker.pickMedia();
single = null;
_videoController?.dispose();
setState(() {});
}
IconData _fileIcon(String path) {
final ext = path.split(".").last.toLowerCase();
if (["png", "jpg", "jpeg", "gif", "webp"].contains(ext)) return Icons.image;
if (["mp4", "mov", "mkv", "avi", "webm"].contains(ext))
return Icons.video_file;
if (["mp3", "wav", "aac", "m4a"].contains(ext)) return Icons.audiotrack;
if (["pdf"].contains(ext)) return Icons.picture_as_pdf;
return Icons.insert_drive_file;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("EasyMediaPicker β Demo")),
body: Padding(
padding: const EdgeInsets.all(16),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"πΈ Media Picker Controls",
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
// ---------------- BUTTON GROUPS ----------------
buildGroupTitle("βΆ Images"),
Wrap(
spacing: 10,
runSpacing: 10,
children: [
ElevatedButton(
onPressed: _handlePickImage,
child: const Text("Pick Image"),
),
ElevatedButton(
onPressed: _handleCaptureImage,
child: const Text("Capture Image"),
),
ElevatedButton(
onPressed: _handlePickImagesMultiple,
child: const Text("Pick Multiple Images"),
),
],
),
const SizedBox(height: 20),
buildGroupTitle("π₯ Videos"),
Wrap(
spacing: 10,
runSpacing: 10,
children: [
ElevatedButton(
onPressed: _handlePickVideo,
child: const Text("Pick Video"),
),
ElevatedButton(
onPressed: _handleCaptureVideo,
child: const Text("Capture Video"),
),
ElevatedButton(
onPressed: _handlePickVideosMultiple,
child: const Text("Pick Multiple Videos"),
),
],
),
const SizedBox(height: 20),
buildGroupTitle("π΅ Audio & π Documents"),
Wrap(
spacing: 10,
runSpacing: 10,
children: [
ElevatedButton(
onPressed: () async {
multiple = await picker.pickAudio(
const AudioPickOptions(allowMultiple: true),
);
single = null;
_videoController?.dispose();
setState(() {});
},
child: const Text("Pick Audio (multi)"),
),
ElevatedButton(
onPressed: () async {
multiple = await picker.pickDocument(
const DocumentPickOptions(allowMultiple: true),
);
single = null;
_videoController?.dispose();
setState(() {});
},
child: const Text("Pick Document"),
),
ElevatedButton(
onPressed: () async {
multiple = await picker.pickDocument(
const DocumentPickOptions(
allowMultiple: true,
extensions: ['json', 'apk', 'zip'],
),
);
single = null;
_videoController?.dispose();
setState(() {});
},
child: const Text("Document (custom ext)"),
),
],
),
const SizedBox(height: 20),
buildGroupTitle("π Folder / Any File / Mixed Media"),
Wrap(
spacing: 10,
runSpacing: 10,
children: [
ElevatedButton(
onPressed: _handlePickAnyFile,
child: const Text("Pick Any File"),
),
ElevatedButton(
onPressed: _handlePickFolder,
child: const Text("Pick Folder"),
),
ElevatedButton(
onPressed: _handlePickMediaMixed,
child: const Text("Pick Media (mixed)"),
),
],
),
const SizedBox(height: 20),
Center(
child: ElevatedButton(
onPressed: () {
single = null;
multiple = [];
_videoController?.dispose();
selectedFolder = null;
folderFiles = [];
setState(() {});
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.indigo,
),
child: const Text(
"Clear All",
style: TextStyle(color: Colors.white),
),
),
),
const SizedBox(height: 20),
const Divider(height: 30),
// ---------------- PREVIEWS ----------------
const Text(
"π Single File Preview",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
_singlePreview(),
const SizedBox(height: 25),
const Divider(height: 30),
const Text(
"π Multiple Files Preview",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
_multiplePreview(),
const Divider(height: 30),
const Text(
"π Folder Preview",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
SizedBox(height: 10),
_folderPreview(),
const SizedBox(height: 25),
],
),
),
),
);
}
}