我們在前幾天的List&Grid中,介紹了不同的列表用法,其中在ListView, ListView.builder,GridView, GridView.builder 也使用到了基礎的滾動特效。而接續我們會介紹更多Flutter中關於「滾動效果」的特色與使用方法,方便我們在規劃app時能夠給予用戶更好的使用體驗!
DraggableScrollableSheet 元件提供我們一個在特定螢幕大小範圍內可以滾動的效果,通常用於在視窗底部(Bottom)彈出資訊的欄位。以下提供本元件用法:
DraggableScrollableSheet(
          initialChildSize: 0.5, // 初始高度佔父元件的比例
          maxChildSize: 0.9,     // 最大高度佔父元件的比例
          minChildSize: 0.2,     // 最小高度佔父元件的比例
          
          builder: (BuildContext context, ScrollController scrollController) {
            return Container(
              color: Colors.blue[100],//背景顏色
              child: ListView.builder( //主要可滾動區域
                controller: scrollController,//滾動控制器
                itemCount: 25,
                itemBuilder: (BuildContext context, int index) {
                  return ListTile(title: Text('Item $index'));
                },
              ),
            );
          },
        ),
這個範例我們示範如何用DraggableScrollableSheet元件製作個預設佔有一半螢幕空間的List效果,index從0~24
import 'package:flutter/material.dart';
void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: HomePage(),
    );
  }
}
class HomePage extends StatelessWidget {
  const HomePage({Key? key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('DraggableScrollableSheet'),
      ),
      body: SizedBox.expand(
        child: DraggableScrollableSheet(
          
          initialChildSize: 0.5, // 初始高度佔父元件的比例
          maxChildSize: 0.9,     // 最大高度佔父元件的比例
          minChildSize: 0.2,     // 最小高度佔父元件的比例
          builder: (BuildContext context, ScrollController scrollController) {
            return Container(
              color: Colors.blue[100],
              child: ListView.builder(
                controller: scrollController,
                itemCount: 25,
                itemBuilder: (BuildContext context, int index) {
                  return ListTile(title: Text('Item $index'));
                },
              ),
            );
          },
        ),
      ),
    );
  }
}
DraggableScrollableSheet 使用範例
藍色的List部分可以滾動,上方白色部分則可以放置原有的內容,不會跟著滾動!

當我們在視窗中需要多個滾動的事件,為了達成較為複雜的滾動嵌套事件,我們會使用SilverGrid、SilverList的方式來達成。而以下我們會用SliverChildBuilderDelegate元件來介紹如何靈活運用此複雜滾動效果!
以下提供本元件的範例用法:
SliverChildBuilderDelegatem元件建立一個包含兩個卡片的SliverList。 每個卡片內部包含
元件詳細內部功能細分如下:
delegate: SliverChildBuilderDelegate(
                (BuildContext context, int index) {
                  return Card( //卡片元件
                    margin: const EdgeInsets.all(8.0),
                    child: Column(
                      children: <Widget>[
                        Text('Grid Item $index'),
                        Expanded(
                          child: ListView.builder(
                            itemCount: 10, // 列表中的項目數
                            itemBuilder: (BuildContext context, int subIndex) {
                              return ListTile(
                                title: Text('Sub Item $subIndex'),
                              );
                            },
                          ),
                        ),
                      ],
                    ),
                  );
                },
                childCount: 2, //index值為0~1
              ),
最外層我們使用CustomScrollView元件,預先設定
接下來,slivers內具有兩個SliverGrid元件,分別包含了上面提及的SliverChildBuilderDelegate元件,其中
SliverGrid內,有兩列SliverChildBuilderDelegate,並且每個卡片子索引ListTile由0~9計算SliverGrid內,同樣有兩列SliverChildBuilderDelegate,並且每個卡片子索引ListTile做了"subindex+5"的變化,由5~14計算最後我們會再由semanticIndexOffset: 2代表兩行卡片間的 index (第一行為Griditem 0-1、第二行為2-3),相差(偏移量)為2
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: const Text('CustomScrollView Example'),
        ),
        body: CustomScrollView(
          semanticChildCount: 4,
          slivers: <Widget>[
            SliverGrid(
              gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
              ),
              delegate: SliverChildBuilderDelegate(
                (BuildContext context, int index) {
                  return Card(
                    margin: const EdgeInsets.all(8.0),
                    child: Column(
                      children: <Widget>[
                        Text('Grid Item $index'),//設為第二行的索引號Grid item0,1
                        Expanded(
                          child: ListView.builder(
                            itemCount: 10, // 列表中的項目數
                            itemBuilder: (BuildContext context, int subIndex) {
                              return ListTile(
                                title: Text('Sub Item $subIndex'), //設為卡片索引號,為0~9
                              );
                            },
                          ),
                        ),
                      ],
                    ),
                  );
                },
                childCount: 2,
              ),
            ),
            SliverGrid(
              gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
              ),
              delegate: SliverChildBuilderDelegate(
                (BuildContext context, int index) {
                  return Card(
                    margin: const EdgeInsets.all(8.0),
                    child: Column(
                      children: <Widget>[
                        Text('Grid Item ${index + 2}'),//設為第二行的索引號Grid item2,3
                        Expanded(
                          child: ListView.builder(
                            itemCount: 10, // 列表中的項目數
                            itemBuilder: (BuildContext context, int subIndex) {
                              return ListTile(
                                title: Text('Sub Item ${subIndex + 5}'), //設為卡片索引號+5,為5~14
                              );
                            },
                          ),
                        ),
                      ],
                    ),
                  );
                },
                childCount: 2,
                semanticIndexOffset: 2,//兩者語意項目索引的偏移量
              ),
            ),
          ],
        ),
      ),
    );
  }
}
