Created
February 8, 2024 05:01
-
-
Save harsimranmaan/b11d78c6a6b1493ed9a786b2aef4d0ee to your computer and use it in GitHub Desktop.
flutter image picker
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2013 The Flutter Authors. All rights reserved. | |
// Use of this source code is governed by a BSD-style license that can be | |
// found in the LICENSE file. | |
// ignore_for_file: public_member_api_docs | |
import 'dart:async'; | |
import 'dart:io'; | |
import 'package:flutter/foundation.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:image_picker/image_picker.dart'; | |
import 'package:mime/mime.dart'; | |
void main() { | |
runApp(const MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
const MyApp({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
return const MaterialApp( | |
title: 'Image Picker Demo', | |
home: MyHomePage(title: 'Image Picker Example'), | |
); | |
} | |
} | |
class MyHomePage extends StatefulWidget { | |
const MyHomePage({super.key, this.title}); | |
final String? title; | |
@override | |
State<MyHomePage> createState() => _MyHomePageState(); | |
} | |
class _MyHomePageState extends State<MyHomePage> { | |
List<XFile>? _mediaFileList; | |
void _setImageFileListFromFile(XFile? value) { | |
_mediaFileList = value == null ? null : <XFile>[value]; | |
} | |
dynamic _pickImageError; | |
bool isVideo = false; | |
String? _retrieveDataError; | |
final ImagePicker _picker = ImagePicker(); | |
final TextEditingController maxWidthController = TextEditingController(); | |
final TextEditingController maxHeightController = TextEditingController(); | |
final TextEditingController qualityController = TextEditingController(); | |
Future<void> _onImageButtonPressed( | |
ImageSource source, { | |
required BuildContext context, | |
bool isMultiImage = false, | |
bool isMedia = false, | |
}) async { | |
if (context.mounted) { | |
if (isMultiImage) { | |
await _displayPickImageDialog(context, | |
(double? maxWidth, double? maxHeight, int? quality) async { | |
try { | |
final List<XFile> pickedFileList = isMedia | |
? await _picker.pickMultipleMedia( | |
maxWidth: maxWidth, | |
maxHeight: maxHeight, | |
imageQuality: quality, | |
) | |
: await _picker.pickMultiImage( | |
maxWidth: maxWidth, | |
maxHeight: maxHeight, | |
imageQuality: quality, | |
); | |
setState(() { | |
_mediaFileList = pickedFileList; | |
}); | |
} catch (e) { | |
setState(() { | |
_pickImageError = e; | |
}); | |
} | |
}); | |
} else if (isMedia) { | |
await _displayPickImageDialog(context, | |
(double? maxWidth, double? maxHeight, int? quality) async { | |
try { | |
final List<XFile> pickedFileList = <XFile>[]; | |
final XFile? media = await _picker.pickMedia( | |
maxWidth: maxWidth, | |
maxHeight: maxHeight, | |
imageQuality: quality, | |
); | |
if (media != null) { | |
pickedFileList.add(media); | |
setState(() { | |
_mediaFileList = pickedFileList; | |
}); | |
} | |
} catch (e) { | |
setState(() { | |
_pickImageError = e; | |
}); | |
} | |
}); | |
} else { | |
await _displayPickImageDialog(context, | |
(double? maxWidth, double? maxHeight, int? quality) async { | |
try { | |
final XFile? pickedFile = await _picker.pickImage( | |
source: source, | |
maxWidth: maxWidth, | |
maxHeight: maxHeight, | |
imageQuality: quality, | |
); | |
setState(() { | |
_setImageFileListFromFile(pickedFile); | |
}); | |
} catch (e) { | |
setState(() { | |
_pickImageError = e; | |
}); | |
} | |
}); | |
} | |
} | |
} | |
@override | |
void dispose() { | |
maxWidthController.dispose(); | |
maxHeightController.dispose(); | |
qualityController.dispose(); | |
super.dispose(); | |
} | |
Widget _previewImages() { | |
final Text? retrieveError = _getRetrieveErrorWidget(); | |
if (retrieveError != null) { | |
return retrieveError; | |
} | |
if (_mediaFileList != null) { | |
return Semantics( | |
label: 'image_picker_example_picked_images', | |
child: ListView.builder( | |
key: UniqueKey(), | |
itemBuilder: (BuildContext context, int index) { | |
final String? mime = lookupMimeType(_mediaFileList![index].path); | |
// Why network for web? | |
// See https://pub.dev/packages/image_picker_for_web#limitations-on-the-web-platform | |
return Semantics( | |
label: 'image_picker_example_picked_image', | |
child: kIsWeb | |
? Image.network(_mediaFileList![index].path) | |
: Row( | |
children: [ | |
const Padding(padding: EdgeInsets.all(8.0)), | |
Container( | |
width: 300.0, | |
height: 300.0, | |
decoration: const BoxDecoration( | |
borderRadius: | |
BorderRadius.all(Radius.circular(8.0)), | |
color: Colors.redAccent, | |
), | |
child: Image.file( | |
File(_mediaFileList![index].path), | |
errorBuilder: (BuildContext context, Object error, | |
StackTrace? stackTrace) { | |
return const Center( | |
child: | |
Text('This image type is not supported')); | |
}, | |
), | |
), | |
const Padding(padding: EdgeInsets.all(8.0)), | |
], | |
), | |
); | |
}, | |
itemCount: _mediaFileList!.length, | |
), | |
); | |
} else if (_pickImageError != null) { | |
return Text( | |
'Pick image error: $_pickImageError', | |
textAlign: TextAlign.center, | |
); | |
} else { | |
return const Text( | |
'You have not yet picked an image.', | |
textAlign: TextAlign.center, | |
); | |
} | |
} | |
Widget _handlePreview() { | |
return _previewImages(); | |
} | |
Future<void> retrieveLostData() async { | |
final LostDataResponse response = await _picker.retrieveLostData(); | |
if (response.isEmpty) { | |
return; | |
} | |
if (response.file != null) { | |
setState(() { | |
if (response.files == null) { | |
_setImageFileListFromFile(response.file); | |
} else { | |
_mediaFileList = response.files; | |
} | |
}); | |
} else { | |
_retrieveDataError = response.exception!.code; | |
} | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text(widget.title!), | |
), | |
body: Center( | |
child: !kIsWeb && defaultTargetPlatform == TargetPlatform.android | |
? FutureBuilder<void>( | |
future: retrieveLostData(), | |
builder: (BuildContext context, AsyncSnapshot<void> snapshot) { | |
switch (snapshot.connectionState) { | |
case ConnectionState.none: | |
case ConnectionState.waiting: | |
return const Text( | |
'You have not yet picked an image.', | |
textAlign: TextAlign.center, | |
); | |
case ConnectionState.done: | |
return _handlePreview(); | |
case ConnectionState.active: | |
if (snapshot.hasError) { | |
return Text( | |
'Pick image/video error: ${snapshot.error}}', | |
textAlign: TextAlign.center, | |
); | |
} else { | |
return const Text( | |
'You have not yet picked an image.', | |
textAlign: TextAlign.center, | |
); | |
} | |
} | |
}, | |
) | |
: _handlePreview(), | |
), | |
floatingActionButton: Column( | |
mainAxisAlignment: MainAxisAlignment.end, | |
children: <Widget>[ | |
Semantics( | |
label: 'image_picker_example_from_gallery', | |
child: FloatingActionButton( | |
onPressed: () { | |
isVideo = false; | |
_onImageButtonPressed(ImageSource.gallery, context: context); | |
}, | |
heroTag: 'image0', | |
tooltip: 'Pick Image from gallery', | |
child: const Icon(Icons.photo), | |
), | |
), | |
Padding( | |
padding: const EdgeInsets.only(top: 16.0), | |
child: FloatingActionButton( | |
onPressed: () { | |
isVideo = false; | |
_onImageButtonPressed( | |
ImageSource.gallery, | |
context: context, | |
isMultiImage: true, | |
isMedia: true, | |
); | |
}, | |
heroTag: 'multipleMedia', | |
tooltip: 'Pick Multiple Media from gallery', | |
child: const Icon(Icons.photo_library), | |
), | |
), | |
Padding( | |
padding: const EdgeInsets.only(top: 16.0), | |
child: FloatingActionButton( | |
onPressed: () { | |
isVideo = false; | |
_onImageButtonPressed( | |
ImageSource.gallery, | |
context: context, | |
isMedia: true, | |
); | |
}, | |
heroTag: 'media', | |
tooltip: 'Pick Single Media from gallery', | |
child: const Icon(Icons.photo_library), | |
), | |
), | |
Padding( | |
padding: const EdgeInsets.only(top: 16.0), | |
child: FloatingActionButton( | |
onPressed: () { | |
isVideo = false; | |
_onImageButtonPressed( | |
ImageSource.gallery, | |
context: context, | |
isMultiImage: true, | |
); | |
}, | |
heroTag: 'image1', | |
tooltip: 'Pick Multiple Image from gallery', | |
child: const Icon(Icons.photo_library), | |
), | |
), | |
if (_picker.supportsImageSource(ImageSource.camera)) | |
Padding( | |
padding: const EdgeInsets.only(top: 16.0), | |
child: FloatingActionButton( | |
onPressed: () { | |
isVideo = false; | |
_onImageButtonPressed(ImageSource.camera, context: context); | |
}, | |
heroTag: 'image2', | |
tooltip: 'Take a Photo', | |
child: const Icon(Icons.camera_alt), | |
), | |
), | |
Padding( | |
padding: const EdgeInsets.only(top: 16.0), | |
child: FloatingActionButton( | |
backgroundColor: Colors.red, | |
onPressed: () { | |
isVideo = true; | |
_onImageButtonPressed(ImageSource.gallery, context: context); | |
}, | |
heroTag: 'video0', | |
tooltip: 'Pick Video from gallery', | |
child: const Icon(Icons.video_library), | |
), | |
), | |
if (_picker.supportsImageSource(ImageSource.camera)) | |
Padding( | |
padding: const EdgeInsets.only(top: 16.0), | |
child: FloatingActionButton( | |
backgroundColor: Colors.red, | |
onPressed: () { | |
isVideo = true; | |
_onImageButtonPressed(ImageSource.camera, context: context); | |
}, | |
heroTag: 'video1', | |
tooltip: 'Take a Video', | |
child: const Icon(Icons.videocam), | |
), | |
), | |
], | |
), | |
); | |
} | |
Text? _getRetrieveErrorWidget() { | |
if (_retrieveDataError != null) { | |
final Text result = Text(_retrieveDataError!); | |
_retrieveDataError = null; | |
return result; | |
} | |
return null; | |
} | |
Future<void> _displayPickImageDialog( | |
BuildContext context, OnPickImageCallback onPick) async { | |
return showDialog( | |
context: context, | |
builder: (BuildContext context) { | |
return AlertDialog( | |
title: const Text('Add optional parameters'), | |
content: Column( | |
mainAxisSize: MainAxisSize.min, | |
children: <Widget>[ | |
TextField( | |
controller: maxWidthController, | |
keyboardType: | |
const TextInputType.numberWithOptions(decimal: true), | |
decoration: const InputDecoration( | |
hintText: 'Enter maxWidth if desired'), | |
), | |
TextField( | |
controller: maxHeightController, | |
keyboardType: | |
const TextInputType.numberWithOptions(decimal: true), | |
decoration: const InputDecoration( | |
hintText: 'Enter maxHeight if desired'), | |
), | |
TextField( | |
controller: qualityController, | |
keyboardType: TextInputType.number, | |
decoration: const InputDecoration( | |
hintText: 'Enter quality if desired'), | |
), | |
], | |
), | |
actions: <Widget>[ | |
TextButton( | |
child: const Text('CANCEL'), | |
onPressed: () { | |
Navigator.of(context).pop(); | |
}, | |
), | |
TextButton( | |
child: const Text('PICK'), | |
onPressed: () { | |
final double? width = maxWidthController.text.isNotEmpty | |
? double.parse(maxWidthController.text) | |
: null; | |
final double? height = maxHeightController.text.isNotEmpty | |
? double.parse(maxHeightController.text) | |
: null; | |
final int? quality = qualityController.text.isNotEmpty | |
? int.parse(qualityController.text) | |
: null; | |
onPick(width, height, quality); | |
Navigator.of(context).pop(); | |
}), | |
], | |
); | |
}); | |
} | |
} | |
typedef OnPickImageCallback = void Function( | |
double? maxWidth, double? maxHeight, int? quality); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment