Multi Image Picker View Flutter
A complete widget which can easily pick multiple images from device and display them in UI. Also picked image can be re-ordered and removed easily.
🚀 LIVE DEMO OF EXAMPLE PROJECT: https://shubham16g.github.io/multi_image_picker_view/
Features
- Pick multiple images
- Displayed in GridView
- Reorder picked images just by dragging
- Remove picked image
- Limit max images
- Fully customizable UI
Getting started
flutter pub add multi_image_picker_view
For image/file picker
flutter pub add image_picker
OR
flutter pub add file_picker
OR you can use any plugin to pick images/files.
pubspec.yaml
multi_image_picker_view: # latest version
image_picker: ^1.0.4
# or
file_picker: ^6.1.1
Usage
Define the controller
final controller = MultiImagePickerController(
picker: (bool allowMultiple) async {
// use image_picker or file_picker to pick images `pickImages`
final pickedImages = await pickImages(allowMultiple);
// convert the picked image list to `ImageFile` list and return it.
return pickedImages.map((e) => convertToImageFile(e)).toList();
}
);
OR
final controller = MultiImagePickerController(
maxImages: 15,
images: <ImageFile>[], // array of pre/default selected images
picker: (bool allowMultiple) async {
return await pickConvertedImages(allowMultiple);
},
);
UI Implementation
MultiImagePickerView(
controller: controller,
padding: const EdgeInsets.all(10),
);
OR
MultiImagePickerView(
controller: controller,
bulder: (BuldContext context, ImageFile imageFile) {
// here returning DefaultDraggableItemWidget. You can also return your custom widget as well.
return DefaultDraggableItemWidget(
imageFile: imageFile,
boxDecoration:
BoxDecoration(borderRadius: BorderRadius.circular(20)),
closeButtonAlignment: Alignment.topLeft,
fit: BoxFit.cover,
closeButtonIcon:
const Icon(Icons.delete_rounded, color: Colors.red),
closeButtonBoxDecoration: null,
showCloseButton: true,
closeButtonMargin: const EdgeInsets.all(3),
closeButtonPadding: const EdgeInsets.all(3),
);
},
initialWidget: DefaultInitialWidget(
centerWidget: Icon(Icons.image_search_outlined),
backgroundColor: Theme.of(context).colorScheme.secondary.withOpacity(0.05),
margin: EdgeInsets.zero,
), // Use any Widget or DefaultInitialWidget. Use null to hide initial widget
addMoreButton: DefaultAddMoreWidget(
icon: Icon(Icons.image_search_outlined),
backgroundColor: Theme.of(context).colorScheme.primaryColor.withOpacity(0.2),
), // Use any Widget or DefaultAddMoreWidget. Use null to hide add more button.
gridDelegate: /* Your SliverGridDelegate */,
draggable: /* true or false, images can be reordered by dragging by user or not, default true */,
shrinkWrap: /* true or false, to control GridView's shrinkWrap */
longPressDelayMilliseconds: /* time to press and hold to start dragging item */
onDragBoxDecoration: /* BoxDecoration when item is dragging */,
padding: /* GridView padding */
);
ImageFile
This package use ImageFile
entity to represent one image or file. Inside picker
method in MultiImagePickerController
, pick your images/files and convert it to list of ImageFile
object and then return it. The ImageFile
consists of:
final imageFile = ImageFile(
UniqueKey().toString(), // A unique key required to track it in grid view.
name: fileName,
extension: fileExtension,
path: fileFullPath,
);
Note: The package have two Extension functions to convert
XFile
(image_picker
plugin) andPlatformFile
(image_picker
plugin) toImageFile
object.final imageFile = convertXFileToImageFile(xFileObject);
andfinal imageFile = convertPlatformFileToImageFile(platformFileObject);
. This functions will help you to write your picker logic easily.
ImageFileView
The ImageFileView
is a widget which is used to display Image using ImageFile
object. This will work on web as well as mobile platforms.
child: ImageFileView(imageFile: imageFile),
child: ImageFileView(
imageFile: imageFile,
borderRadius: BorderRadius.circular(8),
fit: BoxFit.cover,
backgroundColor: Theme.of(context).colorScheme.background,
errorBuilder: (BuildContext context, Object error, StackTrace? trace) {
return MyCustomErrorWidget(imageFile: imageFile)
} // if errorBuilder is null, default error widget is used.
),
Custom UI
GridView Draggable item
- In builder, you can use either
DefaultDraggableItemWidget
or your full custom Widget. i.e.
builder: (context, imageFile) {
return Stack(
children: [
Positioned.fill(child: ImageFileView(imageFile: imageFile)),
Positioned(
top: 4,
right: 4,
child: DraggableItemInkWell(
borderRadius: BorderRadius.circular(2),
onPressed: () => controller.removeImage(imageFile),
child: Container(
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondary.withOpacity(0.4),
shape: BoxShape.circle,
),
child: Icon(
Icons.delete_forever_rounded,
size: 18,
color: Theme.of(context).colorScheme.background,
),
),
),
),
],
);
},
- The
DraggableItemInkWell
can be used instead ofInkWell
insidebuilder
to handle proper clicks when using laptop touchpads. ImageFileView
is a custom widget to show the image usingImageFile
.
Initial Widget
- You can use either
DefaultInitialWidget
or Custom widget or null if you don't want to show initial widget.
initialWidget: DefaultInitialWidget(
centerWidget: Icon(Icons.image_search_outlined),
backgroundColor: Theme.of(context).colorScheme.secondary.withOpacity(0.05),
margin: EdgeInsets.zero,
),
OR
initialWidget: SizedBox(
height: 170,
width: double.infinity,
child: Center(
child: ElevatedButton(
child: const Text('Add Images'),
onPressed: () {
controller.pickImages();
},
),
),
),
OR
initialWidget: null,
Initial Widget
- You can use either
DefaultInitialWidget
or Custom widget or null if you don't want to show initial widget.
addMoreButton: DefaultAddMoreWidget(
icon: Icon(Icons.image_search_outlined),
backgroundColor: Theme.of(context).colorScheme.primary.withOpacity(0.2),
),
OR
addMoreButton: SizedBox(
height: 170,
width: double.infinity,
child: Center(
child: TextButton(
style: TextButton.styleFrom(
backgroundColor: Colors.blue.withOpacity(0.2),
shape: const CircleBorder(),
),
onPressed: controller.pickImages,
child: const Padding(
padding: EdgeInsets.all(10),
child: Icon(
Icons.add,
color: Colors.blue,
size: 30,
),
),
),
),
),
OR
addMoreButton: null,
Get Picked Images
Picked Images can be get from controller.
final images = controller.images; // return Iterable<ImageFile>
for (final image in images) {
if (image.hasPath)
request.addFile(File(image.path!));
else
request.addFile(File.fromRawPath(image.bytes!));
}
request.send();
Also controller can perform more actions.
controller.pickImages();
controller.hasNoImages; // return bool
controller.maxImages; // return maxImages
controller.removeImage(imageFile); // remove image from the images
controller.clearImages(); // remove all images (clear selection)
controller.reOrderImage(oldIndex, newIndex); // reorder the image
Custom Examples
Check the example to access all the custom examples.
Migrating <1.0.0
to >=1.0.0
Changes in MultiImagePickerController
- Inbuilt image picker is removed. You have to provide your own image/file picker logic. This will provide you more controls over image/file picking. You have to pass your
picker
inMultiImagePickerController
. allowedImageTypes
removed.withData
removed.withReadStream
removed.
Changes in MultiImagePickerView
addMoreBuilder
is removed. Now useaddMoreButton
to define your custom Add More Button.showAddMoreButton
is removed. To hide the default Add More Button, passnull
inaddMoreButton
field.initialContainerBuilder
is removed. Now useinitialWidget
to define your custom Initial Widget.showInitialContainer
is removed. To hide the default Initial Widget, passnull
ininitialWidget
field.itemBuilder
is removed. Now usebuilder
to define your custom Draggable item widget. You can now define different widget for different image (ImaegFile
).addMoreButtonTitle
is removed. UseaddMoreButton
and passDefaultAddMoreWidget
with custom parameters.addButtonTitle
is removed. UseinitialWidget
and passDefaultInitialWidget
with custom parameters.longPressDelayMilliseconds
is added. This is used to define the press and hold duration to start dragging.onChange
is removed.MultiImagePickerView.of(context)
can be used inside anywhere in MultiImagePickerView get the instance of it's components. i.e.MultiImagePickerView.of(context).controller.pickImages()
.
My other flutter packages
- view_model_x - An Android similar state management package (StateFlow and SharedFlow with ViewModel) which helps to implement MVVM pattern easily.
Support
Contributors
Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.