根据颤振中的动态内容调整GridView子级高度
如何在颤振中实现此复杂视图? 我试图实现一个包含n列的根据颤振中的动态内容调整GridView子级高度,gridview,dart,flutter,staggered-gridview,Gridview,Dart,Flutter,Staggered Gridview,如何在颤振中实现此复杂视图? 我试图实现一个包含n列的GridView,子对象应该具有一定的纵横比(比如1.3),但子对象的高度应该是(用Android术语包装内容) 我被卡住了,因为作为fas,我理解GridView的childAspectRatio:1.3(默认值:1)总是以相同的纵横比布置子对象,而不是动态内容 注意:子对象应根据图像的高度扩展其高度 用例:我试图实现如下视图,其中图像被包装height=wrap content,以便在这种情况下,具有拉伸高度的图像可以看起来很好,并形成类
GridView
,子对象应该具有一定的纵横比(比如1.3),但子对象的高度应该是(用Android术语包装内容)
我被卡住了,因为作为fas,我理解GridView的childAspectRatio:1.3
(默认值:1)总是以相同的纵横比布置子对象,而不是动态内容
注意:子对象应根据图像的高度扩展其高度
用例:我试图实现如下视图,其中图像被包装height=wrap content
,以便在这种情况下,具有拉伸高度的图像可以看起来很好,并形成类似StaggeredGridView
的结构
这里有两件事:
DecorationImage
小部件上的BoxFit.cover
类GridViewExample扩展StatefulWidget{
@凌驾
_GridViewExampleState createState()=>new_GridViewExampleState();
}
类\u GridViewExampleState扩展状态{
@凌驾
小部件构建(构建上下文){
归还新脚手架(
车身:新衬垫(
填充:常数边集全部(8.0),
子项:新建EdgridView.countBuilder(
交叉轴计数:4,
物品计数:8,
itemBuilder:(BuildContext上下文,int索引)=>新容器(
装饰:新盒子装饰(
图片:新装饰图片(
图像:新的网络图像(“https://i.imgur.com/EVTkpZL.jpg"),
适合:BoxFit.cover
)
)
),
交错文件生成器:(int索引)=>
新的交错文件计数(2,index.isEven?2:1),
主轴间距:4.0,
交叉轴间距:4.0,
),),
);
}
}
对于任何一种相对简单的方法(即,在没有深入了解颤振中布局的工作原理的情况下),您都需要在构建任何内容之前获得图像的大小。它描述了如何使用和来实现这一点
然后你可以使用@aziza的例子,一旦你知道了图像的基本尺寸
另一种方法是在存储图像/URL列表的任何位置存储图像大小或至少是纵横比(我不知道您是如何填充图像列表的,因此我无法帮助您)
如果您希望它完全基于图像的大小,而不是像网格一样,那么您可以使用小部件来实现。不过有一个警告——我相信它不会很好地处理大量的项目,因为它每次都必须把所有的孩子都放在外面,但我可能错了。如果您没有大量的项目,可以使用Flow+a作为滚动部分
如果你想要有大量的项目(和/或想做一些像动态加载新项目的事情),你可能需要做一些事情-我认为这会更有效,但你仍然需要做一些事情来了解图像的大小
最后一个可能的解决方案(我不知道这到底是怎么回事)是让两个可滚动视图并排并同步它们的位置。不过,您必须将shrinkwrap设置为true,这样才能进行滚动,而且您还必须知道每个图像的高度,这样才能决定将每个图像放在哪一侧
希望这至少能帮助你开始 编辑:我在0.2.0中添加了构造函数
交错文件.fit
。有了它,你应该能够构建你的应用程序;-)
第一条评论:
现在,布局和子渲染是完全独立的。因此,正如@rmtmckenzie所说,您必须获得图像大小才能创建瓷砖。
然后,您可以使用StaggedTitle.count
构造函数,为mainAxisCellCount
参数设置一个双值:新建StaggedTitle.count(x,x*h/w)
(其中h
是图像的高度,而w
是图像的宽度。这样,具有相同纵横比的平铺就可以与图像具有相同的纵横比
您想要完成的工作将需要更多的工作,因为您希望在图像下方有一个包含一些信息的区域。为此,我认为您必须在创建平铺之前计算平铺的实际宽度,并使用交错文件.extent
构造函数
我知道这并不理想,我目前正在研究一种新的方式来创建布局。我希望这将有助于构建像您这样的场景。首先让我告诉您我是如何在这里结束的: 在我的应用程序中,我需要一个网格视图来显示我的广告卡,所有来自服务器数据库的数据和图像都来自服务器,并且图像大小不同。我使用
FutureBuilder
将这些数据映射到GridView
。首先,我尝试使用:
double cardWidth = MediaQuery.of(context).size.width / 3.3;
double cardHeight = MediaQuery.of(context).size.height / 3.6;
//....
GridView.count(
childAspectRatio: cardWidth / cardHeight,
//..
正如你所看到的,它不会对所有的卡片都是动态的。我和你一样来到这里,尝试使用所有的答案,这些答案都很好,你必须解决一些问题来了解如何解决,但这些答案中的任何一个都完全解决了我的问题
使用@ROMAINRUSTEL回答并感谢他的软件包。我必须使用StaggedGridView.count
作为映射所有卡片的构造函数,对于StaggedTitles
属性,我必须再次映射所有卡片,并为每个StaggedTitle.fit(2)
我相信您仍然没有理解,因此让我们尝试一个简单的示例,这样您就不需要去其他地方寻找答案:
首先将依赖项添加到pubspec.yaml
,现在的版本是0.2.5
。您可以签出最新的版本
如果您正在从internet获取数据,或者
double cardWidth = MediaQuery.of(context).size.width / 3.3;
double cardHeight = MediaQuery.of(context).size.height / 3.6;
//....
GridView.count(
childAspectRatio: cardWidth / cardHeight,
//..
dependencies:
flutter_staggered_grid_view: ^0.2.5
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),
],
),
);
}
}
crossAxisCount:
StaggeredTile.fit(value)
crossAxisCount: 2,
staggeredTileBuilder: (int index) => new StaggeredTile.fit(1),
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: FutureBuilder(
future: NetworkHelper.getData(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null)
return Center(child: CircularProgressIndicator());
else {
var data = snapshot.data;
print(data);
List<dynamic> jsonData = jsonDecode(snapshot.data);
return StaggeredGridView.countBuilder(
crossAxisCount: 4,
itemCount: jsonData.length,
itemBuilder: (BuildContext context, int index) {
JsonModelClass models =
JsonModelClass.fromJson(jsonData[index]);
String url = models.urls.regular;
return GestureDetector(
onTap: () {
print(url);
},
child: Card(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.network(url),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text("${models.user.name}"),
Card(
child: Padding(
padding: const EdgeInsets.all(3.0),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
Icon(
Icons.favorite,
color: Colors.red,
),
SizedBox(
width: 6,
),
Text("${models.likes}")
],
),
),
),
],
),
)
],
),
),
);
},
staggeredTileBuilder: (int index) {
JsonModelClass models =
JsonModelClass.fromJson(jsonData[index]);
var height = (models.height * 2) /
models.width; //calculating respective 'height' of image in view, 'model.height' is image's original height received from json.
return StaggeredTile.count(
2,
height +
0.5); //Adding extra 0.5 space for other content under the image
},
mainAxisSpacing: 4,
crossAxisSpacing: 4,
);
}
},
),
),
);
}
}