flutter sliver 多種滾動組合開發指南
影片
https://www.bilibili.com/video/BV1WW4y1d7ZC/
前言
有不少同學工作中遇到需要把幾個不同滾動行為元件(頂部 appBar、內容固定塊、tabBar 切換、tabBarView檢視、自適應高度、橫向滾動)黏貼成一個元件。
這時候就需要 sliver 出場了,本文將會寫一個多種滾動的組合。
業務場景分析
下面是淘寶、小紅書的常見情況。
原文 https://ducafecat.com/blog/flutter-sliver-scroll
知識點 sliver
Sliver
是 Flutter 中用於構建可滾動檢視的基本構建塊之一。Sliver
是可滾動區域中的一小部分,具有固定的大小和位置,可以根據需要動態載入和解除安裝。Sliver
通常用於建立高效能、高度靈活的可滾動檢視,例如列表、網格、瀑布流等。
在 Flutter 中,有許多不同型別的 Sliver
元件,每個元件都有特定的作用和用途。下面是一些常見的 Sliver
元件:
SliverAppBar
:一個帶有滾動效果的應用欄,可以在向上滾動時隱藏,並在向下滾動時顯示。SliverList
:將子元件放置在一個垂直列表中,可以根據需要動態載入和解除安裝列表項。SliverGrid
:將子元件放置在一個網格中,可以根據需要動態載入和解除安裝網格項。SliverPadding
:為子元件提供填充,以使它們與其他Sliver
元件的大小和位置保持一致。SliverToBoxAdapter
:將一個普通的元件包裝成一個Sliver
元件,以便將其放置在CustomScrollView
中。
參考
步驟
第一步:Sliver 橫向滾動
lib/page.dart
Widget _mainView() {
return CustomScrollView(
slivers: [
// 橫向滾動
SliverToBoxAdapter(
child: SizedBox(
height: 100,
child: PageView(
children: [
Container(
color: Colors.yellow,
child: const Center(child: Text('橫向滾動')),
),
Container(color: Colors.green),
Container(color: Colors.blue),
],
),
),
),
...
SliverToBoxAdapter 進行包裝才能 slivers 使用。
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Sliver Scroll')),
body: _mainView(),
);
}
第二步:固定高度的 tabView
return CustomScrollView(
slivers: [
...
// 固定高度內容
SliverToBoxAdapter(
child: Container(
height: 200,
color: Colors.greenAccent,
child: const Center(child: Text('固定高度內容')),
),
),
// tabView 內容
SliverToBoxAdapter(
child: DefaultTabController(
length: 3,
child: Column(
children: [
const TabBar(
tabs: [
Tab(text: 'Tab 1'),
Tab(text: 'Tab 2'),
Tab(text: 'Tab 3'),
],
),
SizedBox(
height: 200,
child: TabBarView(
children: [
Container(color: Colors.yellow),
Container(color: Colors.green),
Container(color: Colors.blue),
],
),
),
],
),
),
),
外層巢狀 DefaultTabController ,才能讓 TabBar、TabBarView 順利工作。
第三步:自適應高度的 tabView
實現 SliverPersistentHeaderDelegate 抽象類
class _SliverDelegate extends SliverPersistentHeaderDelegate {
_SliverDelegate({
required this.minHeight,
required this.maxHeight,
required this.child,
});
final double minHeight; //最小高度
final double maxHeight; //最大高度
final Widget child;
@override
double get minExtent => minHeight;
@override
double get maxExtent => max(maxHeight, minHeight);
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return SizedBox.expand(child: child);
}
@override //是否需要重建
bool shouldRebuild(_SliverDelegate oldDelegate) {
return maxHeight != oldDelegate.maxHeight ||
minHeight != oldDelegate.minHeight ||
child != oldDelegate.child;
}
}
編寫固定頭部 sliver 元件
Widget _buildPersistentHeader(Widget child,
{double? minHeight, double? maxHeight}) =>
SliverPersistentHeader(
pinned: true,
delegate: _SliverDelegate(
minHeight: minHeight ?? 40.0,
maxHeight: maxHeight ?? 40.0,
child: child,
));
定義 TabController
class _MyPageViewState extends State<MyPageView> with TickerProviderStateMixin {
...
混入 TickerProviderStateMixin
late TabController _tabController;
@override
void initState() {
_tabController = TabController(length: 3, vsync: this);
super.initState();
}
@override
void dispose() {
_tabController.dispose(); // 釋放記憶體
super.dispose();
}
加入 slivers
Widget _mainView() {
return CustomScrollView(
slivers: [
...
// TabBar 固定
_buildPersistentHeader(TabBar(
controller: _tabController,
tabs: const [
Tab(text: 'Tab 1'),
Tab(text: 'Tab 2'),
Tab(text: 'Tab 3'),
],
)),
使用 SliverFillRemaining 來撐開剩餘空間
// TabBarView 自適應高度
SliverFillRemaining(
child: TabBarView(
controller: _tabController,
children: [
// 第一個選項卡的內容
ListView.builder(
itemCount: 20,
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text('Item $index'));
},
),
// 第二個選項卡的內容
ListView.builder(
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text('Item $index'));
},
),
// 第三個選項卡的內容
ListView.builder(
itemCount: 5,
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text('Item $index'));
},
),
],
),
),
SliverFillRemaining
是一個可以填充剩餘空間的 sliver 元件,它可以將子元件放置在檢視區域的剩餘空間中,並自動調整子元件的大小以填充整個空間。通常情況下,SliverFillRemaining
用於在滾動檢視中放置一個佔滿整個檢視區域的元件,例如底部欄或頁尾。
第四步:子 tabBar
還可以加入子 tabBar 組成父子選項切換
// 子 TabBar 固定
_buildPersistentHeader(TabBar(
controller: _tabController,
tabs: const [
Tab(text: 'subTab 1'),
Tab(text: 'subTab 2'),
Tab(text: 'subTab 3'),
],
)),
父子 tabBar 中間再加一個固定塊,檢視滾動效果
// 固定高度內容
SliverToBoxAdapter(
child: Container(
height: 100,
color: Colors.greenAccent,
child: const Center(child: Text('固定高度內容')),
),
),
最後:底部再加入 SliverList
我們在底部再加一個 list 模組,看看效果。
Widget _mainView() {
return CustomScrollView(
slivers: [
...
// 固定高度內容
SliverToBoxAdapter(
child: Container(
height: 200,
color: Colors.greenAccent,
child: const Center(child: Text('固定高度內容')),
),
),
// 列表 100 行
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return ListTile(title: Text('Item $index'));
},
childCount: 100,
),
),
程式碼
https://github.com/ducafecat/flutter_develop_tips/tree/main/flutter_application_sliver_scroll
小結
使用 Sliver
元件後我們確實把幾種滾動給黏貼上了,但是不難發現過於複雜的滾動,使用者體驗方面還是要考慮的。
不能只為了堆積功能。
感謝閱讀本文
如果我有什麼錯?請在評論中讓我知道。我很樂意改進。
© 貓哥
ducafecat.com
end