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<List> 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<List>(
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<Widget>((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>((_) => 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<AdCard> {
//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: <Widget>[
Image.network(_imageUrl),
Text(_title),
Text('\$ $_price'),
Text(_location),
],
),
);
}
}
If you have any issue, here is complete example in a git repository.
Good luck!