- 滾動(dòng)組件有
ListView,GridView,Sliver
ListView
- ListView創(chuàng)建的方式通常有三種,分別為
ListView(),ListView.builder(),ListView.separated()
ListView創(chuàng)建方式
- 第一種方式:
ListView() - 案例代碼:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: SFHomePage());
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("基礎(chǔ)widget")), body: SFHomeContent());
}
}
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView(
scrollDirection: Axis.vertical,
itemExtent: 100,//設(shè)置Item的高度
children: List.generate(100, (index) {
return ListTile(
leading: Icon(Icons.people),
trailing: Icon(Icons.delete),
title: Text("聯(lián)系人${index+1}"),
subtitle: Text("聯(lián)系人電話號(hào)碼:19991604555"),
);
}),
);
}
}
-
ListTile組件就是ListView的Item; -
itemExtent:設(shè)置Item的高度; - 效果圖如下:

通過
ListView()創(chuàng)建,會(huì)一次性創(chuàng)建100個(gè)Item,這樣性能比較差,其適用于Item個(gè)數(shù)確定,且數(shù)量較少的情況下才會(huì)采用;第二種方式:
ListView.builder()ListView.builder()不會(huì)一次性創(chuàng)建所有Item,而是需要展示的Item才會(huì)去創(chuàng)建,性能較好;案例代碼如下:
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: 100,
itemExtent: 50,
itemBuilder: (BuildContext ctx, int index){
return Text("Hello World!!! ${index}",style: TextStyle(fontSize: 20),);
}
);
}
}
- 第三種方式:
ListView.separated() - 案例代碼如下:
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView.separated(
itemBuilder: (BuildContext ctx, int index){
return Text("Hello World!!! ${index}",style: TextStyle(fontSize: 20),);
},
//分割線
separatorBuilder: (BuildContext ctx,int index){
return Divider(color: Colors.red,indent: 20,endIndent: 20,thickness: 5);
},
itemCount: 100
);
}
}
-
itemBuilder:創(chuàng)建Item; -
separatorBuilder:創(chuàng)建分割線;
GridView
- GridView的創(chuàng)建方式有:
GridView(),GridView.builder() - 案例代碼一:
GridView()+SliverGridDelegateWithFixedCrossAxisCount
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 1.5,
crossAxisSpacing: 8,
mainAxisSpacing: 8
),
children:
List.generate(100, (index) {
return Container(
color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
);
}),
);
}
}
-
SliverGridDelegateWithFixedCrossAxisCount:交叉軸方向上item數(shù)量固定,其寬度根據(jù)屏幕的寬度與item的數(shù)量進(jìn)行計(jì)算;-
crossAxisCount:item的個(gè)數(shù); -
childAspectRatio:item的寬高比; -
crossAxisSpacing:交叉軸方向上 item之間的間距; -
mainAxisSpacing:主軸方向上 item之間的間距;
-
案例代碼二:
GridView()+SliverGridDelegateWithMaxCrossAxisExtent
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GridView(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 220,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
childAspectRatio: 1.5
),
children: List.generate(100, (index) {
return Container(
color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),Random().nextInt(256)),
);
})
);
}
}
-
SliverGridDelegateWithMaxCrossAxisExtent:交叉軸方向上的設(shè)置item寬度,個(gè)數(shù)不固定;-
maxCrossAxisExtent:item的最大寬度;
-
案例代碼三:
GridView.builder()+SliverGridDelegateWithFixedCrossAxisCount
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 8,
mainAxisSpacing: 8
),
itemBuilder: (BuildContext ctx,int index){
return Container(
color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),Random().nextInt(256)),
);
}
);
}
}
Slivers -- CustomScrollView
CustomScrollView:自定義滾動(dòng)組件,需要傳入Slivers即Sliver數(shù)組,我們知道ListView與GridView都是繼承自BoxScrollView,而BoxScrollView是一個(gè)抽象類,從源碼來看ListView與GridView在察創(chuàng)建的過程中都需要執(zhí)行buildSlivers方法,其內(nèi)部調(diào)用buildChildLayout方法,這是一個(gè)抽象方法,分別由ListView與GridView來實(shí)現(xiàn),最終提供一個(gè)Sliver數(shù)組,其中ListView提供的Sliver為SliverFixedExtentList或者是SliverList,GridView提供的Sliver為SliverGrid案例代碼:?jiǎn)蝹€(gè)Sliver
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: SFHomePage());
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("基礎(chǔ)widget")),
body: SFHomeContent());
}
}
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SliverDemo1();
}
}
class SliverDemo1 extends StatelessWidget {
const SliverDemo1({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: [
SliverSafeArea(
sliver: SliverPadding(
padding: EdgeInsets.all(8),
sliver: SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
childAspectRatio: 1.5
),
delegate: SliverChildBuilderDelegate(
(BuildContext ctx,int index){
return Container(
color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),Random().nextInt(256)),
);
},
childCount: 100
),
),
),
)
],
);
}
}
自定義CustomScrollView,需傳入Slivers數(shù)組,這里傳入的Sliver為
SliverGrid;參數(shù)
gridDelegate:是提供布局信息;參數(shù)
delegate:是提供item組件,類型為SliverChildDelegate;-
SliverChildDelegate是抽象類,其作用是用來創(chuàng)建滾動(dòng)組件的item,其有兩個(gè)子類分別為
SliverChildListDelegate與SliverChildBuilderDelegate-
SliverChildListDelegate:性能較差,item一次性創(chuàng)建所有; -
SliverChildBuilderDelegate:性能較好,創(chuàng)建需要展示的item;
-
-
SafeArea與SliverSafeArea的區(qū)別:- SafeArea:安全區(qū)域,讓目標(biāo)組件在安全區(qū)域內(nèi)顯示;
- SliverSafeArea:給Sliver設(shè)置安全區(qū)域,且在滾動(dòng)時(shí)可以在非安全區(qū)域內(nèi)滾動(dòng),而SafeArea不可以;
SliverPadding:是Sliver自己的設(shè)置內(nèi)邊距的組件;案例代碼:多個(gè)Sliver
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: SFHomePage());
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(title: Text("基礎(chǔ)widget")),
body: SFHomeContent());
}
}
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: [
SliverAppBar(
pinned: true,
expandedHeight: 200,
flexibleSpace: FlexibleSpaceBar(
title: Text("Hello World!!",style: TextStyle(fontSize: 25),),
),
),
SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
childAspectRatio: 2.5
),
delegate: SliverChildBuilderDelegate(
(BuildContext ctx,int index){
return Container(
color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),Random().nextInt(256)),
);
},
childCount: 10
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext ctx,int index){
return ListTile(
leading: Icon(Icons.people),
title: Text("聯(lián)系人$index"),
);
},
childCount: 20
),
)
],
);
}
}
-
slivers數(shù)組中傳入了SliverAppBar,SliverGrid與SliverList三種類型的Sliver,效果如下:
image.png
滾動(dòng)組件的監(jiān)聽
- 滾動(dòng)組件的監(jiān)聽通常有兩種方式,分別為
controller與NotificationListener
controller監(jiān)聽
- 可以設(shè)置默認(rèn)值offset;
- 監(jiān)聽滾動(dòng),也可以監(jiān)聽滾動(dòng)的位置;
- 案例代碼:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: SFHomePage());
}
}
class SFHomePage extends StatefulWidget {
@override
_SFHomePageState createState() => _SFHomePageState();
}
class _SFHomePageState extends State<SFHomePage> {
ScrollController controller = ScrollController(initialScrollOffset: 300);
bool isShowFloatButton = false;
@override
void initState() {
super.initState();
controller.addListener(() {
print("監(jiān)聽到滾動(dòng): ${controller.offset}");
setState(() {
isShowFloatButton = controller.offset >= 1000;
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("基礎(chǔ)widget")),
body: SFHomeContent(controller),
floatingActionButton: isShowFloatButton ? FloatingActionButton(
child: Icon(Icons.arrow_upward),
onPressed: (){
controller.animateTo(0, duration: Duration(seconds: 1), curve: Curves.easeIn);
},
) : null,
);
}
}
class SFHomeContent extends StatelessWidget {
final ScrollController controller;
SFHomeContent(this.controller);
@override
Widget build(BuildContext context) {
return ListView.builder(
controller: controller,
itemBuilder: (BuildContext ctx, int index) {
return ListTile(
leading: Icon(Icons.people),
title: Text("聯(lián)系人$index"),
);
},
itemCount: 100,
);
}
}
- 右下角懸浮按鈕,當(dāng)前滾動(dòng)偏移量>=1000時(shí)顯示;
NotificationListener監(jiān)聽
- 案例代碼:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: SFHomePage());
}
}
class SFHomePage extends StatefulWidget {
@override
_SFHomePageState createState() => _SFHomePageState();
}
class _SFHomePageState extends State<SFHomePage> {
bool isShowFloatButton = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("基礎(chǔ)widget")),
body: SFHomeContent(),
floatingActionButton: isShowFloatButton
? FloatingActionButton(
child: Icon(Icons.arrow_upward),
onPressed: () {
},
)
: null,
);
}
}
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return NotificationListener(
onNotification: (ScrollNotification notification){
if(notification is ScrollStartNotification){
print("開始滾動(dòng)");
}else if (notification is ScrollUpdateNotification){
print("正在滾動(dòng) -- 總區(qū)域:${notification.metrics.maxScrollExtent} 當(dāng)前位置: ${notification.metrics.pixels}");
}else if (notification is ScrollEndNotification){
print("結(jié)束滾動(dòng)");
}
return true;
},
child: ListView.builder(
itemBuilder: (BuildContext ctx, int index) {
return ListTile(
leading: Icon(Icons.people),
title: Text("聯(lián)系人$index"),
);
},
itemCount: 100,
),
);
}
}
