flutter_media_picker_pro 1.0.1
flutter_media_picker_pro: ^1.0.1 copied to clipboard
A versatile Flutter package for picking and handling images, videos, and audio, with compression, timestamps, and recording features.
example/lib/main.dart
import 'dart:io';
import 'package:audioplayers/audioplayers.dart';
import 'package:example/custom_recording_button.dart';
import 'package:example/custom_recording_wave_widget.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:flutter_media_picker_pro/flutter_media_picker_pro.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:image/image.dart';
import 'package:image_picker/image_picker.dart';
import 'package:image/image.dart' as img;
import 'package:permission_handler/permission_handler.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends ConsumerWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: PictureScreen(),
);
}
}
class PictureScreen extends ConsumerStatefulWidget {
const PictureScreen({super.key});
@override
_PictureScreenState createState() => _PictureScreenState();
}
class _PictureScreenState extends ConsumerState<PictureScreen> {
final FlutterMediaPickerPro mediaPicker = FlutterMediaPickerPro();
XFile? _mediaFile;
bool _isProcessing = false;
bool _isStartRecording = false;
final AudioPlayer _audioPlayer = AudioPlayer();
bool _isPlaying = false;
bool isPlayAudio = true;
String? audioPath;
XFile? randomFile;
@override
void initState() {
_requestPermissions();
_audioPlayer.onPlayerComplete.listen((_) {
setState(() {
isPlayAudio = true;
_isPlaying = false;
});
});
super.initState();
}
@override
void dispose() {
_audioPlayer.dispose();
super.dispose();
}
Future<void> _requestPermissions() async {
if (await Permission.storage.request().isGranted) {
// Permission is granted
} else {
// Handle the case where permission is not granted
}
}
Future<void> _pickMedia({CompressionLevel? compressionLevel,required bool isNeedTimeStamp,required bool isVideo, required bool isAudio, required bool isRecording, required bool isStartRecording,
required bool isSourceCamera, required bool isCompressionRequired,}) async {
final XFile? pickedFile = await FlutterMediaPickerPro.getMedia(compressionLevel: compressionLevel??CompressionLevel.high,isStartRecording: isStartRecording,isRecording: isRecording,isAudio: isAudio,
isSourceCamera: isSourceCamera,context: context, isVideo: isVideo, ref: ref, isCompressionRequired: isCompressionRequired, isNeedTimeStamp: isNeedTimeStamp,onProcessing: (bool isProcessing) {
setState(() {
_isProcessing = isProcessing;
});
},);
if (pickedFile != null) {
setState(() {
_mediaFile = pickedFile;
});
}
if (isAudio) {
if(!isRecording){
await _audioPlayer.play(DeviceFileSource((pickedFile?.path).toString()));
setState(() {
_isPlaying = true;
});
}
}
}
final PageController _pageController = PageController();
int _activePage = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Row(
children: [
Icon(Icons.perm_media, color: Colors.white), // General media icon
SizedBox(width: 12), // Spacing between icon and title
Text('Flutter Media Picker Pro', style: TextStyle(color: Colors.white, fontSize: 18),),
],
),
backgroundColor: Colors.deepPurpleAccent,
actions: [
IconButton(
icon: const Icon(Icons.info_outline, color: Colors.white,),
onPressed: () {
// Handle info action (e.g., show package information)
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('About Media Picker Pro', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),),
content: const Text(
'This package helps you pick and process media files including images, videos, and audio.', style: TextStyle( fontSize: 14),),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Close'),
),
],
),
);
},
),
],
),
body: PageView.builder(
physics: const NeverScrollableScrollPhysics(),
controller: _pageController,
itemCount: 4,
onPageChanged: (int page) {
setState(() {
_activePage = page;
_mediaFile = null;
if (kDebugMode) {
print(_activePage);
}
});
},
itemBuilder: (BuildContext context, int index) {
if (kDebugMode) {
print('index : $index');
}
if (_activePage == 0) {
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
_isProcessing
? const Center(child: CircularProgressIndicator())
: _mediaFile == null
? Container(
height: 200,
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: const Center(
child: Text(
'No media selected!',
style: TextStyle(
fontSize: 18,
),
),
)
)
: mediaPicker.displayImage(File(_mediaFile!.path)),
const SizedBox(height: 20),
Wrap(
crossAxisAlignment: WrapCrossAlignment.start,
spacing: 10,
runSpacing: 10,
children: [
_buildMediaButton(
icon: Icons.camera_alt,
label: 'Capture Image',
onPressed: () {
_pickMedia(
isVideo: false,
isAudio: false,
isRecording: false,
isStartRecording: false,
isNeedTimeStamp: false,
isSourceCamera: true,
isCompressionRequired: false);
},
),
_buildMediaButton(
icon: Icons.image,
label: 'Pick Image',
onPressed: () {
_pickMedia(
isVideo: false,
isAudio: false,
isRecording: false,
isStartRecording: false,
isNeedTimeStamp: false,
isSourceCamera: false,
isCompressionRequired: false);
},
),
_buildMediaButton(
showSecondIcon: true,
secondIcon: Icons.compress,
icon: Icons.image,
label: 'Pick & Compress Image',
onPressed: () {
showCompressionDialog(isSourceCamera: false, isTimeStampNeeded: false);
/*_pickMedia(
isVideo: false,
isAudio: false,
isRecording: false,
isStartRecording: false,
isNeedTimeStamp: false,
isSourceCamera: false,
isCompressionRequired: true);*/
},
),
_buildMediaButton(
icon: Icons.camera_alt,
showSecondIcon: true,
secondIcon: Icons.compress,
label: 'Pick & Compress Image',
onPressed: () {
showCompressionDialog(isSourceCamera: true, isTimeStampNeeded: false);
/*_pickMedia(
isVideo: false,
isAudio: false,
isRecording: false,
isStartRecording: false,
isNeedTimeStamp: false,
isSourceCamera: true,
isCompressionRequired: true);*/
},
),
_buildMediaButton(
icon: Icons.access_time,
label: 'Add Timestamp on Gallery Image',
onPressed: () {
_pickMedia(
isVideo: false,
isAudio: false,
isRecording: false,
isStartRecording: false,
isNeedTimeStamp: true,
isSourceCamera: false,
isCompressionRequired: false);
},
),
_buildMediaButton(
icon: Icons.access_time,
label: 'Add Timestamp on Camera Image',
//color: Colors.white,
onPressed: () {
_pickMedia(
isVideo: false,
isAudio: false,
isRecording: false,
isStartRecording: false,
isNeedTimeStamp: true,
isSourceCamera: true,
isCompressionRequired: false);
},
),
_buildMediaButton(
icon: Icons.access_time,
showSecondIcon: true,
secondIcon: Icons.compress,
label: 'Add Timestamp & Compress Camera Image',
//color: Colors.white,
onPressed: () {
showCompressionDialog(isSourceCamera: true, isTimeStampNeeded: true);
},
),
_buildMediaButton(
icon: Icons.access_time,
showSecondIcon: true,
secondIcon: Icons.compress,
label: 'Add Timestamp & Compress Gallery Image',
//color: Colors.white,
onPressed: () {
showCompressionDialog(isSourceCamera: false, isTimeStampNeeded: true);
},
),
],
),
],
),
),
);
} else if (_activePage == 1) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.blue)
),
width: double.infinity,
child: Column(
children: [
if (_isStartRecording) const CustomRecordingWaveWidget(),
const SizedBox(height: 16),
Center(
child: CustomRecordingButton(
isRecording: _isStartRecording,
onPressed: () async {
_mediaFile = null;
audioPath = null;
if (_isStartRecording == false) {
_isStartRecording = true;
_pickMedia(
isVideo: false,
isAudio: true,
isRecording: true,
isStartRecording: true,
isNeedTimeStamp: false,
isSourceCamera: false,
isCompressionRequired: false);
} else {
_isStartRecording = false;
audioPath = await FlutterMediaPickerPro.getMedia(
isStartRecording: false,
isRecording: true,
isAudio: true,
isSourceCamera: true,
context: context,
isVideo: false,
ref: ref,
isCompressionRequired: false,
isNeedTimeStamp: true,
onProcessing: (bool isProcessing) {
setState(() {
_isProcessing = isProcessing;
});
},
);
if (kDebugMode) {
print('Checking audio path at stop recording::${audioPath.toString()}');
}
setState(() {});
}
},
),
),
audioPath != null
? Text('$audioPath', textAlign: TextAlign.center,)
: const SizedBox(),
audioPath != null
? IconButton(
icon: Icon(!isPlayAudio
? Icons.pause
: Icons.play_arrow),
onPressed: () async {
if (kDebugMode) {
print('Checking audio path::${audioPath.toString()}');
}
if (isPlayAudio) {
isPlayAudio = false;
await _audioPlayer.play(DeviceFileSource(audioPath.toString()));
setState(() {});
} else {
isPlayAudio = true;
_audioPlayer.pause();
}
setState(() {});
},
)
: const SizedBox(),
const SizedBox(height: 16),
],
),
),
),
const SizedBox(width: 10),
if (_isProcessing)
const CircularProgressIndicator()
else if (_mediaFile == null)
const SizedBox(
)
else
Text(_mediaFile!.name),
_mediaFile != null
? IconButton(
icon: Icon(_isPlaying ? Icons.pause : Icons.play_arrow),
onPressed: () async {
if (_isPlaying) {
await _audioPlayer.pause();
} else {
await _audioPlayer.resume();
}
setState(() {
_isPlaying = !_isPlaying;
});
},
)
: const SizedBox(),
_buildMediaButton(
icon: Icons.audiotrack,
label: 'Pick Audio',
onPressed: () {
_mediaFile = null;
audioPath = null;
_pickMedia(
isVideo: false,
isAudio: true,
isRecording: false,
isStartRecording: false,
isNeedTimeStamp: false,
isSourceCamera: false,
isCompressionRequired: false);
},
),
],
);
}
else if(_activePage == 2){
return SingleChildScrollView(
child: Column(
children: [
_isProcessing
? const Center(child: CircularProgressIndicator())
: _mediaFile == null
? Padding(
padding: const EdgeInsets.all(12.0),
child: Container(
height: 200,
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: const Center(
child: Text(
'No media selected!',
style: TextStyle(
fontSize: 18,
),
),
)
),
)
: mediaPicker.displayVideo(File(_mediaFile!.path)),
Wrap(
crossAxisAlignment: WrapCrossAlignment.start,
spacing: 10,
runSpacing: 10,
children: [
_buildMediaButton(
icon: Icons.video_call,
label: 'Take Video',
//color: Colors.white,
onPressed: () {
_pickMedia(
isVideo: true,
isAudio: false,
isRecording: false,
isStartRecording: false,
isNeedTimeStamp: false,
isSourceCamera: true,
isCompressionRequired: false);
},
),
_buildMediaButton(
icon: Icons.video_call,
label: 'Pick Video',
//color: Colors.white,
onPressed: () {
_pickMedia(
isVideo: true,
isAudio: false,
isRecording: false,
isStartRecording: false,
isNeedTimeStamp: false,
isSourceCamera: false,
isCompressionRequired: false);
},
),
_buildMediaButton(
icon: Icons.image,
label: 'Compress Camera Video',
//color: Colors.white,
onPressed: () {
showCompressionDialog(isVideo: true,isTimeStampNeeded: false, isSourceCamera: true);
},
),
_buildMediaButton(
icon: Icons.image,
label: 'Compress Gallery Video',
//color: Colors.white,
onPressed: () {
showCompressionDialog(isVideo: true,isTimeStampNeeded: false, isSourceCamera: false);
},
),
_buildMediaButton(
icon: Icons.access_time,
label: 'Add Timestamp on Camera Video',
//color: Colors.white,
onPressed: () {
_pickMedia(
isVideo: true,
isAudio: false,
isRecording: false,
isStartRecording: false,
isNeedTimeStamp: true,
isSourceCamera: true,
isCompressionRequired: false);
},
),
_buildMediaButton(
icon: Icons.access_time,
label: 'Add Timestamp on Gallery Video',
//color: Colors.white,
onPressed: () {
_pickMedia(
isVideo: true,
isAudio: false,
isRecording: false,
isStartRecording: false,
isNeedTimeStamp: true,
isSourceCamera: false,
isCompressionRequired: false);
},
),
_buildMediaButton(
icon: Icons.access_time,
secondIcon: Icons.compress,
showSecondIcon: true,
label: 'Add Timestamp & Compress Gallery Video',
//color: Colors.white,
onPressed: () {
showCompressionDialog(isVideo: true,isTimeStampNeeded: true, isSourceCamera: false);
},
),
_buildMediaButton(
icon: Icons.access_time,
secondIcon: Icons.compress,
showSecondIcon: true,
label: 'Add Timestamp & Compress Camera Video',
//color: Colors.white,
onPressed: () {
showCompressionDialog(isVideo: true,isTimeStampNeeded: true, isSourceCamera: true);
},
),
],
),
],
),
);
}
else{
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (_isProcessing)
const CircularProgressIndicator()
else if (randomFile == null)
const SizedBox(
)
else
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('Picked File Path: ${(randomFile?.path).toString()}', textAlign: TextAlign.center, ),
),
_buildMediaButton(
icon: Icons.audiotrack,
label: 'Pick File',
onPressed: () async {
randomFile = await FlutterMediaPickerPro.getMedia(
isFile: true,
isStartRecording: false,
isRecording: false,
isAudio: false,
isSourceCamera: true,
context: context,
isVideo: false,
ref: ref,
isCompressionRequired: false,
isNeedTimeStamp: false,
onProcessing: (bool isProcessing) {
setState(() {
_isProcessing = isProcessing;
});
},
);
},
),
],
);
}
},
),
// Center(
// child: _isProcessing
// ? CircularProgressIndicator()
// :_mediaFile == null
// ? Text('No media selected.')
// : _mediaFile!.path.endsWith('.mp4')
// ?
// mediaPicker.displayVideo(File(_mediaFile!.path))
// : _mediaFile!.path.endsWith('.mp3') || _mediaFile!.path.endsWith('.wav') || _mediaFile!.path.endsWith('.aac')
// ? Column(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Text('Playing audio: ${_mediaFile!.name}'),
// IconButton(
// icon: Icon(_isPlaying ? Icons.pause : Icons.play_arrow),
// onPressed: () async {
// if (_isPlaying) {
// await _audioPlayer.pause();
// } else {
// await _audioPlayer.resume();
// }
// setState(() {
// _isPlaying = !_isPlaying;
// });
// },
// ),
// ],
// )
// : mediaPicker.displayImage(File(_mediaFile!.path)),
// ),
floatingActionButton: SpeedDial(
spacing: 8,
spaceBetweenChildren: 5,
animatedIcon: AnimatedIcons.menu_arrow,
children: [
SpeedDialChild(
child: const Icon(Icons.image_outlined),
label: 'Image',
onTap: (){
_pageController.animateToPage(
0,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
),
SpeedDialChild(
child: const Icon(Icons.video_call),
label: 'Video',
onTap: (){
_pageController.animateToPage(
2,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
),
SpeedDialChild(
child: const Icon(Icons.audiotrack),
label: 'Audio',
onTap: (){
_pageController.animateToPage(
1,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
),
SpeedDialChild(
child: const Icon(Icons.file_present),
label: 'File',
onTap: (){
_pageController.animateToPage(
4,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
)
],
)
);
}
Widget _buildMediaButton({
required IconData icon,
IconData? secondIcon,
bool showSecondIcon = false,
required String label,
Color? color,
required VoidCallback onPressed,
}) {
return ElevatedButton.icon(
onPressed: onPressed,
icon: Row(
children: [
Icon(icon, color: Colors.white, size: 14,),
showSecondIcon? Row(
children: [
const Padding(
padding: EdgeInsets.symmetric(horizontal: 5.0),
child: Text('+'),
),
Icon(secondIcon, color: Colors.white, size: 14,),
],
): const SizedBox()
],
),
label: Text(label, style: const TextStyle(color: Colors.white, fontSize: 12)),
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white, backgroundColor: Colors.deepPurpleAccent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 2,
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
),
);
}
Future<void> showCompressionDialog({bool isVideo = false ,bool isSourceCamera = false, bool isTimeStampNeeded = false}) async {
CompressionLevel? selectedLevel = CompressionLevel.high;
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Select Compression Level', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),),
content: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
RadioListTile<CompressionLevel>(
title: const Text('High'),
value: CompressionLevel.high,
groupValue: selectedLevel,
onChanged: (CompressionLevel? value) {
setState(() {
selectedLevel = value;
});
},
),
RadioListTile<CompressionLevel>(
title: const Text('Medium'),
value: CompressionLevel.medium,
groupValue: selectedLevel,
onChanged: (CompressionLevel? value) {
setState(() {
selectedLevel = value;
});
},
),
RadioListTile<CompressionLevel>(
title: const Text('Low'),
value: CompressionLevel.low,
groupValue: selectedLevel,
onChanged: (CompressionLevel? value) {
setState(() {
selectedLevel = value;
});
},
),
],
);
},
),
actions: <Widget>[
TextButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
TextButton(
child: const Text('OK'),
onPressed: () {
Navigator.of(context).pop();
if (selectedLevel != null) {
_pickMedia(
isVideo: isVideo,
isAudio: false,
isRecording: false,
isStartRecording: false,
isNeedTimeStamp: isTimeStampNeeded,
isSourceCamera: isSourceCamera,
isCompressionRequired: true,
compressionLevel: selectedLevel,
);
}
},
),
],
);
},
);
}
}