skip to Main Content

I am partially new to Flutter. I am trying to achieve a Dialog which carries an expandable grid of images (images to be added by the user), and every time the user adds an image (using the add/plus button) the added image inserts before the add/plus button.

A pictorial representation of what I want to achieve:
enter image description here

I hope my question/code/image make sense. Thanks in advance.

My current incomplete code:

List<Widget> getAttachFileDialogContent() {
    List<Widget> content = [];
    content.add(
      Column(
        children: [
          Container(
            decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(20),
                border: Border.all(
                  color: purple1.withOpacity(0.25),
                )),
            margin: const EdgeInsets.only(left: 16, right: 16, bottom: 8),
            padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
            width: double.infinity,
            height: 135,
            child: //I want to use the grid here,
                   //I tried to use GridView, but didn't know how to add/insert images
                   //Even if I had added images, I couldn't know how to add to specific index 
                     position
          ),

        MyElevatedButton(
                width: 100,
                text: 'Done',
                elevation: 0,
                onPressed: () {},
              ),
            ],
          ),
        ],
      ),
    );
    return content;
  }

2

Answers


  1. You can use GridView from flutter. for more: https://docs.flutter.dev/cookbook/lists/grid-lists.

    or If you want any examples. please visit Medium.com and search for what you want. You can find more informations posted by experienced devs. For example:
    https://medium.com/flutter-community/flutter-widgets-grid-view-the-whole-picture-34d2dd6dff9f

    Login or Signup to reply.
  2. If I understand what you want, it’s a bit similar to what I’m doing, except that instead of putting the images in a GrindView I’m putting them in a carousel . In this example I’m editing an ad for a Virtual Store. The part of images to be added in a temporary list before pressing the ADD button. which has not yet been implemented. But I think this might help you.

    import 'package:ecommerce/models/product.dart';
    import 'package:ecommerce/screens/edit_product/components/images_form.dart';
    import 'package:flutter/material.dart';
    
    class EditProductScreen extends StatelessWidget {
      const EditProductScreen({Key? key, required this.product}) : super(key: key);
    
      final Product product;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Editar Anúncio'),
            centerTitle: true,
          ),
          body: ListView(
            children: [
              ImagesForm(product: product),
            ],
          ),
        );
      }
    }
    
    import 'dart:io';
    import 'package:flutter/material.dart';
    import 'package:flutter/cupertino.dart';
    import 'package:flutter/foundation.dart' show kIsWeb;
    
    import 'package:ecommerce/models/product.dart';
    import 'package:ecommerce/screens/edit_product/components/image_source_sheet.dart';
    import 'package:ecommerce/screens/edit_product/components/image_source_web.dart';
    
    import 'package:carousel_slider/carousel_slider.dart';
    import 'package:universal_html/html.dart' as html;
    
    class ImagesForm extends StatelessWidget {
      const ImagesForm({
        Key? key,
        this.product,
        this.imageSourceSheet,
      }) : super(key: key);
    
      final Product? product;
      final ImageSourceSheet? imageSourceSheet;
    
      @override
      Widget build(BuildContext context) {
        return FormField<List<dynamic>>(
          initialValue: List.from(product!.images!),
          builder: (state) {
            void onImageSelected(File file) {
              state.value!.add(file);
              state.didChange(state.value);
              Navigator.of(context).pop();
            }
    
            void onImageSelectedList(List<File> files) {
              state.value!.addAll(files);
              state.didChange(state.value);
              Navigator.of(context).pop();
            }
    
            void onImageSelectedWeb(List<html.File> files) async {
              for (html.File file in files) {
                html.FileReader reader = html.FileReader();
                reader.readAsDataUrl(file);
                reader.onLoadEnd.listen((event) {
                  state.value!.add(reader.result.toString());
                  state.didChange(state.value);
                });
              }
              Navigator.of(context).pop();
            }
    
            Widget buildImageSourceSheet() {
              if (kIsWeb) {
                return ImageSourceWeb(
                  onImageSelectedWeb: onImageSelectedWeb,
                );
              } else if (imageSourceSheet?.local == 'gallery') {
                return ImageSourceSheet(
                  onImageSelectedList: onImageSelectedList,
                );
              } else {
                return ImageSourceSheet(
                    onImageSelected: onImageSelected,
                    onImageSelectedList: onImageSelectedList);
              }
            }
    
            return CarouselSlider(
              options: CarouselOptions(
                initialPage: 0,
                enableInfiniteScroll: state.value!.isEmpty ? false : true,
                height: 400,
                enlargeCenterPage: true,
                disableCenter: true,
              ),
              items: state.value!.map<Widget>((image) {
                return Stack(
                  fit: StackFit.expand,
                  children: <Widget>[
                    if (image is String)
                      Image.network(
                        image,
                        fit: BoxFit.cover,
                      )
                    else if (image is File)
                      Image.file(
                        image,
                        fit: BoxFit.cover,
                      ),
                    Align(
                      alignment: Alignment.topRight,
                      child: IconButton(
                        icon: const Icon(
                          Icons.delete,
                          size: 40,
                          color: Colors.red,
                        ),
                        onPressed: () {
                          state.value!.remove(image);
                          state.didChange(state.value);
                        },
                      ),
                    )
                  ],
                );
              }).toList()
                ..add(
                  kIsWeb
                      ? Container(
                          color: Colors.grey[100],
                          child: IconButton(
                              icon: Icon(
                                Icons.add_a_photo,
                                size: 60,
                                color: Theme.of(context).primaryColor,
                              ),
                              onPressed: () {
                                showDialog(
                                    context: context,
                                    builder: (_) => buildImageSourceSheet());
                              }),
                        )
                      : Material(
                          color: Colors.grey[100],
                          child: IconButton(
                              icon: Icon(
                                Icons.add_a_photo,
                                size: 60,
                                color: Theme.of(context).primaryColor,
                              ),
                              onPressed: () {
                                if (Platform.isAndroid) {
                                  showModalBottomSheet(
                                      context: context,
                                      builder: (_) => buildImageSourceSheet());
                                } else if (Platform.isIOS) {
                                  showCupertinoModalPopup(
                                      context: context,
                                      builder: (_) => buildImageSourceSheet());
                                } else {
                                  showDialog(
                                      context: context,
                                      builder: (_) => buildImageSourceSheet());
                                }
                              })),
                ),
            );
          },
        );
      }
    }
    
    import 'dart:async';
    import 'package:universal_html/html.dart' as html;
    import 'package:flutter/material.dart';
    
    class ImageSourceWeb extends StatelessWidget {
      ImageSourceWeb({Key? key, this.onImageSelectedWeb}) : super(key: key);
    
      final Function(List<html.File>)? onImageSelectedWeb;
    
      List<html.File> _files = [];
    
      Future<void> pickFiles() async {
        final completer = Completer<List<html.File>>();
        final html.FileUploadInputElement input = html.FileUploadInputElement()
          ..accept = 'image/*'
          ..multiple = true;
    
        input.onChange.listen((e) {
          final files = input.files!;
          completer.complete(files);
        });
        input.click();
        _files = await completer.future;
      }
    
      Future<void> pickImages() async {
        bool mounted = true;
        try {
          await pickFiles();
          if (mounted && _files.isNotEmpty) {
            final List<html.File> files = List.from(_files);
            onImageSelectedWeb?.call(files);
          }
        } finally {
          mounted = false;
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return AlertDialog(
          title: const Text('Selecionar fotos:'),
          content: const Text('Escolha as fotos do seu dispositivo!'),
          actions: [
            TextButton(
              onPressed: () async {
                await pickImages();
                //Navigator.of(context).pop();
              },
              child: const Text('Carregar...'),
            ),
            TextButton(
              onPressed: () {
                Navigator.of(context).pop();
              },
              child: const Text('Cancelar'),
            )
          ],
        );
      }
    }
    
    import 'dart:io';
    import 'dart:async';
    import 'package:ecommerce/common/button/custom_text_button.dart';
    import 'package:flutter/cupertino.dart';
    import 'package:flutter/material.dart';
    import 'package:image_cropper/image_cropper.dart';
    import 'package:image_picker/image_picker.dart';
    
    
    // ignore: must_be_immutable
    class ImageSourceSheet extends StatelessWidget {
      ImageSourceSheet({
        Key? key,
        this.onImageSelected,
        required this.onImageSelectedList,
        this.local,
      }) : super(key: key);
    
      final ImagePicker picker = ImagePicker();
    
      final Function(File)? onImageSelected;
      final Function(List<File>) onImageSelectedList;
    
      String? local = '';
    
      @override
      Widget build(BuildContext context) {
        Future<void> editImage(String? path) async {
          if (path != null) {
            final croppedFile = await ImageCropper().cropImage(
              sourcePath: path,
              compressFormat: ImageCompressFormat.jpg,
              compressQuality: 100,
              uiSettings: [
                AndroidUiSettings(
                    toolbarTitle: 'Editar Imagem',
                    toolbarColor: Theme
                        .of(context)
                        .primaryColor,
                    toolbarWidgetColor: Colors.white,
                    initAspectRatio: CropAspectRatioPreset.original,
                    lockAspectRatio: false),
                IOSUiSettings(
                  title: 'Editar Imagem',
                  cancelButtonTitle: 'Cancelar',
                  doneButtonTitle: 'Concluir',
                ),
                WebUiSettings(
                  context: context,
                  presentStyle: CropperPresentStyle.dialog,
                  boundary: const CroppieBoundary(
                    width: 520,
                    height: 520,
                  ),
                  viewPort: const CroppieViewPort(
                      width: 480, height: 480, type: 'circle'),
                  enableExif: true,
                  enableZoom: true,
                  showZoomer: true,
                ),
              ],
            );
            if (croppedFile != null) {
              final originalFile = File(path);
              await originalFile.writeAsBytes(await croppedFile.readAsBytes());
              onImageSelected!(originalFile);
            }
          }
        }
    
        Future<void> imgGallery() async {
          local = 'gallery';
          final List<XFile> xfiles = await picker.pickMultiImage();
          final List<File> files = xfiles.map((xfile) => File(xfile.path)).toList();
          if (files.length == 1) {
            File file = files.first;
            editImage(file.path);
          } else {
            onImageSelectedList(List.from(files));
          }
        }
    
        Future<void> imgCamera() async {
          XFile? photo = await picker.pickImage(source: ImageSource.camera);
          editImage(photo!.path);
        }
    
        if (Platform.isAndroid) {
          return BottomSheet(
            builder: (_) => Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                CustomTextButton(
                  onPressed: () {
                    imgCamera();
                  },
                  text: 'Câmera',
                  fontSize: 18,
                ),
                const Divider(
                  height: 5,
                ),
                CustomTextButton(
                  onPressed: () {
                    imgGallery();
                  },
                  text: 'Galeria',
                  fontSize: 18,
                ),
                //const SizedBox(height: 8,),
                const Divider(
                  height: 2,
                  thickness: 2,
                ),
                CustomTextButton(
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                  text: 'Cancelar',
                  color: Colors.red,
                  fontSize: 18,
                ),
              ],
            ),
            onClosing: () {},
          );
        } else if (Platform.isIOS) {
          return CupertinoActionSheet(
            title: const Text('Selecionar a foto para o item:'),
            message: const Text('Escolha a origem da foto!'),
            cancelButton: CupertinoActionSheetAction(
              onPressed: Navigator.of(context).pop,
              child: const Text('Cancelar'),
            ),
            actions: [
              CupertinoActionSheetAction(
                isDefaultAction: true,
                child: const Text('Câmera'),
                onPressed: () {
                  imgCamera();
                },
              ),
              CupertinoActionSheetAction(
                child: const Text('Galeria'),
                onPressed: () {
                  imgGallery();
                },
              ),
            ],
          );
        } else {
          return AlertDialog(
            title: const Text('Selecionar foto para o item:'),
            content: const Text('Escolher Fotos!'),
            actions: [
              CustomTextButton(
                onPressed: () {
                  imgCamera();
                },
                text: 'Câmera',
                fontSize: 18,
              ),
              const Divider(
                height: 5,
              ),
              CustomTextButton(
                onPressed: () {
                  imgGallery();
                },
                text: 'Galeria',
                fontSize: 18,
              ),
              TextButton(
                onPressed: () {
                  Navigator.of(context).pop();
                },
                child: const Text('Cancelar'),
              )
            ],
          );
        }
      }
    }
    

    I hope I understood and was able to help you.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search