Adjust GridView child height according to the dynamic content in flutter

前端 未结 5 2219
执念已碎
执念已碎 2020-12-23 17:14

How to implement this complex view in the flutter?

I am trying to implement a GridView with n columns and the child should be of a certa

5条回答
  •  心在旅途
    2020-12-23 17:44

    First let me tell you about how I ended up here:

    In my application I wanted a grid view to display my ad cards and all the data coming from the server database and images are coming from the server and images are in different sizes. I used FutureBuilder to map those data to GridView. First I tried to use:

    double cardWidth = MediaQuery.of(context).size.width / 3.3;
    double cardHeight = MediaQuery.of(context).size.height / 3.6;
    //....
    GridView.count(
      childAspectRatio: cardWidth / cardHeight,
      //..
    

    As you can see it will not dynamic to all cards. I came here like you and tried to use all answers those are great and you have to tackle a bit to understand how, but any of those answer completely solved my issue.

    Using @RomainRastel answer and thanks to his StaggeredGridView package. I had to use StaggeredGridView.count as my constructor to map all cards and for the staggeredTiles property I had to again map all cards and add for each StaggeredTile.fit(2).

    I'm sure you didn't get it still, so let's try a simple example so that you do not need to go somewhere else to find an answer:

    First add dependency to pubspec.yaml, now version is 0.2.5. You can checkout the latest one here.

    dependencies:
     flutter_staggered_grid_view: ^0.2.5
    

    If you are fetching data from internet or if you are going to copy paste this example, you have to also ad this dependency: http: ^0.12.0.

    import 'package:flutter/material.dart';
    
    //this is what you need to have for flexible grid
    import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
    
    //below two imports for fetching data from somewhere on the internet
    import 'dart:convert';
    import 'package:http/http.dart' as http;
    
    //boilerplate that you use everywhere
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "Flexible GridView",
          home: HomePage(),
        );
      }
    }
    
    //here is the flexible grid in FutureBuilder that map each and every item and add to a gridview with ad card
    class HomePage extends StatelessWidget {
      //this is should be somewhere else but to keep things simple for you,
      Future fetchAds() async {
        //the link you want to data from, goes inside get
        final response = await http
            .get('https://blasanka.github.io/watch-ads/lib/data/ads.json');
    
        if (response.statusCode == 200) return json.decode(response.body);
        return [];
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("Dynamic height GridView Demo"),
          ),
          body: FutureBuilder(
              future: fetchAds(),
              builder: (BuildContext context, AsyncSnapshot snapshot) {
                if (snapshot.hasData) {
                  return new Padding(
                    padding: const EdgeInsets.all(4.0),
                    //this is what you actually need
                    child: new StaggeredGridView.count(
                      crossAxisCount: 4, // I only need two card horizontally
                      padding: const EdgeInsets.all(2.0),
                      children: snapshot.data.map((item) {
                        //Do you need to go somewhere when you tap on this card, wrap using InkWell and add your route
                        return new AdCard(item);
                      }).toList(),
    
                      //Here is the place that we are getting flexible/ dynamic card for various images
                      staggeredTiles: snapshot.data
                          .map((_) => StaggeredTile.fit(2))
                          .toList(),
                      mainAxisSpacing: 3.0,
                      crossAxisSpacing: 4.0, // add some space
                    ),
                  );
                } else {
                  return Center(
                      child:
                          new CircularProgressIndicator()); // If there are no data show this
                }
              }),
        );
      }
    }
    
    //This is actually not need to be a StatefulWidget but in case, I have it
    class AdCard extends StatefulWidget {
      AdCard(this.ad);
    
      final ad;
    
      _AdCardState createState() => _AdCardState();
    }
    
    class _AdCardState extends State {
      //to keep things readable
      var _ad;
      String _imageUrl;
      String _title;
      String _price;
      String _location;
    
      void initState() {
        setState(() {
          _ad = widget.ad;
          //if values are not null only we need to show them
          _imageUrl = (_ad['imageUrl'] != '')
              ? _ad['imageUrl']
              : 'https://uae.microless.com/cdn/no_image.jpg';
          _title = (_ad['title'] != '') ? _ad['title'] : '';
          _price = (_ad['price'] != '') ? _ad['price'] : '';
          _location = (_ad['location'] != '') ? _ad['location'] : '';
        });
    
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return Card(
          semanticContainer: false,
          shape: const RoundedRectangleBorder(
            borderRadius: BorderRadius.all(Radius.circular(4.0)),
          ),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Image.network(_imageUrl),
              Text(_title),
              Text('\$ $_price'),
              Text(_location),
            ],
          ),
        );
      }
    }
    

    If you have any issue, here is complete example in a git repository.

    Good luck!

提交回复
热议问题