Flutter跨平臺(tái)移動(dòng)端開(kāi)發(fā)丨SingleChildScrollView、ListView谭梗、GridView忘晤、CustomScrollView、ScrollController

目錄

  1. SingleChildScrollView(可滑動(dòng) View)
  2. ListView(列表 View)
  3. GridView(網(wǎng)格 View)
  4. CustomScrollView(自定義滑動(dòng) View)
  5. ScrollController(控制器)

SingleChildScrollView(可滑動(dòng) View)

SingleChildScrollView 類似 Android 中的 scrollview 默辨,且同樣的只可包含有一個(gè)子元素

  const SingleChildScrollView({
    Key key,
    this.scrollDirection = Axis.vertical,
    this.reverse = false,
    this.padding,
    bool primary,
    this.physics,
    this.controller,
    this.child,
    this.dragStartBehavior = DragStartBehavior.down,
  }) : assert(scrollDirection != null),
       assert(dragStartBehavior != null),
       assert(!(controller != null && primary == true),
          'Primary ScrollViews obtain their ScrollController via inheritance from a PrimaryScrollController widget. '
          'You cannot both set primary to true and pass an explicit controller.'
       ),
       primary = primary ?? controller == null && identical(scrollDirection, Axis.vertical),
       super(key: key);
  • key:當(dāng)前元素的唯一標(biāo)識(shí)符(類似于 Android 中的 id)
  • scrollDirection:滾動(dòng)方向德频,默認(rèn)是垂直
  • reverse:是否按照閱讀方向相反的方向滑動(dòng)。
  • padding:填充距離
  • primary:是否使用 widget 樹(shù)中默認(rèn)的 PrimaryScrollController 缩幸。當(dāng)滑動(dòng)方向?yàn)榇怪狈较颍╯crollDirection值為Axis.vertical)并且controller沒(méi)有指定時(shí)壹置,primary默認(rèn)為true
  • physics:此屬性接受一個(gè)ScrollPhysics對(duì)象竞思,它決定可滾動(dòng)Widget如何響應(yīng)用戶操作,比如用戶滑動(dòng)完抬起手指后钞护,繼續(xù)執(zhí)行動(dòng)畫(huà)盖喷;或者滑動(dòng)到邊界時(shí),如何顯示难咕。默認(rèn)情況下课梳,F(xiàn)lutter會(huì)根據(jù)具體平臺(tái)分別使用不同的ScrollPhysics對(duì)象,應(yīng)用不同的顯示效果余佃,如當(dāng)滑動(dòng)到邊界時(shí)暮刃,繼續(xù)拖動(dòng)的話,在iOS上會(huì)出現(xiàn)彈性效果爆土,而在Android上會(huì)出現(xiàn)微光效果椭懊。如果你想在所有平臺(tái)下使用同一種效果,可以顯式指定步势,F(xiàn)lutter SDK中包含了兩個(gè)ScrollPhysics的子類可以直接使用:
    ClampingScrollPhysics→Android下微光效果 / BouncingScrollPhysics→iOS下彈性效果
  • controller:此屬性接受一個(gè)ScrollController對(duì)象氧猬。ScrollController的主要作用是控制滾動(dòng)位置和監(jiān)聽(tīng)滾動(dòng)事件
  • child:子元素
import 'package:flutter/material.dart';

/**
 * @des Scroll Widget
 * @author liyongli 20190506
 * */
class SingleChildScrollViewWidget extends StatefulWidget{

  @override
  State<StatefulWidget> createState() => new _StackState();

}

/**
 * @des Scroll Widget State
 * @author liyongli 20190506
 * */
class _StackState extends State<SingleChildScrollViewWidget>{

  String numberStr = "12345678909876543210123456789";

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(

        appBar: new AppBar(
          title: new Text("Scroll Widget"),
        ),

        body: Scrollbar(
          child: SingleChildScrollView(
            scrollDirection: Axis.horizontal,
            child: Center(
              child: Row(
                children: numberStr.split("").map((c) => Text(c, textScaleFactor: 2.0,)).toList(),
              ),
            ),
          ),
        )
      ),
    );
  }
}
import 'package:flutter/material.dart';

/**
 * @des Scroll Widget
 * @author liyongli 20190506
 * */
class SingleChildScrollViewWidget extends StatefulWidget{

  @override
  State<StatefulWidget> createState() => new _StackState();

}

/**
 * @des Scroll Widget State
 * @author liyongli 20190506
 * */
class _StackState extends State<SingleChildScrollViewWidget>{

  String numberStr = "1234567890987654321";

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(

        appBar: new AppBar(
          title: new Text("Scroll Widget"),
        ),

        body: Scrollbar(
          child: SingleChildScrollView(
            child: Center(
              child: Column(
                children: numberStr.split("").map((c) => Text(c, textScaleFactor: 2.0,)).toList(),
              ),
            ),
          ),
        )
      ),
    );
  }
}

ListView(列表 View)

ListView 可以構(gòu)建一個(gè)列表視圖

  ListView({
    Key key,
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
    ScrollController controller,
    bool primary,
    ScrollPhysics physics,
    bool shrinkWrap = false,
    EdgeInsetsGeometry padding,
    this.itemExtent,
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
    bool addSemanticIndexes = true,
    double cacheExtent,
    List<Widget> children = const <Widget>[],
    int semanticChildCount,
    DragStartBehavior dragStartBehavior = DragStartBehavior.down,
  }) : childrenDelegate = SliverChildListDelegate(
         children,
         addAutomaticKeepAlives: addAutomaticKeepAlives,
         addRepaintBoundaries: addRepaintBoundaries,
         addSemanticIndexes: addSemanticIndexes,
       ),
       super(
         key: key,
         scrollDirection: scrollDirection,
         reverse: reverse,
         controller: controller,
         primary: primary,
         physics: physics,
         shrinkWrap: shrinkWrap,
         padding: padding,
         cacheExtent: cacheExtent,
         semanticChildCount: semanticChildCount ?? children.length,
         dragStartBehavior: dragStartBehavior,
       );
  • key:當(dāng)前元素的唯一標(biāo)識(shí)符(類似于 Android 中的 id)
  • scrollDirection:滾動(dòng)方向,默認(rèn)是垂直
  • reverse:是否按照閱讀方向相反的方向滑動(dòng)坏瘩。
  • controller:控制器對(duì)象盅抚,主要作用是控制滾動(dòng)位置和監(jiān)聽(tīng)滾動(dòng)事件
  • primary:是否使用 widget 樹(shù)中默認(rèn)的 PrimaryScrollController 。當(dāng)滑動(dòng)方向?yàn)榇怪狈较颍╯crollDirection值為Axis.vertical)并且controller沒(méi)有指定時(shí)倔矾,primary默認(rèn)為true
  • physics:此屬性接受一個(gè)ScrollPhysics對(duì)象妄均,它決定可滾動(dòng)Widget如何響應(yīng)用戶操作,比如用戶滑動(dòng)完抬起手指后哪自,繼續(xù)執(zhí)行動(dòng)畫(huà)丛晦;或者滑動(dòng)到邊界時(shí),如何顯示提陶。默認(rèn)情況下,F(xiàn)lutter會(huì)根據(jù)具體平臺(tái)分別使用不同的ScrollPhysics對(duì)象匹层,應(yīng)用不同的顯示效果隙笆,如當(dāng)滑動(dòng)到邊界時(shí),繼續(xù)拖動(dòng)的話升筏,在iOS上會(huì)出現(xiàn)彈性效果撑柔,而在Android上會(huì)出現(xiàn)微光效果。如果你想在所有平臺(tái)下使用同一種效果您访,可以顯式指定铅忿,F(xiàn)lutter SDK中包含了兩個(gè)ScrollPhysics的子類可以直接使用:
    ClampingScrollPhysics→Android下微光效果 / BouncingScrollPhysics→iOS下彈性效果
  • shrinkWrap:表示是否根據(jù)子 widget 的總長(zhǎng)度設(shè)置 listview 的長(zhǎng)度,默認(rèn)為 false灵汪。
  • padding:填充距離
  • itemExtent:強(qiáng)制 listview 的 children 的長(zhǎng)度 為 itemExtent 的值檀训。指定 itemExtent 的值比讓子元素決定自身長(zhǎng)度在繪制時(shí)更高效柑潦,特別是在滾動(dòng)位置頻繁變化的狀態(tài)下,因?yàn)樵O(shè)置 itemExtent 可以讓滾動(dòng)系統(tǒng)提前知道列表的長(zhǎng)度峻凫。
  • addAutomaticKeepAlives:表示是否將列表項(xiàng)包裹在 AutomaticKeepAlive widget 中渗鬼。(在懶加載時(shí),如果設(shè)置了包裹那么在此列表項(xiàng)滑出屏幕外時(shí)不會(huì)被GC荧琼。如果此列表項(xiàng)需要自己維護(hù) KeepAlive 狀態(tài)譬胎,那么此參數(shù)需為 false)
  • addRepaintBoundaries:表示是否將列表項(xiàng)包裹在 RepaintBoundary 中。(當(dāng)選擇將列表項(xiàng)包裹在 RepaintBoundary 時(shí)命锄,在滾動(dòng)過(guò)程中可以避免重繪堰乔,如果此列表項(xiàng)需要自己維護(hù) KeepAlive 狀態(tài),那么此參數(shù)需為 false)
  • addSemanticIndexes:表示是否給子元素添加索引脐恩,默認(rèn)為 true
  • cacheExtent:設(shè)置預(yù)加載的區(qū)域镐侯,范圍在窗口可見(jiàn)范圍之前與之后。如果設(shè)置為 0.0被盈,表示關(guān)閉預(yù)加載
  • children:列表項(xiàng)集合
  • semanticChildCount:提供語(yǔ)義信息的孩子的數(shù)量
item 數(shù)量固定的 listview 示例

listview 構(gòu)造方法中的參數(shù) children 表示子列表集析孽,使用這種方式構(gòu)建列表需要我們提前準(zhǔn)備好子 widget 集合。這種方式只適合實(shí)現(xiàn)少量且數(shù)量固定的列表展示需求

import 'package:flutter/material.dart';


/**
 * @des Listview Widget
 * @author liyongli 20190506
 * */
class ListViewWidget extends StatefulWidget{

  @override
  State<StatefulWidget> createState() {
    return new _ListViewWigetState();
  }

}

/**
 * @des Listview Widget State
 * @author liyongli 20190506
 * */
class _ListViewWigetState extends State<ListViewWidget>{

  @override
  Widget build(BuildContext context) {
    return Scaffold(

        appBar: new AppBar(
          title: new Text("Scroll Widget"),
        ),

        body: ListView(
          shrinkWrap: true,
          padding: const EdgeInsets.all(20.0),
          children: <Widget>[
            const Text("1111111111111111111111111111111111", style: TextStyle(color: Colors.blue)),
            const Text("2222222222222222222222222222222222", style: TextStyle(color: Colors.blue)),
            const Text("3333333333333333333333333333333333", style: TextStyle(color: Colors.blue)),
            const Text("4444444444444444444444444444444444", style: TextStyle(color: Colors.blue)),
            const Text("5555555555555555555555555555555555", style: TextStyle(color: Colors.blue)),
            const Text("6666666666666666666666666666666666", style: TextStyle(color: Colors.blue)),
            const Text("7777777777777777777777777777777777", style: TextStyle(color: Colors.blue)),
            const Text("8888888888888888888888888888888888", style: TextStyle(color: Colors.blue)),
          ],
        ),
    );
  }

}
ListView.builder

當(dāng) listview 的列表項(xiàng)較多或數(shù)量未知時(shí)只怎,就需要使用 ListView.builder 來(lái)構(gòu)建列表了

import 'package:flutter/material.dart';


/**
 * @des Listview.builder Widget
 * @author liyongli 20190506
 * */
class ListViewBuilderWidget extends StatefulWidget{

  @override
  State<StatefulWidget> createState() {
    return new _ListViewBuilderWidget ();
  }

}

/**
 * @des Listview.builder Widget State
 * @author liyongli 20190506
 * */
class _ListViewBuilderWidget  extends State<ListViewBuilderWidget>{

  @override
  Widget build(BuildContext context) {
    return Scaffold(

        appBar: new AppBar(
          title: new Text("ListviewBuilder Widget"),
        ),

        body: ListView.builder(
            itemCount: 100,
            itemExtent: 50.0, //強(qiáng)制高度為50.0
            itemBuilder: (BuildContext context, int index) {
              return ListTile(title: Text(" $index - "));
            }
        ),
    );
  }

}
ListView.separated

當(dāng) listview 的 item 間需要分割線時(shí)袜瞬,我們就需要用到 ListView.separated

import 'package:flutter/material.dart';


/**
 * @des Listview.builder Widget
 * @author liyongli 20190506
 * */
class ListViewBuilderWidget extends StatefulWidget{

  @override
  State<StatefulWidget> createState() {
    return new _ListViewBuilderWidget ();
  }

}

/**
 * @des Listview.builder Widget State
 * @author liyongli 20190506
 * */
class _ListViewBuilderWidget  extends State<ListViewBuilderWidget>{

  @override
  Widget build(BuildContext context) {
    return Scaffold(

        appBar: new AppBar(
          title: new Text("ListviewBuilder Widget"),
        ),

        body: ListView.separated(
            itemCount: 100,
            itemBuilder: (BuildContext context, int index) {
              return ListTile(title: Text(" $index - ", style: TextStyle(color: Colors.blue),));
            },

            separatorBuilder: (BuildContext context, int index) {
              return Divider(color: Colors.blue, height: 10,);
            }
        ),
    );
  }

}
ListView 分頁(yè)加載

工程 yaml 文件中要添加 english_words 的依賴

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2
  english_words: ^3.1.0
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';


/**
 * @des Listview  Widget
 * @author liyongli 20190507
 * */
class InfiniteListView extends StatefulWidget {
  @override
  _InfiniteListViewState createState() => new _InfiniteListViewState();
}

/**
 * @des Listview  Widget State
 * @author liyongli 20190507
 * */
class _InfiniteListViewState extends State<InfiniteListView> {
  static const loadingTag = "##loading##"; //表尾標(biāo)記
  var _words = <String>[loadingTag];

  @override
  void initState() {
    _retrieveData();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("listview 分頁(yè)加載"),
        ),

        body: ListView.separated(
          itemCount: _words.length,
          itemBuilder: (context, index) {
            //如果到了表尾
            if (_words[index] == loadingTag) {
              //不足100條,繼續(xù)獲取數(shù)據(jù)
              if (_words.length - 1 < 100) {
                //獲取數(shù)據(jù)
                _retrieveData();
                //加載時(shí)顯示loading
                return Container(
                  padding: const EdgeInsets.all(16.0),
                  alignment: Alignment.center,
                  child: SizedBox(
                      width: 24.0,
                      height: 24.0,
                      child: CircularProgressIndicator(strokeWidth: 2.0)
                  ),
                );
              } else {
                //已經(jīng)加載了100條數(shù)據(jù)身堡,不再獲取數(shù)據(jù)邓尤。
                return Container(
                    alignment: Alignment.center,
                    padding: EdgeInsets.all(16.0),
                    child: Text("沒(méi)有更多了", style: TextStyle(color: Colors.grey),)
                );
              }
            }
            //顯示單詞列表項(xiàng)
            return ListTile(title: Text(_words[index], style: TextStyle(color: Colors.blue),));
          },
          separatorBuilder: (context, index) => Divider(height: 1, color: Colors.blue,),
        ),
      ),

    );
  }

  void _retrieveData() {
    Future.delayed(Duration(seconds: 2)).then((e) {
      _words.insertAll(_words.length - 1,
          //每次生成20個(gè)單詞
          generateWordPairs().take(20).map((e) => e.asPascalCase).toList()
      );
      setState(() {
        //重新構(gòu)建列表
      });
    });
  }

}

GridView(網(wǎng)格 View)

GridView 可以構(gòu)建一個(gè)網(wǎng)格列表視圖

  GridView.builder({
    Key key,
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
    ScrollController controller,
    bool primary,
    ScrollPhysics physics,
    bool shrinkWrap = false,
    EdgeInsetsGeometry padding,
    @required this.gridDelegate,
    @required IndexedWidgetBuilder itemBuilder,
    int itemCount,
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
    bool addSemanticIndexes = true,
    double cacheExtent,
    int semanticChildCount,
  }) : assert(gridDelegate != null),
       childrenDelegate = SliverChildBuilderDelegate(
         itemBuilder,
         childCount: itemCount,
         addAutomaticKeepAlives: addAutomaticKeepAlives,
         addRepaintBoundaries: addRepaintBoundaries,
         addSemanticIndexes: addSemanticIndexes,
       ),
       super(
         key: key,
         scrollDirection: scrollDirection,
         reverse: reverse,
         controller: controller,
         primary: primary,
         physics: physics,
         shrinkWrap: shrinkWrap,
         padding: padding,
         cacheExtent: cacheExtent,
         semanticChildCount: semanticChildCount ?? itemCount,
       );
  • key:當(dāng)前元素的唯一標(biāo)識(shí)符(類似于 Android 中的 id)
  • scrollDirection:滾動(dòng)方向,默認(rèn)是垂直
  • reverse:是否按照閱讀方向相反的方向滑動(dòng)贴谎。
  • controller:控制器對(duì)象汞扎,主要作用是控制滾動(dòng)位置和監(jiān)聽(tīng)滾動(dòng)事件
  • primary:是否使用 widget 樹(shù)中默認(rèn)的 PrimaryScrollController 。當(dāng)滑動(dòng)方向?yàn)榇怪狈较颍╯crollDirection值為Axis.vertical)并且controller沒(méi)有指定時(shí)擅这,primary默認(rèn)為true
  • physics:此屬性接受一個(gè)ScrollPhysics對(duì)象澈魄,它決定可滾動(dòng)Widget如何響應(yīng)用戶操作,比如用戶滑動(dòng)完抬起手指后仲翎,繼續(xù)執(zhí)行動(dòng)畫(huà)痹扇;或者滑動(dòng)到邊界時(shí),如何顯示溯香。默認(rèn)情況下鲫构,F(xiàn)lutter會(huì)根據(jù)具體平臺(tái)分別使用不同的ScrollPhysics對(duì)象,應(yīng)用不同的顯示效果玫坛,如當(dāng)滑動(dòng)到邊界時(shí)结笨,繼續(xù)拖動(dòng)的話,在iOS上會(huì)出現(xiàn)彈性效果,而在Android上會(huì)出現(xiàn)微光效果炕吸。如果你想在所有平臺(tái)下使用同一種效果伐憾,可以顯式指定,F(xiàn)lutter SDK中包含了兩個(gè)ScrollPhysics的子類可以直接使用:
    ClampingScrollPhysics→Android下微光效果 / BouncingScrollPhysics→iOS下彈性效果
  • shrinkWrap:表示是否根據(jù)子 widget 的總長(zhǎng)度設(shè)置 listview 的長(zhǎng)度算途,默認(rèn)為 false塞耕。
  • padding:填充距離
  • itemCount:子元素?cái)?shù)量
  • addAutomaticKeepAlives:表示是否將列表項(xiàng)包裹在 AutomaticKeepAlive widget 中。(在懶加載時(shí)嘴瓤,如果設(shè)置了包裹那么在此列表項(xiàng)滑出屏幕外時(shí)不會(huì)被GC扫外。如果此列表項(xiàng)需要自己維護(hù) KeepAlive 狀態(tài),那么此參數(shù)需為 false)
  • addRepaintBoundaries:表示是否將列表項(xiàng)包裹在 RepaintBoundary 中廓脆。(當(dāng)選擇將列表項(xiàng)包裹在 RepaintBoundary 時(shí)筛谚,在滾動(dòng)過(guò)程中可以避免重繪,如果此列表項(xiàng)需要自己維護(hù) KeepAlive 狀態(tài)停忿,那么此參數(shù)需為 false)
  • addSemanticIndexes:表示是否給子元素添加索引驾讲,默認(rèn)為 true
  • cacheExtent:設(shè)置預(yù)加載的區(qū)域,范圍在窗口可見(jiàn)范圍之前與之后席赂。如果設(shè)置為 0.0吮铭,表示關(guān)閉預(yù)加載
  • semanticChildCount:提供語(yǔ)義信息的孩子的數(shù)量
GridView 固定列數(shù)
import 'package:flutter/material.dart';

/**
 * @des GridView 固定列數(shù)
 * @author liyongli 20190508
 * */
class GridViewTest extends StatefulWidget{

  @override
  State<StatefulWidget> createState() => new _GridViewState();

}

/**
 * @des GridView Widget State
 * @author liyongli 20190508
 * */
class _GridViewState extends State<GridViewTest>{

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(

        appBar: new AppBar(
          title: new Text("GridView 固定列數(shù)"),
        ),

        body: GridView.count(
          crossAxisCount: 4,
          childAspectRatio: 0.7,
          children: <Widget>[
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
          ],
        ),
      ),
    );
  }
}
import 'package:flutter/material.dart';

/**
 * @des GridView 等分寬度
 * @author liyongli 20190508
 * */
class GridViewTest extends StatefulWidget{

  @override
  State<StatefulWidget> createState() => new _GridViewState();

}

/**
 * @des GridView Widget State
 * @author liyongli 20190508
 * */
class _GridViewState extends State<GridViewTest>{

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(

        appBar: new AppBar(
          title: new Text("GridView 等分寬度"),
        ),

        body: GridView.extent(
          maxCrossAxisExtent: 200,
          childAspectRatio: 1.0,
          children: <Widget>[
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
          ],
        ),
      ),
    );
  }
}
GridView 分頁(yè)加載
import 'package:flutter/material.dart';

/**
 * @des GridView 分頁(yè)加載
 * @author liyongli 20190508
 * */
class GridViewTest extends StatefulWidget{

  @override
  State<StatefulWidget> createState() => new _GridViewState();

}

/**
 * @des GridView Widget State
 * @author liyongli 20190508
 * */
class _GridViewState extends State<GridViewTest>{

  List<IconData> _iconList = [];

  @override
  void initState() {
    _getData();
  }

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(

        appBar: new AppBar(
          title: new Text("GridView 分頁(yè)加載"),
        ),

        body: GridView.builder(
          itemCount: _iconList.length,
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 5, childAspectRatio: 1.0),
            itemBuilder: (context, index){
              if(index == _iconList.length - 1 && _iconList.length < 200){
                _getData();
              }
              return Icon(_iconList[index]);
            }
        )
      ),
    );
  }

  // 請(qǐng)求數(shù)據(jù)
  void _getData(){
    Future.delayed(Duration(milliseconds: 200)).then((e) {
      setState(() {
        _iconList.addAll([
          Icons.add_a_photo,
          Icons.add_a_photo,
          Icons.add_a_photo,
          Icons.add_a_photo, Icons.cake,
          Icons.add_a_photo
        ]);
      });
    });
  }
}

CustomScrollView(自定義滑動(dòng) View)

Sliver

Sliver 是分片、分區(qū)的意思颅停。當(dāng)我們需要將不同的可滑動(dòng)組件組合在一起時(shí)谓晌,就需要使用此對(duì)象來(lái)完成。ListView 和 GridView 都有對(duì)應(yīng)的組合對(duì)象如:SliverList 和 SliverGrid癞揉。

import 'package:flutter/material.dart';

/**
 * @des CustomScrollView Widget
 * @author liyongli 20190509
 * */
class CustomScrollViewTest extends StatelessWidget{


  @override
  Widget build(BuildContext context) {
    return Material(
      child: CustomScrollView(
        slivers: <Widget>[

          // 跟隨頁(yè)面滑動(dòng)的導(dǎo)航欄
          SliverAppBar(
            pinned: true, // 是否固定
            expandedHeight: 200.0, // 高度
            flexibleSpace: FlexibleSpaceBar(
              title: Text("title"),
              background: Image.asset("images/custom_scroll_title.png", fit: BoxFit.cover,)
            ),
          ),

          // Grid
          SliverPadding(
            padding: EdgeInsets.all(10.0),
            sliver: SliverGrid(
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 3, // 4行
                mainAxisSpacing: 20.0,
                crossAxisSpacing: 10.0,
                childAspectRatio: 5.0,
              ),
              delegate: SliverChildBuilderDelegate(
                  (BuildContext context, int index){
                    return Container(
                      alignment: Alignment.center,
                      color: Colors.blue,
                      child: Text("grid Item $index", style: TextStyle(color: Colors.white),),
                    );
                  },
                childCount: 12
              ),
            ),
          ),

          // List
          SliverFixedExtentList(
            itemExtent: 20,
            delegate: SliverChildBuilderDelegate((BuildContext context , int index){
              return Container(
                alignment: Alignment.center,
                color: Colors.blue,
                child: Text("list Item $index", style: TextStyle(color: Colors.white),),
              );
            },
              childCount: 30
            ),
          ),
        ],
      ),
    );
  }

}

ScrollController(控制器)

可設(shè)置滑動(dòng) View 的滾動(dòng)位置纸肉,還可監(jiān)聽(tīng)并獲取滑動(dòng) View 的滾動(dòng)狀態(tài)及數(shù)據(jù)

  ScrollController({
    double initialScrollOffset = 0.0,
    this.keepScrollOffset = true,
    this.debugLabel,
  }) : assert(initialScrollOffset != null),
       assert(keepScrollOffset != null),
       _initialScrollOffset = initialScrollOffset;
  • initialScrollOffset:初始位置
  • keepScrollOffset:是否保存滾動(dòng)位置
  • ScrollController.jumpTo(0.0):直接滾動(dòng)至指定位置
  • ScrollController.animateTo(0.0, duration: Duration(milliseconds: 500), curve: Curves.decelerate):帶動(dòng)畫(huà)滾動(dòng)至指定位置
import 'package:flutter/material.dart';

/**
 * des ScrollController Test
 * @author liyongli 20190513
 * */
class ScrollControllerTest extends StatefulWidget{

  @override
  State<StatefulWidget> createState() {
    return new _ScrollControllerTestState();
  }

}

/**
 * des ScrollController Test State
 * @author liyongli 20190513
 * */
class _ScrollControllerTestState extends State<ScrollControllerTest>{

  ScrollController _controller = new ScrollController();
  double oldOffset = -1;

  @override
  void initState() {
    super.initState();

    _controller.addListener((){
      print("$_controller.offset" + " / " + "$oldOffset");

      if(oldOffset > _controller.offset){
        print("向下滑");
      }else{
        print("向上滑");
      }

      oldOffset = _controller.offset;
    });

  }

  @override
  void dispose() {
    super.dispose();
    _controller.dispose(); // 釋放資源
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(

        appBar: AppBar(
          title: Text("ScrollController"),

        ),

        body: Scrollbar(
            child: ListView.builder(
                controller: _controller, // 控制器
                itemCount: 50, // item count
                itemExtent: 100.0, // item height
                itemBuilder: (context, index){
                  return ListTile(title:Text("$index", style: TextStyle(color: Colors.blue),));
                }
            )
        ),

        floatingActionButton: FloatingActionButton(
          onPressed: _up,
          child: Icon(Icons.arrow_upward)
        ),
      ),
    );
  }

//  滾動(dòng)
  void _up(){
    // 帶動(dòng)畫(huà)滾動(dòng)
    _controller.animateTo(0.0, duration: Duration(milliseconds: 500), curve: Curves.decelerate);
    // 無(wú)動(dòng)畫(huà)滾動(dòng)
//    _controller.jumpTo(0.0);
  }

}

本篇到此完結(jié),更多 Flutter 跨平臺(tái)移動(dòng)端開(kāi)發(fā) 原創(chuàng)內(nèi)容持續(xù)更新中~

期待您 關(guān)注 / 點(diǎn)贊 / 收藏 向著 大前端工程師 晉級(jí)喊熟!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末柏肪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子芥牌,更是在濱河造成了極大的恐慌烦味,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件壁拉,死亡現(xiàn)場(chǎng)離奇詭異拐叉,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)扇商,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)宿礁,“玉大人案铺,你說(shuō)我怎么就攤上這事“鹁福” “怎么了控汉?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵笔诵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我姑子,道長(zhǎng)乎婿,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任街佑,我火速辦了婚禮谢翎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘沐旨。我一直安慰自己森逮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布磁携。 她就那樣靜靜地躺著褒侧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪谊迄。 梳的紋絲不亂的頭發(fā)上闷供,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音统诺,去河邊找鬼歪脏。 笑死,一個(gè)胖子當(dāng)著我的面吹牛篙议,可吹牛的內(nèi)容都是我干的唾糯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼鬼贱,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼移怯!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起这难,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤舟误,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后姻乓,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體嵌溢,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年蹋岩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了赖草。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡剪个,死狀恐怖秧骑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤乎折,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布绒疗,位于F島的核電站,受9級(jí)特大地震影響骂澄,放射性物質(zhì)發(fā)生泄漏吓蘑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一坟冲、第九天 我趴在偏房一處隱蔽的房頂上張望磨镶。 院中可真熱鬧,春花似錦樱衷、人聲如沸棋嘲。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)沸移。三九已至,卻和暖如春侄榴,著一層夾襖步出監(jiān)牢的瞬間雹锣,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工癞蚕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蕊爵,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓桦山,卻偏偏與公主長(zhǎng)得像攒射,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子恒水,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容