Flutter第二章(Image,ListView,GridView)

版權(quán)聲明:本文為作者原創(chuàng)書籍。轉(zhuǎn)載請注明作者和出處,未經(jīng)授權(quán),嚴(yán)禁私自轉(zhuǎn)載,侵權(quán)必究!?。?/a>

情感語錄: 請你相信,歲月會成就最好的自己,時光也必將打磨出你獨一無二的美麗。

哈嘍!大家好,上一章節(jié)介紹了自定義Widget、Center組件、Text組件、MaterialApp組件、Scaffold組件的簡單應(yīng)用你都掌握了嗎?上章知識回顧 戳 Flutter基礎(chǔ)第一章相信你在上一章節(jié)的練習(xí)中已經(jīng)感受到了 ' Hot Reload 的魅力,Flutter舍棄xml使用代碼描述布局,布局的變動能立刻反映出變化,告別了原生中的重新編譯安裝的過程,你覺得怎樣呢?反正我是覺得很爽 O(∩_∩)O

本章簡要:

本章主要講解圖片組件(Image)、列表組件(ListView)、網(wǎng)格列表組件(GridView)。做過原生開發(fā)的同學(xué)對這三個組件感覺應(yīng)該是親切至極。像極了原生中的ImageView 和ListView以及 GridView控件,在Flutter中用法也是極其相似。

一、圖片組件(Image)

Image組件是顯示圖像的組件,用法和原生ImageView大相徑庭,但在原生中需要借助Glide或者其他框架才能很方便的加載圖片,在Flutter中自身就能實現(xiàn)加載。Image 組件有很多構(gòu)造函數(shù):

 Image.asset:用來加載當(dāng)前應(yīng)用資源圖片

 Image.network:用來加載網(wǎng)絡(luò)圖片

 Image.file:用來加載SD卡(File文件)圖片

 Image.memory:用來加載 byte[] 字節(jié)數(shù)組圖片

 Image:通過ImageProvider來加載圖片

1、Image.asset

加載一個本地資源圖片同 IOS 一樣,分為 1x,2x,3x ...,具體做法是在項目的根目錄下創(chuàng)建倍圖文件夾,一倍圖直接放入Images目錄下,如下圖所示:

image.png

實例:在屏幕上加載一個寬高300的本地資源圖片,代碼如下:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
          appBar: AppBar(title: Text('呆萌')),
          body: HomeContent(),
        ));
  }
}
class HomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
        child: Container(
          child:Image.asset('images/mm.jpg',
          ),
          height: 300,
          width: 300,
        )
    );
  }
}

本篇文章篇幅可能會很長,這里就不貼效果圖了,本章的實戰(zhàn)效果會在最后貼出,喜歡嘗試的同學(xué)可以復(fù)制體驗下,需要注意的是記得修改你的圖片名稱喲?。。?!

2、Image.network

Flutter中加載的無論是普通網(wǎng)絡(luò)圖片還是Gif使用都是如加載本地圖片一樣,沒有原生中那么顯得復(fù)雜:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
          appBar: AppBar(title: Text('呆萌')),
          body: HomeContent(),
        ));
  }
}
class HomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
        child: Container(
          //加載本地資源
          //child:Image.asset('images/mm.jpg'),
          //加載網(wǎng)絡(luò)圖片
          child:Image.network('https://upload.jianshu.io/users/upload_avatars/3030564/2789e9ea-9856-456f-be2a-e00ed5992c26.png?imageMogr2/auto-orient/strip|imageView2/1/w/300/h/300/format/webp'),
          height: 300,
          width: 300,
        )
    );
  }
}

3、Image.file

加載本地磁盤圖片文件,相比加載網(wǎng)絡(luò)圖片要復(fù)雜一些,首先在Android目錄下的 AndroidManifest.xml 配置讀寫權(quán)限 如下:

 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>

讀文件當(dāng)然需要IO包 ,引入 import 'dart:io';,

import 'dart:io';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());


class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
          appBar: AppBar(title: Text('呆萌')),
          body: HomeContent(),
        ));
  }
}
class HomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
        child: Container(
          //加載本地資源
          //child:Image.asset('images/mm.jpg'),
          //加載網(wǎng)絡(luò)圖片
          //child:Image.network('https://upload.jianshu.io/users/upload_avatars/3030564/2789e9ea-9856-456f-be2a-e00ed5992c26.png?imageMogr2/auto-orient/strip|imageView2/1/w/300/h/300/format/webp'),
          //加載磁盤圖片文件
          child:Image.file(File('/storage/emulated/0/Pictures/mm.jpg')),
        //  child:Image.memory(byteList),
          height: 300,
          width: 300,
        )
    );
  }
}

Duang???磁盤圖片文件加載是不是遇到問題了?熱加載起來怎么一片空白,圖片也不顯示,客官別急,先將程序卸載掉,然后重新運行安裝便可顯示了。這個問題希望后期Flutter會優(yōu)化吧,如果還是不顯示,可能就是android版本是6.0以上的問題,需要動態(tài)申請運行權(quán)限。還有需要注意的是你需要替換成你本地圖片文件的地址喲。

4、Image.memory

用來將一個 byte 數(shù)組加載成圖片,這個使用場景相對較少,后面章節(jié)中會有應(yīng)用到。這里就不做詳細介紹了:

new Image.memory(bytes)

5、ImageProvider 加載占位圖

有的時候我們需要像Android那樣使用一個占位圖或者圖片加載出錯時顯示某張?zhí)囟ǖ膱D片,這時候需要用到 FadeInImage 這個組件:

import 'dart:io';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());


class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {


    return MaterialApp(
        home: Scaffold(
          appBar: AppBar(title: Text('呆萌')),
          body: HomeContent(),
        ));
  }
}
class HomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
        child: Container(
           child: new FadeInImage.assetNetwork(
           placeholder: 'images/mm.jpg', //目標(biāo)圖片沒顯示或者加載失敗 顯示 該圖片
           image: "https://upload.jianshu.io/users/upload_avatars/3030564/2789e9ea-9856-456f-be2a-e00ed5992c26.png?imageMogr2/auto-orient/strip|imageView2/1/w/300/h/300/format/webp",
           width: 300,
           fit: BoxFit.fitWidth,
        ),
          height: 300,
          width: 300,
        )
    );
  }
}

二、Image組件中的常用屬性

名稱                              說明
                                   
alignment                         圖片的對齊方式

color和colorBlendMode             設(shè)置圖片的背景顏色,通常和 colorBlendMode 配合一起
                                  使用,這樣可以是圖片顏色和背景色混合。
                                  
fit                               fit 屬性用來控制圖片的拉伸和擠壓,這都是根據(jù)父容器來的。
                                  BoxFit.fill:全圖顯示,圖片會被拉伸,并充滿父容器。
                                  BoxFit.contain:全圖顯示,顯示原比例,可能會有空隙。
                                  BoxFit.cover:顯示可能拉伸,可能裁切,充滿(圖片要
                                  充滿整個容器,還不變形)。
                                  BoxFit.fitWidth:寬度充滿(橫向充滿),顯示可能拉伸,
                                  可能裁切。
                                  BoxFit.fitHeight :高度充滿(豎向充滿),顯示可能拉
                                  伸,可能裁切。
                                  BoxFit.scaleDown:效果和 contain 差不多,但是此屬
                                  性不允許顯示超過源圖片大小,可小不可大。
                                  
repeat                            平鋪 ImageRepeat.repeat : 橫向和縱向都進行重復(fù),直到鋪滿整
                                  個畫布。
                                  ImageRepeat.repeatX: 橫向重復(fù),縱向不重復(fù)。
                                  ImageRepeat.repeatY:縱向重復(fù),橫向不重復(fù)。
                                  
width                             寬度 一般結(jié)合 ClipOval 才能看到效果

height                            高度 一般結(jié)合 ClipOval 才能看到效果

三、ListView組件

Flutter中的列表組件一樣具有Android原生中的ListView控件或者RecyclerView的功效,在寫法上我覺的比原生使用更加簡單,下面來看ListView組件中常用的一些屬性:

名稱                        類型                       說明

scrollDirection             Axis                       Axis.horizontal 水平列表
                                                       Axis.vertical 垂直列表
                                                       
padding                     EdgeInsetsGeometry         內(nèi)邊距

resolve                     bool                       組件反向排序

children                    List<Widget>               列表元素

1、水平布局

在Flutter中水平布局相比Android 原生控件簡單太多,尤其是在Android 發(fā)布RecyclerView 控件之前,你可能要重寫一個橫向的自定義ListView控件去實現(xiàn)橫向菜單,這對于剛?cè)胧值耐瑢W(xué)來說就很難實現(xiàn)了。下面我們利用Flutter ListView組件來實現(xiàn)一個簡易的banner

實例代碼:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
          appBar: AppBar(title: Text('呆萌')),
          body: BannerView(),
        ));
  }
}

class BannerView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.all(3),
      height: 120, // 限制Container 的高度,不讓子元素listView填充整個屏幕
      child: ListView(
        padding: EdgeInsets.all(10), //10個單位的內(nèi)邊距,離屏幕四周分隔開一點

        scrollDirection: Axis.horizontal, // 指定為水平樣式

        children: <Widget>[
          Container(
            width: 120.0,
            height: 120.0,
            decoration: BoxDecoration(  //設(shè)置成圓角卡片樣式
              color: Colors.red,
              borderRadius: BorderRadius.circular(10),
            ),
          ),
          SizedBox(   // 分割線 其實就是給點里右邊的距離
            width: 10,
            height: 10,
          ),
          Container(
            width: 120.0,
            height: 120.0,
            decoration: BoxDecoration(
              color: Colors.orange,
              borderRadius: BorderRadius.circular(10),
            ),
          ),
          SizedBox(
            width: 10,
            height: 10,
          ),
          Container(
            width: 120.0,
            height: 120.0,
            decoration: BoxDecoration(
              color: Colors.blue,
              borderRadius: BorderRadius.circular(10),
            ),
          ),
          SizedBox(
            width: 10,
            height: 10,
          ),
          Container(
            width: 120.0,
            height: 120.0,
            decoration: BoxDecoration(
              color: Colors.green,
              borderRadius: BorderRadius.circular(10),
            ),
          ),
          SizedBox(
            width: 10,
            height: 10,
          ),
          Container(
            width: 120.0,
            height: 120.0,
            decoration: BoxDecoration(
              color: Colors.deepPurpleAccent,
              borderRadius: BorderRadius.circular(10),
            ),
          ),
        ],
      ),
    );
  }
}

【溫馨提示】 在Flutter 列表組件中不能使用 margin屬性的情況下,可以使用SizedBox 組件,或者Container組件都能滿足我們的需求。效果如下:

Banner.gif

2、垂直布局

延用上面橫向banner這個例子,簡單修改下,做成一個垂直的菜單欄,代碼如下:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
          appBar: AppBar(title: Text('呆萌')),
          body: BannerView(),
        ));
  }
}

class BannerView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.all(3),
      child: ListView(
        padding: EdgeInsets.all(10), //10個單位的內(nèi)邊距,離屏幕四周分隔開一點
        scrollDirection: Axis.vertical, // 指定為垂直樣式

        children: <Widget>[
          Container(

            alignment: Alignment.center,
            child: new Text(
              "Java專欄",
               textAlign: TextAlign.center,
               style: TextStyle(
                fontSize: 30.0,
                color: Colors.white,
              ),
            ),
            height: 120.0,
            decoration: BoxDecoration(  //設(shè)置成圓角卡片樣式
              color: Colors.red,
              borderRadius: BorderRadius.circular(10),
            ),
          ),
          SizedBox(   // 分割線
            width: 10,
            height: 10,
          ),
          Container(
            alignment: Alignment.center,
            child: new Text(
              "Kotlin專欄",
              textAlign: TextAlign.center,
              style: TextStyle(
                fontSize: 30.0,
                color: Colors.white,
              ),
            ),
            height: 120.0,
            decoration: BoxDecoration(  //設(shè)置成圓角卡片樣式
              color: Colors.orange,
              borderRadius: BorderRadius.circular(10),
            ),

          ),
          SizedBox(
            height: 10,
          ),
          Container(
            alignment: Alignment.center,
            child: new Text(
              "Flutter專欄",
              textAlign: TextAlign.center,
              style: TextStyle(
                fontSize: 30.0,
                color: Colors.white,
              ),
            ),
            height: 120.0,
            decoration: BoxDecoration(  //設(shè)置成圓角卡片樣式
              color: Colors.blue,
              borderRadius: BorderRadius.circular(10),
            ),
          ),
          SizedBox(
            height: 10,
          ),
          Container(
            alignment: Alignment.center,
            child: new Text(
              "Swift專欄",
              textAlign: TextAlign.center,
              style: TextStyle(
                fontSize: 30.0,
                color: Colors.white,
              ),
            ),
            height: 120.0,
            decoration: BoxDecoration(  //設(shè)置成圓角卡片樣式
              color: Colors.green,
              borderRadius: BorderRadius.circular(10),
            ),
          ),
          SizedBox(
            height: 10,
          ),
          Container(
            alignment: Alignment.center,
            child: new Text(
              "Object C",
              textAlign: TextAlign.center,
              style: TextStyle(
                fontSize: 30.0,
                color: Colors.white,
              ),
            ),
            height: 120.0,
            decoration: BoxDecoration(  //設(shè)置成圓角卡片樣式
              color: Colors.deepPurpleAccent,
              borderRadius: BorderRadius.circular(10),
            ),
          ),
        ],
      ),
    );
  }
}

呆萌效果,也是分分鐘見高逼格菜單,O(∩_∩)O

menue.gif

細心的你肯定發(fā)現(xiàn)了一個問題,上面的數(shù)據(jù)都是提前寫死在程序里的,對于常規(guī)的固定菜單自然是沒問題,但假如我們的數(shù)據(jù)是動態(tài)從服務(wù)器請求獲取的呢?

3、ListView組件之ListView.builder

在Android原生中ListView、RecyclerView是通常是繼承BaseAdapter、RecyclerView.Adapter 去實列表布局顯示的,在Flutter中也能很輕松去實現(xiàn)同樣的效果。

3.1 列表 item 之 ListTile

在講解ListView.builder的用法之前,先看下常配合ListView使用的ListTile,查看源碼有如下這么多屬性可以配置。

const ListTile({
    Key key,
    this.leading,              // item 前置圖標(biāo)
    this.title,                // item 標(biāo)題
    this.subtitle,             // item 副標(biāo)題
    this.trailing,             // item 后置圖標(biāo)
    this.isThreeLine = false,  // item 是否三行顯示
    this.dense,                // item 直觀感受是整體大小
    this.contentPadding,       // item 內(nèi)容內(nèi)邊距
    this.enabled = true,       // itme 狀態(tài)是否啟用 
    this.onTap,                // item onTap 點擊事件
    this.onLongPress,          // item onLongPress 長按事件
    this.selected = false,     // item 是否選中狀態(tài)
})

下面我們來看下適配器之 ListView.builder 的簡單實例:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
          appBar: AppBar(title: Text('呆萌')),
          body: HomeContent(),
        ));
  }
}
class HomeContent extends StatelessWidget {

  //自定義方法
  Widget _getListData(context,index){

    return new Container(

      child: Column(
        children: <Widget>[
          ListTile(
             title: Text(musicData[index]["music"]), //設(shè)置標(biāo)題文本內(nèi)容
             leading:Image.network(musicData[index]["url"]), //在文本前顯示網(wǎng)絡(luò)圖片
             subtitle:Text(musicData[index]["author"]), // 設(shè)置二級標(biāo)題
             ),
          //添加一條分割線
          Divider(
            color: Colors.grey,
            height: 1,
          )
        ],
      ),
    );

  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
         //返回數(shù)據(jù)長度
        itemCount:musicData.length,

        //將_getListData 的結(jié)果綁定到當(dāng)前Item上,注意這里沒有加(),加()表示執(zhí)行該方法
        itemBuilder:this._getListData
    );
  }
}



// 假設(shè)這是從服務(wù)器端請求下來的數(shù)據(jù)
List musicData=[

  {
    "music": '愛情轉(zhuǎn)移',
    "author": '陳奕迅',
    "url": 'http://qukufile2.qianqian.com/data2/pic/af739e0109798366b9419230be5253ce/541222074/541222074.jpg',
  },
  {
    "music": '說謊',
    "author": '林宥嘉',
    "url": 'http://qukufile2.qianqian.com/data2/pic/11fd3062a07e029325e152aac593531a/533505799/533505799.jpg',
  },
  {
    "music": '后來',
    "author": '劉若英',
    "url": 'http://qukufile2.qianqian.com/data2/pic/246708144/246708144.jpg',
  },
  {
    "music": '暖暖',
    "author": '梁靜茹',
    "url": 'http://qukufile2.qianqian.com/data2/pic/ad87603bb29cc5ac6ef5945582d56cdd/580337204/580337204.jpg',
  },
  {
    "music": '數(shù)天數(shù)',
    "author": '龔玥',
    "url": 'http://qukufile2.qianqian.com/data2/pic/246584942/246584942.jpg',
  },
  {
    "music": '大美青海',
    "author": '瓊雪卓瑪',
    "url": 'http://qukufile2.qianqian.com/data2/pic/318191ab36d37cdf1b7a8f12d7c633ed/611246006/611246006.jpg',
  },
  {
    "music": '愛在心里',
    "author": '劉思嬡',
    "url": 'http://qukufile2.qianqian.com/data2/pic/baf889dd9e0e36efdde4d8c23b1ff944/607931693/607931693.jpg',
  }

];

上面代碼中用到了 Column 組件,這個組件會在后面的章節(jié)中講到,這里看不懂沒關(guān)系,你可以理解成原生中的 LinearLayout 垂直布局,添加到該容器中的View以此往下排版。Divider組件就是用來顯示分割線的,你可以很輕松的配置分割線高度,以及顏色值,當(dāng)然你也可以不用它,或者使用ListView.separated 帶分隔符的列表甚至是Container組件來代替它的使用。

由于模擬器有點垃圾,一些分割線顯示不出來,這里使用手機截屏,效果大致如下:


紅紅的截屏.jpg

你可能會問可不可以不使用 ListView.builde 這玩意去實現(xiàn)嗎?查看源碼 發(fā)現(xiàn) ListView 的children 接收的是一個 List<Widget>。這個List<Widget>其實就是我們每個Item視圖,下面我們通過循環(huán)去組裝一個列表視圖,實現(xiàn)上面一樣的效果。

import 'package:flutter/material.dart';

void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
          appBar: AppBar(title: Text('呆萌')),
          body: HomeContent(),
        ));
  }
}
class HomeContent extends StatelessWidget {

  //通過循環(huán)添加
  List<Widget> _getData(){

    List<Widget> list=new List();

    for(var i=0;i<musicData.length;i++){
      list.add(Column(
        children: <Widget>[
          ListTile(
            title: Text(musicData[i]["music"]), //設(shè)置標(biāo)題文本內(nèi)容
            leading:Image.network(musicData[i]["url"]), //在文本前顯示網(wǎng)絡(luò)圖片
            subtitle:Text(musicData[i]["author"]), // 設(shè)置二級標(biāo)題
          ),
          //添加一條分割線
          new Container(
            color: Colors.yellow,
            height: 1,
          )
        ],
      ),
      );
    }
    return list;
 }

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: this._getData(),
    );
  }
}

// 假設(shè)這是從服務(wù)器端請求下來的數(shù)據(jù)
List musicData=[

  {
    "music": '愛情轉(zhuǎn)移1',
    "author": '陳奕迅',
    "url": 'http://qukufile2.qianqian.com/data2/pic/af739e0109798366b9419230be5253ce/541222074/541222074.jpg',
  },
  {
    "music": '說謊',
    "author": '林宥嘉',
    "url": 'http://qukufile2.qianqian.com/data2/pic/11fd3062a07e029325e152aac593531a/533505799/533505799.jpg',
  },
  {
    "music": '后來',
    "author": '劉若英',
    "url": 'http://qukufile2.qianqian.com/data2/pic/246708144/246708144.jpg',
  },
  {
    "music": '暖暖',
    "author": '梁靜茹',
    "url": 'http://qukufile2.qianqian.com/data2/pic/ad87603bb29cc5ac6ef5945582d56cdd/580337204/580337204.jpg',
  },
  {
    "music": '數(shù)天數(shù)',
    "author": '龔玥',
    "url": 'http://qukufile2.qianqian.com/data2/pic/246584942/246584942.jpg',
  },
  {
    "music": '大美青海',
    "author": '瓊雪卓瑪',
    "url": 'http://qukufile2.qianqian.com/data2/pic/318191ab36d37cdf1b7a8f12d7c633ed/611246006/611246006.jpg',
  },
  {
    "music": '愛在心里',
    "author": '劉思嬡',
    "url": 'http://qukufile2.qianqian.com/data2/pic/baf889dd9e0e36efdde4d8c23b1ff944/607931693/607931693.jpg',
  }

];

運行后你發(fā)現(xiàn)效果是和上面一模一樣的,這里就不貼效果圖了,當(dāng)然在開發(fā)中我們還是建議使用 ListView.builde 因為它在內(nèi)部做了些優(yōu)化,這就像Android 原生中 ListView控件 使用ViewHolder是一樣的,顯示條目復(fù)用,能節(jié)約很大一部分內(nèi)存和性能。

四、GridView 組件

在Flutter中網(wǎng)格布局和Android原生同名依然是采用的GridView命名,用法也是極其簡單,這和我們上面講到的ListView極為相似,下面我們先來看下它的常用屬性:

名稱                    類型                             說明

scrollDirection          Axis                            滾動方法

padding                  EdgeInsetsGeometry              內(nèi)邊距

resolve                  bool                            組件反向排序

crossAxisSpacing         double                          水平子 Widget 之間間距

mainAxisSpacing          double                          垂直子 Widget 之間間距

crossAxisCount           int                             一行的 Widget 數(shù)量

childAspectRatio         double                          子 Widget 寬高比例

children                                                   <Widget>[ ]

gridDelegate     SliverGridDelegateWithFixedCrossAxisCount             控制布局主要用在GridView.builder 里面
                 SliverGridDelegateWithMaxCrossAxisExtent
4.1 GridView 創(chuàng)建網(wǎng)格列表有多種方式,其中常用的構(gòu)造函數(shù)
1、GridView.builder 

2、GridView.count 

3、GridView.custom

4、GridView.extent

下面我們主要介紹兩種:GridView.builderGridView.count

嘿哈,看到?jīng)]?GridView.builder也同樣具有ListView.builder一樣的適配器,我們來將上面的音樂列表換成網(wǎng)格列表來顯示看看 代碼如下:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
          appBar: AppBar(title: Text('呆萌')),
          body: HomeContent(),
        ));
  }
}
class HomeContent extends StatelessWidget {

  Widget _getListData(context, index) {
    return Container(

      margin: EdgeInsets.all(10),

      width: 110,
      height: 110,
      child: Column(
        children: <Widget>[

          SizedBox(height: 10),
          Image.network(musicData[index]["url"],width: 110,height: 100,fit: BoxFit.fitWidth),

          SizedBox(height: 10),

          Text(musicData[index]["author"], textAlign: TextAlign.center,
              style: TextStyle(
                  fontSize: 20,
                  color: Colors.white
              )),
        ],
      ),

      //繪制圓角背景
      decoration: BoxDecoration(

          //圓角10個單位
          borderRadius: BorderRadius.all(Radius.circular(10)),
           //背景色
          color: Colors.deepPurple,

          //繪制邊框
          border: Border.all(
              color: Colors.deepPurple,
              width: 1.0,

          )
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return GridView.builder(

      //item總數(shù)量
      itemCount: musicData.length,

      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          //橫軸元素個數(shù)
          crossAxisCount: 2,
          //縱軸間距
          mainAxisSpacing: 10.0,
           //橫軸間距
          crossAxisSpacing: 10.0,
          //子組件寬高長度比例
          childAspectRatio: 1.0
      ),
      itemBuilder: this._getListData,
    );
  }
}

// 假設(shè)這是從服務(wù)器端請求下來的數(shù)據(jù)
List musicData=[

  {
    "music": '愛情轉(zhuǎn)移',
    "author": '陳奕迅',
    "url": 'http://qukufile2.qianqian.com/data2/pic/af739e0109798366b9419230be5253ce/541222074/541222074.jpg',
  },
  {
    "music": '說謊11',
    "author": '林宥嘉',
    "url": 'http://qukufile2.qianqian.com/data2/pic/11fd3062a07e029325e152aac593531a/533505799/533505799.jpg',
  },
  {
    "music": '后來',
    "author": '劉若英',
    "url": 'http://qukufile2.qianqian.com/data2/pic/246708144/246708144.jpg',
  },
  {
    "music": '暖暖',
    "author": '梁靜茹',
    "url": 'http://qukufile2.qianqian.com/data2/pic/ad87603bb29cc5ac6ef5945582d56cdd/580337204/580337204.jpg',
  },
  {
    "music": '數(shù)天數(shù)',
    "author": '龔玥',
    "url": 'http://qukufile2.qianqian.com/data2/pic/246584942/246584942.jpg',
  },
  {
    "music": '大美青海',
    "author": '瓊雪卓瑪',
    "url": 'http://qukufile2.qianqian.com/data2/pic/318191ab36d37cdf1b7a8f12d7c633ed/611246006/611246006.jpg',
  },
  {
    "music": '愛在心里',
    "author": '劉思嬡',
    "url": 'http://qukufile2.qianqian.com/data2/pic/baf889dd9e0e36efdde4d8c23b1ff944/607931693/607931693.jpg',
  }

];
image.png

上面通過 GridView.builder 實現(xiàn)網(wǎng)格布局,接下來通過使用 GridView.count 實現(xiàn)上面一樣的效果:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
          appBar: AppBar(title: Text('呆萌')),
          body: HomeContent(),
        ));
  }
}
class HomeContent extends StatelessWidget {


  List<Widget> _getListData() {

    var tempList = musicData.map((value){

      return Container(
        
        child:Column(
          children: <Widget>[

            SizedBox(height: 10),

            Image.network(value["url"],width: 110,height: 100,fit: BoxFit.fitWidth),

            SizedBox(height: 10),

            Text(value["author"], textAlign: TextAlign.center,
                style: TextStyle(
                    fontSize: 20,
                    color: Colors.white
                )),
          ],

        ),
        //繪制圓角背景
        decoration: BoxDecoration(

          //圓角10個單位
            borderRadius: BorderRadius.all(Radius.circular(10)),
            //背景色
            color: Colors.deepPurple,
            //繪制邊框
            border: Border.all(
              color: Colors.deepPurple,
              width: 1.0,
            )
        ),
      );
    });
    return tempList.toList();
  }

  @override
  Widget build(BuildContext context) {
    return GridView.count(

      //水平子 Widget 之間間距
      crossAxisSpacing:10.0 ,
      //垂直子 Widget 之間間距
      mainAxisSpacing: 10.0,   
      padding: EdgeInsets.all(10),
      //一行的 Widget 數(shù)量
      crossAxisCount: 2,
      //寬度和高度的比例
      // childAspectRatio:0.7, 
      
      children: this._getListData(),
    );
  }
}

// 假設(shè)這是從服務(wù)器端請求下來的數(shù)據(jù)
List musicData=[

  {
    "music": '愛情轉(zhuǎn)移',
    "author": '陳奕迅',
    "url": 'http://qukufile2.qianqian.com/data2/pic/af739e0109798366b9419230be5253ce/541222074/541222074.jpg',
  },
  {
    "music": '說謊11',
    "author": '林宥嘉',
    "url": 'http://qukufile2.qianqian.com/data2/pic/11fd3062a07e029325e152aac593531a/533505799/533505799.jpg',
  },
  {
    "music": '后來',
    "author": '劉若英',
    "url": 'http://qukufile2.qianqian.com/data2/pic/246708144/246708144.jpg',
  },
  {
    "music": '暖暖',
    "author": '梁靜茹',
    "url": 'http://qukufile2.qianqian.com/data2/pic/ad87603bb29cc5ac6ef5945582d56cdd/580337204/580337204.jpg',
  },
  {
    "music": '數(shù)天數(shù)',
    "author": '龔玥',
    "url": 'http://qukufile2.qianqian.com/data2/pic/246584942/246584942.jpg',
  },
  {
    "music": '大美青海',
    "author": '瓊雪卓瑪',
    "url": 'http://qukufile2.qianqian.com/data2/pic/318191ab36d37cdf1b7a8f12d7c633ed/611246006/611246006.jpg',
  },
  {
    "music": '愛在心里',
    "author": '劉思嬡',
    "url": 'http://qukufile2.qianqian.com/data2/pic/baf889dd9e0e36efdde4d8c23b1ff944/607931693/607931693.jpg',
  }
];

從代碼上來看兩者的使用其實都很簡單,沒什么難點。但需要注意的兩點是:
1. GridView.builder 中需要指定條目的數(shù)量,itemBuilder 接收是一個方法,至于該方法何時被調(diào)用,我們無需關(guān)心,因為這一切都在內(nèi)部幫你實現(xiàn)了,所以再寫的時候一定得注意。

2. GridView.count 雖然無需要指定item數(shù)量,因為Item的數(shù)量完全取決于 children返回的List<Widget>的長度,所以要顯示網(wǎng)格列表,需要提前組裝好 children 包裹的內(nèi)容。

本章實戰(zhàn)

通過對上面Image組件、ListView組件、GridView組件的大致了解,本章實戰(zhàn)內(nèi)容就是利用上面學(xué)到的組件實現(xiàn)一個圖文列表,點擊按鈕可以從單列表變成網(wǎng)格列表,網(wǎng)格列表又能變成單列表的效果。

import 'package:flutter/material.dart';

import 'res/TestData.dart';

void main() => runApp(MyApp());

bool isGrid = false;

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new ViewStatefulWidget(),
    );
  }
}

//自定義Widget 實現(xiàn)有狀態(tài)StatefulWidget
class ViewStatefulWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return HomeContent();
  }
}

class HomeContent extends State<ViewStatefulWidget> {
  void changeView() {
    // 通過 setState() 更新數(shù)據(jù),組件樹自動刷新
    setState(() {
      if (isGrid) {
        isGrid = false;
      } else {
        isGrid = true;
      }
    });
  }

  List<Widget> _getListData() {
    var tempList = musicData.map((value) {
      return Container(
        child: Column(
          children: <Widget>[
            SizedBox(height: 10),
            Image.network(value["url"],
                width: 110, height: 100, fit: BoxFit.fitWidth),
            SizedBox(height: 10),
            Text(value["author"],
                textAlign: TextAlign.center,
                style: TextStyle(fontSize: 20, color: Colors.white)),
          ],
        ),
        //繪制圓角背景
        decoration: BoxDecoration(
            //圓角10個單位
            borderRadius: BorderRadius.all(Radius.circular(10)),
            //背景色
            color: Colors.deepPurple,
            //繪制邊框
            border: Border.all(
              color: Colors.deepPurple,
              width: 1.0,
            )),
      );
    });
    return tempList.toList();
  }

  //通過循環(huán)添加
  List<Widget> _getData() {
    List<Widget> list = new List();

    for (var i = 0; i < musicData.length; i++) {
      list.add(
        Column(
          children: <Widget>[
            ListTile(
              title: Text(musicData[i]["music"]), //設(shè)置標(biāo)題文本內(nèi)容
              leading: Image.network(musicData[i]["url"]), //在文本前顯示網(wǎng)絡(luò)圖片
              subtitle: Text(musicData[i]["author"]), // 設(shè)置二級標(biāo)題
            ),
            //添加一條分割線
            new Container(
              color: Colors.black12,
              height: 1,
            )
          ],
        ),
      );
    }
    return list;
  }

  // 根據(jù)狀態(tài)返回不同的Widget
  Widget getWidget() {
    if (isGrid) {
      return ListView(
        children: this._getData(),
      );
    } else {
      return GridView.count(
        //水平子 Widget 之間間距
        crossAxisSpacing: 10.0,
        //垂直子 Widget 之間間距
        mainAxisSpacing: 10.0,
        padding: EdgeInsets.all(10),
        //一行的 Widget 數(shù)量
        crossAxisCount: 2,
        //寬度和高度的比例
        // childAspectRatio:0.7,

        children: this._getListData(),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return new Container(
      child: new Scaffold(
          appBar: AppBar(
            title: Text("呆萌"),
             backgroundColor: Colors.red, //appBar 背景色
             centerTitle: true, //標(biāo)題居中顯示
             actions: <Widget>[
              IconButton(

                icon: Icon(Icons.menu),
                onPressed: () { //添加按下事件
                  changeView();
                },
              )
            ],
          ),
          body: new Container(
            child: getWidget(),
          )),
    );
  }
}

這里只是演示只簡單的寫了下樣式,你也可以仔細的調(diào)整下頁面這也是知識點的鞏固嘛,最終效果:


shizhan.gif

在本章實戰(zhàn)中使用到了有狀態(tài)StatefulWidget 以及點擊事件onPressed,點擊事件很好理解,至于StatefulWidget 現(xiàn)在看不懂沒關(guān)系,先嘗試用嘛,這會在后面的章節(jié)中詳細講到。

實戰(zhàn)源碼地址: https://github.com/zhengzaihong/flutter_learn

好了本章節(jié)就此結(jié)束,又到了說再見的時候了,如果你喜歡請留下你的小紅星,你們的支持才是創(chuàng)作的動力。謝謝大家觀看,下章再會 O(∩_∩)O

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

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容