flutter_media_picker_pro 1.0.4
flutter_media_picker_pro: ^1.0.4 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() {
_audioPlayer.onPlayerComplete.listen((_) {
setState(() {
isPlayAudio = true;
_isPlaying = false;
});
});
super.initState();
}
@override
void dispose() {
_audioPlayer.dispose();
super.dispose();
}
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(
'The Flutter Media Picker Pro package is a versatile solution for Flutter developers, enabling users to pick images, videos, and audio files from their device gallery or camera. It includes features like media compression, adding timestamps to videos, and audio recording and playback. The package is designed to simplify media handling in Flutter apps, providing a smooth and efficient user experience.',
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.file_present_outlined,
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;
});
},
);
},
),
],
);
}
},
),
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,
);
}
},
),
],
);
},
);
}
}