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/
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.
multi_image_picker_view: # latest version
image_picker: ^1.0.4
# or
file_picker: ^6.1.1
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);
},
);
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 */
);
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.
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.
),
GridView Draggable item
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,
),
),
),
),
],
);
},
DraggableItemInkWell
can be used instead of InkWell
inside builder
to handle proper clicks when using laptop touchpads.ImageFileView
is a custom widget to show the image using ImageFile
.Initial Widget
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
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,
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
Check the example to access all the custom examples.
<1.0.0
to >=1.0.0
MultiImagePickerController
picker
in MultiImagePickerController
.allowedImageTypes
removed.withData
removed.withReadStream
removed.MultiImagePickerView
addMoreBuilder
is removed. Now use addMoreButton
to define your custom Add More Button.showAddMoreButton
is removed. To hide the default Add More Button, pass null
in addMoreButton
field.initialContainerBuilder
is removed. Now use initialWidget
to define your custom Initial Widget.showInitialContainer
is removed. To hide the default Initial Widget, pass null
in initialWidget
field.itemBuilder
is removed. Now use builder
to define your custom Draggable item widget. You can now define different widget for different image (ImaegFile
).addMoreButtonTitle
is removed. Use addMoreButton
and pass DefaultAddMoreWidget
with custom parameters.addButtonTitle
is removed. Use initialWidget
and pass DefaultInitialWidget
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()
.Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.