介紹
Flutter GridView
幾乎與 ListView
相同煎饼,只是它提供了與 ListView
單向視圖的 2D 視圖比較。同時它也是移動應(yīng)用開發(fā)中非常受歡迎的小部件烟馅。如果你不相信我唠倦,那就舉個例子,打開你手機(jī)中的任何一個電子商務(wù)應(yīng)用咐吼,它肯定是依賴于 ListView
或 GridView
來顯示數(shù)據(jù)的疫向。
Amazon 移動應(yīng)用程序利用網(wǎng)格顯示數(shù)據(jù)
另一個是 PayTM咳蔚,它是印度流行的在線錢包服務(wù)應(yīng)用之一豪嚎,它廣泛使用網(wǎng)格布局來顯示不同的產(chǎn)品
背景
本文的最終目的是實(shí)現(xiàn)類似的界面:
但是,如果你注意到上面的圖像谈火,那是橫屏模式下的侈询。所以我將在本文中做以下的事情,當(dāng)應(yīng)用程序處于豎屏模式時糯耍,移動 APP 將在 ListView
中顯示項(xiàng)目扔字,當(dāng)它處于橫屏模式時,將會在網(wǎng)格中每行顯示3個條目温技。我還通過在單獨(dú)的類中移動 gridview 來實(shí)現(xiàn)創(chuàng)建自定義窗口小部件革为。
使用代碼
我將以我之前的文章為基礎(chǔ) Flutter Getting Started: Tutorial 4 ListView,我已經(jīng)創(chuàng)建了基于 ListView 的應(yīng)用程序舵鳞,這里是初始項(xiàng)目結(jié)構(gòu)和初始UI震檩。
這是我們開始構(gòu)建的初始代碼
class HomePage extends StatelessWidget {
final List<City> _allCities = City.allCities();
HomePage() {}
final GlobalKey scaffoldKey = new GlobalKey();
@override
Widget build(BuildContext context) {
return new Scaffold(
key: scaffoldKey,
appBar: new AppBar(
title: new Text(
"Cites around world",
style: new TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
color: Colors.black87),
),
),
body: new Padding(
padding: EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 0.0),
child: getHomePageBody(context)));
}
getHomePageBody(BuildContext context) {
return ListView.builder(
itemCount: _allCities.length,
itemBuilder: _getListItemUI,
padding: EdgeInsets.all(0.0),
);
}
Widget _getListItemUI(BuildContext context, int index,
{double imgwidth: 100.0}) {
return new Card(
child: new Column(
children: <Widget>[
new ListTile(
leading: new Image.asset(
"assets/" + _allCities[index].image,
fit: BoxFit.fitHeight,
width: imgwidth,
),
title: new Text(
_allCities[index].name,
style: new TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold),
),
subtitle: new Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Text(_allCities[index].country,
style: new TextStyle(
fontSize: 13.0, fontWeight: FontWeight.normal)),
new Text('Population: ${_allCities[index].population}',
style: new TextStyle(
fontSize: 11.0, fontWeight: FontWeight.normal)),
]),
onTap: () {
_showSnackBar(context, _allCities[index]);
},
)
],
));
}
_showSnackBar(BuildContext context, City item) {
final SnackBar objSnackbar = new SnackBar(
content: new Text("${item.name} is a city in ${item.country}"),
backgroundColor: Colors.amber,
);
Scaffold.of(context).showSnackBar(objSnackbar);
}
}
在開始實(shí)際任務(wù)之前,讓我簡要介紹一下我上面做過的事情
- 我已經(jīng)使用
ListView.builder
創(chuàng)建了簡單的ListView
蜓堕,它可以靈活地創(chuàng)建無限的 listitem 視圖抛虏,因?yàn)樗徽{(diào)用那些可以在屏幕上顯示的項(xiàng)目的回調(diào)函數(shù)。 - 我正在顯示城市信息套才,如城市地標(biāo)圖像迂猴,其次是城市名稱,城市所屬的國家和她的人口背伴。
- 最后點(diǎn)擊,它在屏幕底部顯示小的會自動消失的消息傻寂,稱為
SnackBar
。
現(xiàn)在開始我們的工作崎逃,正如我之前提到的眉孩,我們將把新的 widget 重構(gòu)為不同的類个绍,以保持我們的代碼模塊化并提高代碼的可讀性。因此浪汪,在 lib
文件夾下創(chuàng)建一個新的文件夾,并添加新的 DART 文件 mygridview.dart
广恢。
添加文件后,首先通過 'package:flutter/material.dart'
導(dǎo)入 material 組件呀潭,然后添加 MyGridView
類來繼承我們最喜歡的 StatelessWidget
并復(fù)寫 Build
函數(shù)钉迷,代碼如下所示
import 'package:flutter/material.dart';
import 'package:flutter5_gridlist/model/city.dart';
class MyGridView extends StatelessWidget {
final List<City> allCities;
MyGridView({Key key, this.allCities}) : super(key: key);
@override
Widget build(BuildContext context) {
return null;
}
}
我現(xiàn)在添加基本的 GridView 只顯示城市名稱至非,所以我將在重寫的 Build 函數(shù)中添加以下代碼
@override
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 3,
padding: EdgeInsets.all(16.0),
childAspectRatio: 8.0,
children: _getGridViewItems(context),
);
}
_getGridViewItems(BuildContext context){
List<Widget> allWidgets = new List<Widget>();
for (int i = 0; i < allCities.length; i++) {
var widget = new Text(allCities[i].name);
allWidgets.add(widget);
};
return allWidgets;
}
對上述代碼的解釋
-
GridView.count
方法將為應(yīng)用程序提供 GridView 小部件 -
crossAxisCount
屬性用于讓移動應(yīng)用程序知道我們想要顯示每行的項(xiàng)目數(shù) -
children
屬性將包含您希望在加載頁面時顯示的所有小部件 -
childAspectRatio
糠聪,它是每個子節(jié)點(diǎn)的橫軸與主軸范圍的比率,因?yàn)槲绎@示的是名稱趣惠,所以我統(tǒng)一設(shè)置為 8.0身害,以便減少兩個圖塊之間的邊距
這是UI的樣子
現(xiàn)在我們來改變 UI 讓其類似于我們看到的 ListView。在這里我創(chuàng)建了一個新的函數(shù)塌鸯,它將以 Card 的形式發(fā)送 City 類
// Create individual item
_getGridItemUI(BuildContext context, City item) {
return new InkWell(
onTap: () {
_showSnackBar(context, item);
},
child: new Card(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Image.asset(
"assets/" + item.image,
fit: BoxFit.fill,
),
new Expanded(
child: new Center(
child: new Column(
children: <Widget>[
new SizedBox(height: 8.0),
new Text(
item.name,
style: new TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
new Text(item.country),
new Text('Population: ${item.population}')
],
)))
],
),
elevation: 2.0,
margin: EdgeInsets.all(5.0),
));
}
上述代碼的解釋
- 我正在使用
Inkwell
類界赔,因?yàn)?Card 類不直接支持手勢,所以我把它包裝在 InkWell 類中咐低,利用它的onTap
事件替換 SnackBar - 其余代碼類似于
ListView
的卡片袜腥,但未指定寬度 - 此外,由于我們正在顯示完整的卡片羹令,因此我們不要忘記將
childAspectRatio
從 8.0 更改為 8.0/9.0,因?yàn)槲覀冃枰嗟母叨取?/li>
如果沒有忘記的話酒来,在開始做程序時我就說過肪凛,我將在縱向方向上顯示 ListView
,在橫向方向上顯示 GridView
伟墙,為了實(shí)現(xiàn)它我們需要 MediaQuery
類來識別方向。無論何時更改方向就乓,你都可以決定哪些代碼應(yīng)該被調(diào)用,也就是說生蚁,即使你傾斜移動窗口都會調(diào)用 Build
函數(shù),小部件也都會重新繪制绎秒。所以在 homepage.dart
類中我們將使用以下函數(shù)來處理 Orientation 更改的問題
getHomePageBody(BuildContext context) {
if (MediaQuery.of(context).orientation == Orientation.portrait)
return ListView.builder(
itemCount: _allCities.length,
itemBuilder: _getListItemUI,
padding: EdgeInsets.all(0.0),
);
else
return new MyGridView(allCities: _allCities);
}
因此尼摹,最終的 UI 將是這樣的
本教程結(jié)束
興趣點(diǎn)
請仔細(xì)閱讀這些文章蠢涝。它可能會給你一個你真正需要的指引:
- https://material.io/design/components/cards.html
- Github : https://github.com/thatsalok/FlutterExample/tree/master/flutter5_gridlist
Flutter 教程
Dart 教程
- DART2 Prima Plus?—?Tutorial 1
- DART2 Prima Plus?—?Tutorial 2?—?LIST
- DART2 Prima Plus?—?Tutorial 3?—?MAP
歷史
- 22-July-2018:第一個版本