Joe
Joe

Reputation: 343

How to create a custom gridview with appropriate aspect ratio?

I am trying to create a custom image picker that looks something like this:

enter image description here

As you can see the images are capped at a set height however maintain their aspect ratio (i.e. vertical images are vertical and horizontal images are horizontal). I have created the entire custom image picker. However, I'm struggling with the aspect ratio part. I'd prefer not to use a library, however, if it's easier then please provide the adjusted code.

Could you please provide a solution with code? FYI I'm using photo_manager to retrieve the images/videos.

This is what it all looks like right now:

enter image description here

Here is my code:

class MediaGrid extends StatefulWidget {
  @override
  _MediaGridState createState() => _MediaGridState();
}

class _MediaGridState extends State<MediaGrid> {
  List<Widget> _mediaList = [];
  int currentPage = 0;
  int? lastPage;
  @override
  void initState() {
    super.initState();
    _fetchNewMedia();
  }

  _handleScrollEvent(ScrollNotification scroll) {
    if (scroll.metrics.pixels / scroll.metrics.maxScrollExtent > 0.33) {
      if (currentPage != lastPage) {
        _fetchNewMedia();
      }
    }
  }

  _fetchNewMedia() async {
    lastPage = currentPage;
    var result = await PhotoManager.requestPermission();
    if (result) {
      // success
      //load the album list
      List<AssetPathEntity> albums =
          await PhotoManager.getAssetPathList(onlyAll: true);
      print(albums);
      List<AssetEntity> media =
          await albums[0].getAssetListPaged(currentPage, 60);
      print(media);
      List<Widget> temp = [];
      for (var asset in media) {
        temp.add(
          FutureBuilder<dynamic>(
            future: asset.thumbDataWithSize(300, 300),
            builder: (BuildContext context, snapshot) {
              if (snapshot.connectionState == ConnectionState.done)
                return Stack(
                  children: <Widget>[
                    Expanded(
                      child: Image.memory(snapshot.data, fit: BoxFit.cover),
                    ),
                    if (asset.type == AssetType.video)
                      Align(
                        alignment: Alignment.bottomRight,
                        child: Padding(
                          padding: EdgeInsets.only(right: 5, bottom: 5),
                          child: Icon(
                            Icons.videocam_rounded,
                            color: Colors.white,
                          ),
                        ),
                      ),
                  ],
                );
              return Container();
            },
          ),
        );
      }
      setState(() {
        _mediaList.addAll(temp);
        currentPage++;
      });
    } else {
      // fail
      /// if result is fail, you can call `PhotoManager.openSetting();`  to open android/ios applicaton's setting to get permission
    }
  }

  @override
  Widget build(BuildContext context) {
    return NotificationListener<ScrollNotification>(
      onNotification: (ScrollNotification scroll) {
        return _handleScrollEvent(scroll);
      },
      child: GridView.builder(
          physics: NeverScrollableScrollPhysics(),
          itemCount: _mediaList.length,
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 2,
            mainAxisSpacing: 5.0,
            crossAxisSpacing: 5.0,
          ),
          itemBuilder: (BuildContext context, int index) {
            return _mediaList[index];
          }),
    );
  }
}

Upvotes: 1

Views: 1237

Answers (3)

Vishnu Priyan S S
Vishnu Priyan S S

Reputation: 79

You can use https://pub.dev/packages/flutter_staggered_grid_view This plugin has options to modify the aspect ratio

Upvotes: 0

Md. Yeasin Sheikh
Md. Yeasin Sheikh

Reputation: 63569

I hope this is thing you are looking for, replace container with image.

import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';

import 'dart:math' as math;

class TestScreen extends StatefulWidget {
  TestScreen({Key? key}) : super(key: key);

  @override
  _TestScreenState createState() => _TestScreenState();
}

class _TestScreenState extends State<TestScreen> {
  final _items = List.generate(
      100,
      (index) => ClipRRect(
            borderRadius: BorderRadius.circular(
              8,
            ),
            child: Container(
              height: 124,
              color: Color(
                (math.Random().nextDouble() * 0xFFFFFF).toInt(),
              ).withOpacity(1.0),
            ),
          ));
  @override
  Widget build(BuildContext context) {
    return Container(
      child: StaggeredGridView.countBuilder(
        mainAxisSpacing: 2,
        crossAxisSpacing: 2,
        crossAxisCount: 6,
        itemCount: 100,
        itemBuilder: (context, index) {
          return _items[index];
        },
        staggeredTileBuilder: (index) {
          if (index % 6 == 0 || index % 6 == 3) {
            return StaggeredTile.count(2, 1);
          } else if (index % 6 == 1 || index % 6 == 2) {
            return StaggeredTile.count(4, 1);
          } else
            return StaggeredTile.count(3, 1);
        },
      ),
    );
  }
}

Upvotes: 1

Ibrahim Ali
Ibrahim Ali

Reputation: 2503

This idea will only work if you wish to give each image a single specified height. And using fit: BoxFit.cover to fill up the remaining space.

Now you must find a way to get the width of each image, in my code its of Network Images

From here use the width as the flex value.

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  Future<Size> _calculateImageDimension(String url) {
    Completer<Size> completer = Completer();
    Image image = Image.network(url);
    image.image.resolve(ImageConfiguration()).addListener(
      ImageStreamListener(
        (ImageInfo image, bool synchronousCall) {
          var myImage = image.image;
          Size size = Size(myImage.width.toDouble(), myImage.height.toDouble());
          completer.complete(size);
        },
      ),
    );
    return completer.future;
  }

  @override
  Widget build(BuildContext context) {
    //for odd no. of images you might have to add more conditions to your widget
    final _netWorkimages = [
      'https://images.pexels.com/photos/7179053/pexels-photo-7179053.jpeg?cs=srgb&dl=pexels-olya-prutskova-7179053.jpg&fm=jpg',
      'https://images.pexels.com/photos/7527509/pexels-photo-7527509.jpeg?cs=srgb&dl=pexels-john-lee-7527509.jpg&fm=jpg',
      'https://images.pexels.com/photos/8018591/pexels-photo-8018591.jpeg?cs=srgb&dl=pexels-inna-stellinna-8018591.jpg&fm=jpg',
      'https://images.pexels.com/photos/3244513/pexels-photo-3244513.jpeg?cs=srgb&dl=pexels-andy-vu-3244513.jpg&fm=jpg',
      'https://images.pexels.com/photos/694587/pexels-photo-694587.jpeg?cs=srgb&dl=pexels-samuel-silitonga-694587.jpg&fm=jpg',
      'https://images.pexels.com/photos/5121986/pexels-photo-5121986.jpeg?cs=srgb&dl=pexels-marcelo-chagas-5121986.jpg&fm=jpg',
      'https://images.pexels.com/photos/4519234/pexels-photo-4519234.jpeg?cs=srgb&dl=pexels-dinielle-de-veyra-4519234.jpg&fm=jpg',
      'https://images.pexels.com/photos/2286385/pexels-photo-2286385.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260',
      'https://images.pexels.com/photos/35629/bing-cherries-ripe-red-fruit.jpg?cs=srgb&dl=pexels-pixabay-35629.jpg&fm=jpg',
      'https://images.pexels.com/photos/4033324/pexels-photo-4033324.jpeg?cs=srgb&dl=pexels-karolina-grabowska-4033324.jpg&fm=jpg'
    ];

    List<Future<Size>> _niSizes = [];

    _netWorkimages.forEach((url) {
      _niSizes.add(_calculateImageDimension(url));
    });

    return FutureBuilder<List<Size>>(
      future: Future.wait(_niSizes),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting)
          return Center(
            child: CircularProgressIndicator(),
          );
        else
          return ListView.builder(
              itemCount: _netWorkimages.length - 1,
              itemBuilder: (context, i) {
                return i.isEven
                    ? Container(
                        height: 120,
                        child: Row(
                          children: [
                            Flexible(
                                flex: snapshot.data![i].width.toInt(),
                                child: Padding(
                                  padding: const EdgeInsets.all(4.0),
                                  child: Image(
                                    image: NetworkImage(_netWorkimages[i]),
                                    fit: BoxFit.cover,
                                    height: double.infinity,
                                    width: double.infinity,
                                  ),
                                )),
                            Flexible(
                                flex: snapshot.data![i + 1].width.toInt(),
                                child: Padding(
                                  padding: const EdgeInsets.all(4.0),
                                  child: Image(
                                    image: NetworkImage(_netWorkimages[i + 1]),
                                    fit: BoxFit.cover,
                                    height: double.infinity,
                                    width: double.infinity,
                                  ),
                                )),
                          ],
                        ),
                      )
                    : SizedBox.shrink();
              });
      },
    );
  }
}

enter image description here

Upvotes: 1

Related Questions