Flutter:手把手教你使用滾動(dòng)型列表組件GirdView

前言

主要講解Flutter常用的滾動(dòng)型組件GridView組件绊茧,希望你們會(huì)喜歡刀荒。

滾動(dòng)型組件的用法十分相似明肮,可參考學(xué)習(xí)ListView組件,因?yàn)閮烧叨祭^承自BoxScrollView

>閱讀本文前可閱讀文章:


目錄


1. 屬性介紹

GridView({
  Key key,
  Axis scrollDirection = Axis.vertical, // 列表的滾動(dòng)方向簸州,可選值:Axis的horizontal鉴竭、vertical
  bool reverse = false,
  ScrollController controller, // 控制器,與列表滾動(dòng)相關(guān)岸浑,比如監(jiān)聽(tīng)列表的滾動(dòng)事件
  ScrollPhysics physics, // 列表滾動(dòng)至邊緣后繼續(xù)拖動(dòng)的物理效果搏存,Android與iOS效果不同:Android = 波紋狀(ClampingScrollPhysics),而iOS = 回彈的彈性效果(BouncingScrollPhysics)矢洲。若想不同的平臺(tái)上呈現(xiàn)各自的效果可使用AlwaysScrollableScrollPhysics璧眠,它會(huì)根據(jù)不同平臺(tái)自動(dòng)選用各自的物理效果。若想禁用在邊緣的拖動(dòng)效果读虏,可使用NeverScrollableScrollPhysics
  bool shrinkWrap = false, // 決定列表的長(zhǎng)度是否僅包裹其內(nèi)容的長(zhǎng)度责静。當(dāng)ListView嵌在一個(gè)無(wú)限長(zhǎng)的容器組件中時(shí),shrinkWrap必須為true盖桥,否則Flutter會(huì)給出警告灾螃;
  EdgeInsetsGeometry padding, // 列表內(nèi)邊距
  double cacheExtent,// 預(yù)渲染區(qū)域長(zhǎng)度,ListView會(huì)在其可視區(qū)域的兩邊留一個(gè)cacheExtent長(zhǎng)度的區(qū)域作為預(yù)渲染區(qū)域(對(duì)于ListView.build或ListView.separated構(gòu)造函數(shù)創(chuàng)建的列表揩徊,不在可視區(qū)域和預(yù)渲染區(qū)域內(nèi)的子元素不會(huì)被創(chuàng)建或會(huì)被銷毀)腰鬼;
  List<Widget> children = const <Widget>[], // 容納子元素的組件數(shù)組
  @required this.gridDelegate, // 控制排列子元素的一個(gè)委托
})

下面主要講解屬性 gridDelegate:類型 = SliverGridDelegate,是一個(gè)抽象類 & 有2個(gè)實(shí)現(xiàn)類:

  • 類型1:SliverGridDelegateWithFixedCrossAxisCount = 用于固定列數(shù)場(chǎng)景
  • 類型2:SliverGridDelegateWithMaxCrossAxisExtent = 用于子元素有最大寬度限制場(chǎng)景

下面將詳細(xì)介紹

// 類型1:SliverGridDelegateWithFixedCrossAxisCount
 // 應(yīng)用場(chǎng)景:布局中每一行的列數(shù) = 固定
 // 常用屬性(構(gòu)造函數(shù))
  SliverGridDelegateWithFixedCrossAxisCount({
    @required this.crossAxisCount, // 列數(shù)塑荒,即一行有幾個(gè)子元素熄赡;
    this.mainAxisSpacing = 0.0, // 主軸方向上的空隙間距;
    this.crossAxisSpacing = 0.0, // 次軸方向上的空隙間距齿税;
    this.childAspectRatio = 1.0, // 子元素的寬高比例彼硫。
  })

// 類型2:SliverGridDelegateWithMaxCrossAxisExtent
  // 應(yīng)用場(chǎng)景:子元素有最大寬度限制
  // 常用屬性(構(gòu)造函數(shù))
  SliverGridDelegateWithMaxCrossAxisExtent({
    this.mainAxisSpacing = 0.0, // 主軸方向上的空隙間距;
    this.crossAxisSpacing = 0.0, // 次軸方向上的空隙間距偎窘;
    this.childAspectRatio = 1.0, // 子元素的寬高比例
    @required this.maxCrossAxisExtent, // 子元素的最大寬度可能是多少乌助,然后計(jì)算得到合適的列寬
  // 示例:假如手機(jī)屏寬375溜在,crossAxisSpacing值為0陌知,
  // maxCrossAxisExtent值 = 125時(shí),網(wǎng)格列數(shù) = 3掖肋。因?yàn)?25 * 3 = 375仆葡,剛好,每一列的寬度就是375/3。
  // maxCrossAxisExtent值 = 126時(shí)沿盅,網(wǎng)格列數(shù) = 3把篓。因?yàn)?26 * 3 > 375,顯示不下腰涧,每一列的寬度將是375/3韧掩。
  // maxCrossAxisExtent值為124時(shí),網(wǎng)格列數(shù) = 4窖铡。因?yàn)?24 * 3 < 375疗锐,仍有多余,每一列的寬度將是375/4费彼。
  })

2. 基礎(chǔ)使用(含示例講解)

2.1 示例1

使用:SliverGridDelegateWithFixedCrossAxisCount = 用于固定列數(shù)場(chǎng)景

  • 示例代碼
import 'package:flutter/material.dart'; // Material UI組件庫(kù)

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

// 無(wú)狀態(tài)控件顯示
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
// 主界面入口返回的組件 = MaterialApp
    return MaterialApp(
      title: 'Widget_Demo', //標(biāo)題
      theme: ThemeData(primarySwatch: Colors.blue), //主題色
      home: MyWidget(), // 返回一個(gè)Widget對(duì)象滑臊,用來(lái)定義當(dāng)前應(yīng)用打開(kāi)的時(shí)候,所顯示的界面
    );
  }
}

class MyWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new _MyWidgetState();
  }
}

class _MyWidgetState extends State<MyWidget> {

  ScrollController _controller = new ScrollController(); // 定義控制器

  @override
  void initState() {
    super.initState();
    // 設(shè)置監(jiān)聽(tīng)方法
    _controller.addListener(() {
      print('_controller Listener');
      print(_controller.offset); // 打印滾動(dòng)位置
    });
  }

  @override
  Widget build(BuildContext context) {
    return GridView(
      scrollDirection: Axis.vertical, // 排列方向
      controller: _controller, // 設(shè)置控制器
      padding: EdgeInsets.all(10), // 內(nèi)邊距
      physics: new AlwaysScrollableScrollPhysics(), // 設(shè)置到邊緣后的效果
      children: getWidgetList(), // 表格數(shù)據(jù)(僅作demo展示箍铲,代碼在下方)

      // 設(shè)置控制排列子元素的一個(gè)委托:固定列數(shù)
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 3, // 每列3個(gè)
        childAspectRatio: 0.5,//寬高比為1:2
        mainAxisSpacing: 10, // 主軸雇卷、次軸方向間隔
        crossAxisSpacing: 10,
      ),
    );
  }

  // GridView數(shù)據(jù)(僅作demo展示)
  List<String> litems = [
    "1",
    "2",
    "3",
    "4",
    "5",
    "6",
    "7",
    "8",
    "9",
    "10",
    "11",
    "12",
    "13",
    "14",
    "15"
  ];

  List<Widget> getWidgetList() {
    return litems.map((item) => getItemContainer(item)).toList();
  }

  Widget getItemContainer(String item) {
    return Container(
      alignment: Alignment.center,
      child: Text(
        item,
        style: TextStyle(color: Colors.white, fontSize: 20),
      ),
      color: Colors.blue,
    );
  }
}
  • 示意圖

2.2 示例2

使用:SliverGridDelegateWithMaxCrossAxisExtent = 用于子元素有最大寬度限制場(chǎng)景

  • 示例代碼
import 'package:flutter/material.dart'; // Material UI組件庫(kù)

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

// 無(wú)狀態(tài)控件顯示
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
// 主界面入口返回的組件 = MaterialApp
    return MaterialApp(
      title: 'Widget_Demo', //標(biāo)題
      theme: ThemeData(primarySwatch: Colors.blue), //主題色
      home: MyWidget(), // 返回一個(gè)Widget對(duì)象,用來(lái)定義當(dāng)前應(yīng)用打開(kāi)的時(shí)候颠猴,所顯示的界面
    );
  }
}

class MyWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new _MyWidgetState();
  }
}

class _MyWidgetState extends State<MyWidget> {

  ScrollController _controller = new ScrollController(); // 定義控制器

  @override
  void initState() {
    super.initState();
    // 設(shè)置監(jiān)聽(tīng)方法
    _controller.addListener(() {
      print('_controller Listener');
      print(_controller.offset); // 打印滾動(dòng)位置
    });
  }

  @override
  Widget build(BuildContext context) {
    return GridView(
      scrollDirection: Axis.vertical, // 排列方向
      controller: _controller, // 設(shè)置控制器
      padding: EdgeInsets.all(10), // 內(nèi)邊距
      physics: new AlwaysScrollableScrollPhysics(), // 設(shè)置到邊緣后的效果
      children: getWidgetList(), // 表格數(shù)據(jù)(僅作demo展示关划,代碼在下方)

      // 設(shè)置控制排列子元素的一個(gè)委托:限制子元素的最大寬度
      gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
        maxCrossAxisExtent: 200, //子控件最大寬度為200
        childAspectRatio: 0.5,//寬高比為1:2
        mainAxisSpacing: 10, // 主軸、次軸方向間隔
        crossAxisSpacing: 10,
      ),
    );
  }

  // GridView數(shù)據(jù)(僅作demo展示)
  List<String> litems = [
    "1",
    "2",
    "3",
    "4",
    "5",
    "6",
    "7",
    "8",
    "9",
    "10",
    "11",
    "12",
    "13",
    "14",
    "15"
  ];

  List<Widget> getWidgetList() {
    return litems.map((item) => getItemContainer(item)).toList();
  }

  Widget getItemContainer(String item) {
    return Container(
      alignment: Alignment.center,
      child: Text(
        item,
        style: TextStyle(color: Colors.white, fontSize: 20),
      ),
      color: Colors.blue,
    );
  }
}
  • 示意圖

3. 進(jìn)階使用

GridView組件除了上面說(shuō)的基礎(chǔ)使用方式(默認(rèn)構(gòu)造函數(shù))翘瓮,還有一些進(jìn)階使用:

  • GridView.builder
  • GridView.count
  • GridView.extent
  • GridView.custom

下面祭玉,我將詳細(xì)介紹其使用。

類型1:GridView.builder

a. 特點(diǎn)

根據(jù)子元素是否出現(xiàn)在屏幕內(nèi)而動(dòng)態(tài)創(chuàng)建銷毀春畔,減少內(nèi)存消耗脱货,更高效渲染

b. 應(yīng)用場(chǎng)景

長(zhǎng)列表、數(shù)據(jù)量不確定(如網(wǎng)絡(luò)請(qǐng)求)

c. 屬性(構(gòu)造函數(shù))

 GridView.builder({
  Key key,
  Axis scrollDirection = Axis.vertical, // 列表的滾動(dòng)方向律姨,可選值:Axis的horizontal振峻、vertical
  ScrollController controller, // 控制器,與列表滾動(dòng)相關(guān)择份,比如監(jiān)聽(tīng)列表的滾動(dòng)事件
  ScrollPhysics physics, // 列表滾動(dòng)至邊緣后繼續(xù)拖動(dòng)的物理效果扣孟,Android與iOS效果不同:Android = 波紋狀(ClampingScrollPhysics),而iOS = 回彈的彈性效果(BouncingScrollPhysics)荣赶。若想不同的平臺(tái)上呈現(xiàn)各自的效果可使用AlwaysScrollableScrollPhysics凤价,它會(huì)根據(jù)不同平臺(tái)自動(dòng)選用各自的物理效果。若想禁用在邊緣的拖動(dòng)效果拔创,可使用NeverScrollableScrollPhysics
  bool shrinkWrap = false, // 決定列表的長(zhǎng)度是否僅包裹其內(nèi)容的長(zhǎng)度利诺。當(dāng)ListView嵌在一個(gè)無(wú)限長(zhǎng)的容器組件中時(shí),shrinkWrap必須為true剩燥,否則Flutter會(huì)給出警告慢逾;
  EdgeInsetsGeometry padding, // 列表內(nèi)邊距
  double cacheExtent,// 預(yù)渲染區(qū)域長(zhǎng)度,ListView會(huì)在其可視區(qū)域的兩邊留一個(gè)cacheExtent長(zhǎng)度的區(qū)域作為預(yù)渲染區(qū)域(對(duì)于ListView.build或ListView.separated構(gòu)造函數(shù)創(chuàng)建的列表,不在可視區(qū)域和預(yù)渲染區(qū)域內(nèi)的子元素不會(huì)被創(chuàng)建或會(huì)被銷毀)侣滩;
  List<Widget> children = const <Widget>[], // 容納子元素的組件數(shù)組
  @required this.gridDelegate, // 控制排列子元素的一個(gè)委托

  // 上面屬性同默認(rèn)構(gòu)造函數(shù)口注,主要關(guān)注下面兩個(gè)特別的屬性
  @required IndexedWidgetBuilder itemBuilder,// 列表項(xiàng)構(gòu)造器,返回一個(gè)Widget
  int itemCount, // 列表的數(shù)量君珠,一般都是集合的長(zhǎng)度
  })

d. 示例代碼

import 'package:flutter/material.dart'; // Material UI組件庫(kù)

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

// 無(wú)狀態(tài)控件顯示
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
// 主界面入口返回的組件 = MaterialApp
    return MaterialApp(
      title: 'Widget_Demo', //標(biāo)題
      theme: ThemeData(primarySwatch: Colors.blue), //主題色
      home: MyWidget(), // 返回一個(gè)Widget對(duì)象寝志,用來(lái)定義當(dāng)前應(yīng)用打開(kāi)的時(shí)候,所顯示的界面
    );
  }
}

class MyWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new _MyWidgetState();
  }
}

class _MyWidgetState extends State<MyWidget> {

  ScrollController _controller = new ScrollController(); // 定義控制器

  // GridView數(shù)據(jù)(僅作demo展示)
  List<String> litems = [
    "1",
    "2",
    "3",
    "4",
    "5",
    "6",
    "7",
    "8",
    "9",
    "10",
    "11",
    "12",
    "13",
    "14",
    "15"
  ];

  @override
  void initState() {
    super.initState();
    // 設(shè)置監(jiān)聽(tīng)方法
    _controller.addListener(() {
      print('_controller Listener');
      print(_controller.offset); // 打印滾動(dòng)位置
    });
  }

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
      scrollDirection: Axis.vertical, // 排列方向
      controller: _controller, // 設(shè)置控制器
      padding: EdgeInsets.all(10), // 內(nèi)邊距
      physics: new AlwaysScrollableScrollPhysics(), // 設(shè)置到邊緣后的效果
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( // 設(shè)置控制排列子元素的一個(gè)委托:固定列數(shù)
        crossAxisCount: 3, // 每列3個(gè)
        childAspectRatio: 0.5,//寬高比為1:2
        mainAxisSpacing: 10, // 主軸策添、次軸方向間隔
        crossAxisSpacing: 10,
      ),

        // 設(shè)置元素?cái)?shù)量 & 具體元素(僅作demo展示澈段,代碼在下方)
        itemCount: litems.length,
        itemBuilder: (BuildContext ctxt, int index) {
          // 當(dāng)數(shù)據(jù)加載完畢時(shí) 追加數(shù)據(jù)
          if (index == litems.length - 1 && litems.length < 200) {
            _addIndex();
          }
          return Container(
            alignment: Alignment.center,
            child: Text(
              litems[index],
              style: TextStyle(color: Colors.white, fontSize: 20),
            ),
            color: Colors.blue,
          );
        }
    );
  }

  void _addIndex() {
    // 此處需延時(shí)加載  否則會(huì)抱The widget on which setState() or markNeedsBuild() was called was:錯(cuò)誤
    Future.delayed(Duration(milliseconds: 200)).then((e) {
      setState(() {
        litems.add("add");
      });
    });
  }
}

e. 示意圖


類型2:GridView.count

a. 特點(diǎn)

封裝了SliverGridDelegateWithFixedCrossAxisCount

b. 應(yīng)用場(chǎng)景

布局中每一行的列數(shù) = 固定

c. 屬性(構(gòu)造函數(shù))

GridView.count({
   Key key,
   Axis scrollDirection = Axis.vertical, // 列表的滾動(dòng)方向,可選值:Axis的horizontal舰攒、vertical
   @required this.crossAxisCount, // 列數(shù)败富,即一行有幾個(gè)子元素;
   this.mainAxisSpacing = 0.0, // 主軸方向上的空隙間距摩窃;
   this.crossAxisSpacing = 0.0, // 次軸方向上的空隙間距兽叮;
   this.childAspectRatio = 1.0, // 子元素的寬高比例
}

d. 示例代碼

import 'package:flutter/material.dart'; // Material UI組件庫(kù)

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

// 無(wú)狀態(tài)控件顯示
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
// 主界面入口返回的組件 = MaterialApp
    return MaterialApp(
      title: 'Widget_Demo', //標(biāo)題
      theme: ThemeData(primarySwatch: Colors.blue), //主題色
      home: MyWidget(), // 返回一個(gè)Widget對(duì)象,用來(lái)定義當(dāng)前應(yīng)用打開(kāi)的時(shí)候猾愿,所顯示的界面
    );
  }
}

class MyWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new _MyWidgetState();
  }
}

class _MyWidgetState extends State<MyWidget> {

  ScrollController _controller = new ScrollController(); // 定義控制器

  @override
  void initState() {
    super.initState();
    // 設(shè)置監(jiān)聽(tīng)方法
    _controller.addListener(() {
      print('_controller Listener');
      print(_controller.offset); // 打印滾動(dòng)位置
    });
  }

  @override
  Widget build(BuildContext context) {
    return GridView.count(
      scrollDirection: Axis.vertical, // 排列方向
      controller: _controller, // 設(shè)置控制器
      padding: EdgeInsets.all(10), // 內(nèi)邊距
      physics: new AlwaysScrollableScrollPhysics(), // 設(shè)置到邊緣后的效果
      crossAxisCount: 3, // 每列3個(gè)
      childAspectRatio: 0.5,//寬高比為1:2
      mainAxisSpacing: 10, // 主軸鹦聪、次軸方向間隔
      crossAxisSpacing: 10,
      children: getWidgetList(), // 表格數(shù)據(jù)(僅作demo展示,代碼在下方)
    );
  }

  // GridView數(shù)據(jù)(僅作demo展示)
  List<String> litems = [
    "1",
    "2",
    "3",
    "4",
    "5",
    "6",
    "7",
    "8",
    "9",
    "10",
    "11",
    "12",
    "13",
    "14",
    "15"
  ];

  List<Widget> getWidgetList() {
    return litems.map((item) => getItemContainer(item)).toList();
  }

  Widget getItemContainer(String item) {
    return Container(
      alignment: Alignment.center,
      child: Text(
        item,
        style: TextStyle(color: Colors.white, fontSize: 20),
      ),
      color: Colors.blue,
    );
  }
}

e. 示意圖


類型3:GridView.extent

a. 特點(diǎn)

封裝了SliverGridDelegateWithMaxCrossAxisExtent

b. 應(yīng)用場(chǎng)景

子元素有最大寬度限制

c. 屬性(構(gòu)造函數(shù))

 GridView.extent({
   Key key,
   Axis scrollDirection = Axis.vertical, // 列表的滾動(dòng)方向蒂秘,可選值:Axis的horizontal泽本、vertical
   this.mainAxisSpacing = 0.0, // 主軸方向上的空隙間距;
   this.crossAxisSpacing = 0.0, // 次軸方向上的空隙間距姻僧;
   this.childAspectRatio = 1.0, // 子元素的寬高比例
   @required this.maxCrossAxisExtent, // 子元素的最大寬度可能是多少规丽,然后計(jì)算得到合適的列寬
  // 示例:假如手機(jī)屏寬375,crossAxisSpacing值為0撇贺,
  // maxCrossAxisExtent值 = 125時(shí)赌莺,網(wǎng)格列數(shù) = 3。因?yàn)?25 * 3 = 375松嘶,剛好艘狭,每一列的寬度就是375/3。
  // maxCrossAxisExtent值 = 126時(shí)翠订,網(wǎng)格列數(shù) = 3巢音。因?yàn)?26 * 3 > 375,顯示不下尽超,每一列的寬度將是375/3官撼。
  // maxCrossAxisExtent值為124時(shí),網(wǎng)格列數(shù) = 4橙弱。因?yàn)?24 * 3 < 375歧寺,仍有多余燥狰,每一列的寬度將是375/4棘脐。
  })

d. 示例代碼

import 'package:flutter/material.dart'; // Material UI組件庫(kù)

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

// 無(wú)狀態(tài)控件顯示
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
// 主界面入口返回的組件 = MaterialApp
    return MaterialApp(
      title: 'Widget_Demo', //標(biāo)題
      theme: ThemeData(primarySwatch: Colors.blue), //主題色
      home: MyWidget(), // 返回一個(gè)Widget對(duì)象斜筐,用來(lái)定義當(dāng)前應(yīng)用打開(kāi)的時(shí)候,所顯示的界面
    );
  }
}

class MyWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new _MyWidgetState();
  }
}

class _MyWidgetState extends State<MyWidget> {

  ScrollController _controller = new ScrollController(); // 定義控制器

  @override
  void initState() {
    super.initState();
    // 設(shè)置監(jiān)聽(tīng)方法
    _controller.addListener(() {
      print('_controller Listener');
      print(_controller.offset); // 打印滾動(dòng)位置
    });
  }

  @override
  Widget build(BuildContext context) {
    return GridView.extent(
      scrollDirection: Axis.vertical, // 排列方向
      controller: _controller, // 設(shè)置控制器
      padding: EdgeInsets.all(10), // 內(nèi)邊距
      physics: new AlwaysScrollableScrollPhysics(), // 設(shè)置到邊緣后的效果
      maxCrossAxisExtent: 200, // 子元素寬度最大是200
      childAspectRatio: 0.5,//寬高比為1:2
      mainAxisSpacing: 10, // 主軸蛀缝、次軸方向間隔
      crossAxisSpacing: 10,
      children: getWidgetList(), // 表格數(shù)據(jù)(僅作demo展示顷链,代碼在下方)
    );
  }

  // GridView數(shù)據(jù)(僅作demo展示)
  List<String> litems = [
    "1",
    "2",
    "3",
    "4",
    "5",
    "6",
    "7",
    "8",
    "9",
    "10",
    "11",
    "12",
    "13",
    "14",
    "15"
  ];

  List<Widget> getWidgetList() {
    return litems.map((item) => getItemContainer(item)).toList();
  }

  Widget getItemContainer(String item) {
    return Container(
      alignment: Alignment.center,
      child: Text(
        item,
        style: TextStyle(color: Colors.white, fontSize: 20),
      ),
      color: Colors.blue,
    );
  }
}

e. 示意圖

類型4:GridView.custom

a. 特點(diǎn)

可自定義SliverGridDelegate 和 SliverChildBuilderDelegate

b. 應(yīng)用場(chǎng)景

更高程度的自定義監(jiān)聽(tīng) & 實(shí)現(xiàn)效果

c. 屬性(構(gòu)造函數(shù))

 GridView.custom({
   Key key,
   Axis scrollDirection = Axis.vertical, // 列表的滾動(dòng)方向,可選值:Axis的horizontal屈梁、vertical
   this.mainAxisSpacing = 0.0, // 主軸方向上的空隙間距嗤练;
   this.crossAxisSpacing = 0.0, // 次軸方向上的空隙間距;
   this.childAspectRatio = 1.0, // 子元素的寬高比例
   @required SliverGridDelegate gridDelegate, // 可傳入自定義的SliverGridDelegate
  @required SliverChildDelegate childrenDelegate,  // 可傳入自定義的SliverChildDelegate childrenDelegate
  })

d. 示例代碼

/**
 *  導(dǎo)入庫(kù)
 **/
import 'package:flutter/material.dart'; // Material UI組件庫(kù)

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

// 無(wú)狀態(tài)控件顯示
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
// 主界面入口返回的組件 = MaterialApp
    return MaterialApp(
      title: 'Widget_Demo', //標(biāo)題
      theme: ThemeData(primarySwatch: Colors.blue), //主題色
      home: MyWidget(), // 返回一個(gè)Widget對(duì)象在讶,用來(lái)定義當(dāng)前應(yīng)用打開(kāi)的時(shí)候煞抬,所顯示的界面
    );
  }
}

class MyWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new _MyWidgetState();
  }
}

class _MyWidgetState extends State<MyWidget> {

  ScrollController _controller = new ScrollController(); // 定義控制器

  @override
  void initState() {
    super.initState();
    // 設(shè)置監(jiān)聽(tīng)方法
    _controller.addListener(() {
      print('_controller Listener');
      print(_controller.offset); // 打印滾動(dòng)位置
    });
  }

  @override
  Widget build(BuildContext context) {
    return GridView.custom(
      scrollDirection: Axis.vertical, // 排列方向
      controller: _controller, // 設(shè)置控制器
      padding: EdgeInsets.all(10), // 內(nèi)邊距
      physics: new AlwaysScrollableScrollPhysics(), // 設(shè)置到邊緣后的效果

        gridDelegate:SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 3,// 列表數(shù)量是3
          childAspectRatio: 0.5,//寬高比為1:2
          mainAxisSpacing: 10, // 主軸、次軸方向間隔
          crossAxisSpacing: 10,
        ),

        childrenDelegate: MyGridChildrenDelegate( // 更高程度的自定義
              (BuildContext context, int i) {
                return Container(
                  alignment: Alignment.center,
                  child: Text(
                    litems[i],
                    style: TextStyle(color: Colors.white, fontSize: 20),
                  ),
                  color: Colors.blue,
                );
          },
          childCount: litems.length,
        ),


//      children: getWidgetList(), // 表格數(shù)據(jù)(僅作demo展示构哺,代碼在下方)
    );
  }

  // GridView數(shù)據(jù)(僅作demo展示)
  List<String> litems = [
    "1",
    "2",
    "3",
    "4",
    "5",
    "6",
    "7",
    "8",
    "9",
    "10",
    "11",
    "12",
    "13",
    "14",
    "15"
  ];

  List<Widget> getWidgetList() {
    return litems.map((item) => getItemContainer(item)).toList();
  }

  Widget getItemContainer(String item) {
    return Container(
      alignment: Alignment.center,
      child: Text(
        item,
        style: TextStyle(color: Colors.white, fontSize: 20),
      ),
      color: Colors.blue,
    );
  }
}

/**
 * 自定義有SliverChildBuilderDelegate
 * 可實(shí)現(xiàn)對(duì)列表的監(jiān)聽(tīng)
 */
class MyGridChildrenDelegate extends SliverChildBuilderDelegate {
  MyGridChildrenDelegate(
      Widget Function(BuildContext, int) builder, {
        int childCount,
        bool addAutomaticKeepAlive = true,
        bool addRepaintBoundaries = true,
      }) : super(builder,
      childCount: childCount,
      addAutomaticKeepAlives: addAutomaticKeepAlive,
      addRepaintBoundaries: addRepaintBoundaries);

  //監(jiān)聽(tīng)列表
  // 在可見(jiàn)的列表中 顯示的第一個(gè)位置和最后一個(gè)位置
  @override
  void didFinishLayout(int firstIndex, int lastIndex) {
    print('firstIndex: $firstIndex, lastIndex: $lastIndex');
  }

  // 判斷添加進(jìn)來(lái)的實(shí)例與之前的實(shí)例是否相同
  // 相同返回true革答;反之false
  // 可不重寫,默認(rèn)是true
  @override
  bool shouldRebuild(SliverChildBuilderDelegate oldDelegate) {
    // TODO: implement shouldRebuild
    print("oldDelegate$oldDelegate");
    return super.shouldRebuild(oldDelegate);
  }
}

e. 示意圖

示意圖

至此曙强,關(guān)于GridView的使用講解完畢残拐。


4. 總結(jié)

本文全面介紹了Flutter GridView組件的使用,總結(jié)如下:

接下來(lái)推出的文章碟嘴,我將繼續(xù)講解 Flutter 的相關(guān)知識(shí)溪食,包括使用語(yǔ)法、實(shí)戰(zhàn)等娜扇,感興趣的讀者可以繼續(xù)關(guān)注我的博客哦:Carson_Ho的Android博客


請(qǐng)點(diǎn)贊错沃!因?yàn)槟銈兊馁澩?鼓勵(lì)是我寫作的最大動(dòng)力!

相關(guān)文章閱讀
Android開(kāi)發(fā):最全面雀瓢、最易懂的Android屏幕適配解決方案
Android開(kāi)發(fā):史上最全的Android消息推送解決方案
Android開(kāi)發(fā):最全面维哈、最易懂的Webview詳解
Android開(kāi)發(fā):JSON簡(jiǎn)介及最全面解析方法!
Android四大組件:Service服務(wù)史上最全面解析
Android四大組件:BroadcastReceiver史上最全面解析


歡迎關(guān)注Carson_Ho的簡(jiǎn)書灌砖!

不定期分享關(guān)于安卓開(kāi)發(fā)的干貨,追求短、平回窘、快,但卻不缺深度摊灭。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末祭示,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子脱吱,更是在濱河造成了極大的恐慌智政,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件箱蝠,死亡現(xiàn)場(chǎng)離奇詭異续捂,居然都是意外死亡垦垂,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門牙瓢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)劫拗,“玉大人,你說(shuō)我怎么就攤上這事矾克∫晨叮” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵胁附,是天一觀的道長(zhǎng)酒繁。 經(jīng)常有香客問(wèn)我,道長(zhǎng)控妻,這世上最難降的妖魔是什么州袒? 我笑而不...
    開(kāi)封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮弓候,結(jié)果婚禮上郎哭,老公的妹妹穿的比我還像新娘。我一直安慰自己弓叛,他們只是感情好彰居,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著撰筷,像睡著了一般陈惰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上毕籽,一...
    開(kāi)封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天抬闯,我揣著相機(jī)與錄音,去河邊找鬼关筒。 笑死溶握,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蒸播。 我是一名探鬼主播睡榆,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼袍榆!你這毒婦竟也來(lái)了胀屿?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤包雀,失蹤者是張志新(化名)和其女友劉穎宿崭,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體才写,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡葡兑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年奖蔓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片讹堤。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吆鹤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蜕劝,到底是詐尸還是另有隱情檀头,我是刑警寧澤轰异,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布岖沛,位于F島的核電站,受9級(jí)特大地震影響搭独,放射性物質(zhì)發(fā)生泄漏婴削。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一牙肝、第九天 我趴在偏房一處隱蔽的房頂上張望唉俗。 院中可真熱鬧,春花似錦配椭、人聲如沸虫溜。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)衡楞。三九已至,卻和暖如春敦姻,著一層夾襖步出監(jiān)牢的瞬間瘾境,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工镰惦, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留迷守,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓旺入,卻偏偏與公主長(zhǎng)得像兑凿,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子茵瘾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354