[譯] Flutter 系列入門教程五:網(wǎng)格

介紹

Flutter GridView 幾乎與 ListView 相同,只是它提供了與 ListView 單向視圖的 2D 視圖比較。同時它也是移動應用開發(fā)中非常受歡迎的小部件。如果你不相信我,那就舉個例子,打開你手機中的任何一個電子商務應用,它肯定是依賴于 ListViewGridView 來顯示數(shù)據(jù)的。

Amazon 移動應用程序利用網(wǎng)格顯示數(shù)據(jù)

image

另一個是 PayTM,它是印度流行的在線錢包服務應用之一,它廣泛使用網(wǎng)格布局來顯示不同的產(chǎn)品

image

背景

本文的最終目的是實現(xiàn)類似的界面:

image

但是,如果你注意到上面的圖像,那是橫屏模式下的。所以我將在本文中做以下的事情,當應用程序處于豎屏模式時,移動 APP 將在 ListView 中顯示項目,當它處于橫屏模式時,將會在網(wǎng)格中每行顯示3個條目。我還通過在單獨的類中移動 gridview 來實現(xiàn)創(chuàng)建自定義窗口小部件。

使用代碼

我將以我之前的文章為基礎 Flutter Getting Started: Tutorial 4 ListView,我已經(jīng)創(chuàng)建了基于 ListView 的應用程序,這里是初始項目結構和初始UI。

這是我們開始構建的初始代碼

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);
  }
}

在開始實際任務之前,讓我簡要介紹一下我上面做過的事情

  • 我已經(jīng)使用 ListView.builder 創(chuàng)建了簡單的 ListView,它可以靈活地創(chuàng)建無限的 listitem 視圖,因為它只調用那些可以在屏幕上顯示的項目的回調函數(shù)。
  • 我正在顯示城市信息,如城市地標圖像,其次是城市名稱,城市所屬的國家和她的人口。
  • 最后點擊,它在屏幕底部顯示小的會自動消失的消息,稱為 SnackBar

現(xiàn)在開始我們的工作,正如我之前提到的,我們將把新的 widget 重構為不同的類,以保持我們的代碼模塊化并提高代碼的可讀性。因此,在 lib 文件夾下創(chuàng)建一個新的文件夾,并添加新的 DART 文件 mygridview.dart。

添加文件后,首先通過 'package:flutter/material.dart' 導入 material 組件,然后添加 MyGridView 類來繼承我們最喜歡的 StatelessWidget 并復寫 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 方法將為應用程序提供 GridView 小部件
  • crossAxisCount 屬性用于讓移動應用程序知道我們想要顯示每行的項目數(shù)
  • children 屬性將包含您希望在加載頁面時顯示的所有小部件
  • childAspectRatio,它是每個子節(jié)點的橫軸與主軸范圍的比率,因為我顯示的是名稱,所以我統(tǒng)一設置為 8.0,以便減少兩個圖塊之間的邊距

這是UI的樣子

image

現(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 類,因為 Card 類不直接支持手勢,所以我把它包裝在 InkWell 類中,利用它的 onTap 事件替換 SnackBar
  • 其余代碼類似于 ListView 的卡片,但未指定寬度
  • 此外,由于我們正在顯示完整的卡片,因此我們不要忘記將 childAspectRatio 從 8.0 更改為 8.0/9.0,因為我們需要更多的高度。

如果沒有忘記的話,在開始做程序時我就說過,我將在縱向方向上顯示 ListView,在橫向方向上顯示 GridView,為了實現(xiàn)它我們需要 MediaQuery 類來識別方向。無論何時更改方向,你都可以決定哪些代碼應該被調用,也就是說,即使你傾斜移動窗口都會調用 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 將是這樣的

image

本教程結束

興趣點

請仔細閱讀這些文章。它可能會給你一個你真正需要的指引:

  1. https://material.io/design/components/cards.html
  2. Github : https://github.com/thatsalok/FlutterExample/tree/master/flutter5_gridlist

Flutter 教程

  1. Flutter Getting Started: Tutorial 1 Basics
  2. Flutter Getting Started: Tutorial 4 ListView

Dart 教程

  1. DART2 Prima Plus?—?Tutorial 1
  2. DART2 Prima Plus?—?Tutorial 2?—?LIST
  3. DART2 Prima Plus?—?Tutorial 3?—?MAP

歷史

  • 22-July-2018:第一個版本
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容