Flutter 初探(四):滾動類Widgets

若數發表於2019-04-21

學習內容

以下是可滾動Widgets的部分彙總:

  • SingleChildScrollView
  • ListView
  • GridView
  • CustomScrollView

簡單使用

// SingleChildScrollView
class NewSingleChildScrollView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    return new Scaffold(
      appBar: AppBar(title: Text('SingleChildScrollView學習')),
      body: Scrollbar(
          child: SingleChildScrollView(
        padding: EdgeInsets.all(16.0),
        child: Center(
          child: Column(
            // 動態建立一個 List<Widget>
            children: str
                .split('')
                // 每個字母都用一個Text顯示,設定字型大小為原來的兩倍
                .map((c) => Text(
                      c,
                      textScaleFactor: 2.0,
                    ))
                .toList(),
          ),
        ),
      )),
    );
  }
}

// ListView 一個無限載入例項
class InfiniteListView extends StatefulWidget {
  @override
  _InfiniteListViewState createState() => new _InfiniteListViewState();
}

class _InfiniteListViewState extends State<InfiniteListView> {
  static const loadingTag = "##loading##"; //表尾標記
  var _words = <String>[loadingTag];

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

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

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: AppBar(title: Text("ListView學習")),
        body: ListView.separated(
          itemCount: _words.length,
          itemBuilder: (context, index) {
            //如果到了表尾
            if (_words[index] == loadingTag) {
              //不足100條,繼續獲取資料
              if (_words.length - 1 < 100) {
                //獲取資料
                _retrieveData();
                //載入時顯示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 {
                //已經載入了100條資料,不再獲取資料。
                return Container(
                    alignment: Alignment.center,
                    padding: EdgeInsets.all(16.0),
                    child: Text(
                      "沒有更多了",
                      style: TextStyle(color: Colors.grey),
                    ));
              }
            }
            //顯示單詞列表項
            return ListTile(title: Text(_words[index]));
          },
          separatorBuilder: (context, index) => Divider(height: .0),
        ));
  }
}

// 模擬非同步獲取資料並利用GirdView展示
class InfiniteGridView extends StatefulWidget {
  @override
  _InfiniteGridViewState createState() => new _InfiniteGridViewState();
}

class _InfiniteGridViewState extends State<InfiniteGridView> {
  List<IconData> _icons = []; //儲存Icon資料

  @override
  void initState() {
    // 初始化資料
    _retrieveIcons();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: AppBar(title: Text('GridView')),
      body: GridView.builder(
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 3, //每行三列
              childAspectRatio: 1.0 //顯示區域寬高相等
              ),
          itemCount: _icons.length,
          itemBuilder: (context, index) {
            //如果顯示到最後一個並且Icon總數小於200時繼續獲取資料
            if (index == _icons.length - 1 && _icons.length < 200) {
              _retrieveIcons();
            }
            return Icon(_icons[index]);
          }),
    );
  }

  //模擬非同步獲取資料
  void _retrieveIcons() {
    Future.delayed(Duration(milliseconds: 200)).then((e) {
      setState(() {
        _icons.addAll([
          Icons.ac_unit,
          Icons.airport_shuttle,
          Icons.all_inclusive,
          Icons.beach_access,
          Icons.cake,
          Icons.free_breakfast
        ]);
      });
    });
  }
}

class CustomScrollViewTestRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //因為本路由沒有使用Scaffold,為了讓子級Widget(如Text)使用
    //Material Design 預設的樣式風格,我們使用Material作為本路由的根。
    return Material(
      child: CustomScrollView(
        slivers: <Widget>[
          //AppBar,包含一個導航欄
          SliverAppBar(
            pinned: true,
            expandedHeight: 250.0,
            flexibleSpace: FlexibleSpaceBar(
              title: const Text('Demo'),
              background: Image.asset(
                "./images/avatar.png",
                fit: BoxFit.cover,
              ),
            ),
          ),

          SliverPadding(
            padding: const EdgeInsets.all(8.0),
            sliver: new SliverGrid(
              //Grid
              gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2, //Grid按兩列顯示
                mainAxisSpacing: 10.0,
                crossAxisSpacing: 10.0,
                childAspectRatio: 4.0,
              ),
              delegate: new SliverChildBuilderDelegate(
                (BuildContext context, int index) {
                  //建立子widget
                  return new Container(
                    alignment: Alignment.center,
                    color: Colors.cyan[100 * (index % 9)],
                    child: new Text('grid item $index'),
                  );
                },
                childCount: 20,
              ),
            ),
          ),
          //List
          new SliverFixedExtentList(
            itemExtent: 50.0,
            delegate: new SliverChildBuilderDelegate(
                (BuildContext context, int index) {
              //建立列表項
              return new Container(
                alignment: Alignment.center,
                color: Colors.lightBlue[100 * (index % 9)],
                child: new Text('list item $index'),
              );
            }, childCount: 50 //50個列表項
                ),
          ),
        ],
      ),
    );
  }
}

複製程式碼

部分效果:

5F5CC186F933A5E045BADFE6BC16941D.png

32F96E64F9652006EC4989DA717A08A2.png

46EDDE0C0383BC2AAF3B57804BD52A3D.png

FE2EEC894EE7BB7536B16CC9B52AAF10.png

Summary

走馬觀花式的學習並不能帶來高的學習效率,尤其是缺乏學習資料的技術,僅僅靠著一本書是不夠的,需要深入思考,結合文件,原始碼應該才是最佳學習實踐機會。

相關文章