flutter widget組件之-----------可滾動的組件
widget分爲兩類:widgets library中的標準widget和Material Components library中的專用widget;任何應用程序都可以使用widgets library中的widget,但只有Material應用程序可以使用Material Components庫。其中Card,ListTitle就是Material Components庫中的組件。
GridView
一個可滾動的二維空間數組
- 構造函數
GridView({
Key key,
Axis scrollDirection = Axis.vertical,// 滾動方向,水平和垂直
bool reverse = false,// 子項排列是否倒序
ScrollController controller,
bool primary,//如果內容不足,則用戶無法滾動 而如果[primary]爲true,它們總是可以嘗試滾動
ScrollPhysics physics,//滑動類型設置
bool shrinkWrap = false,// 內容適配
EdgeInsetsGeometry padding, // 內邊距
//gridDelegate 委託類 ,類型是SliverGridDelegate 兩個常用的實現類爲SliverGridDelegateWithFixedCrossAxisCount(固定列數),SliverGridDelegateWithMaxCrossAxisExtent(用於子元素有最大寬度限制的場景)
@required this.gridDelegate,
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
bool addSemanticIndexes = true,
double cacheExtent,
List<Widget> children = const <Widget>[],
int semanticChildCount,
})
GridView.builder({
//省略其它
@required this.gridDelegate,//網格委託
@required IndexedWidgetBuilder itemBuilder,//子項構建者
})
GridView.custom({
//省略其它
@required this.gridDelegate, //網格委託
@required this.childrenDelegate,// 子項委託
})
GridView.count({
//省略其它
@required int crossAxisCount,// 列數
})
GridView.extent({
//省略其它
@required double maxCrossAxisExtent, //子項組件大小
})
- 應用示例
// 定義列表數據類型
class BaseBean {
String name;
int age;
String content;
BaseBean(this.name, this.age, this.content);
}
// 利用list.generate函數 構造列表數據集合
List<BaseBean> list = new List<BaseBean>.generate(
60, (i) => new BaseBean("name$i", i, "content=$i"));
class MyStatePageState extends State<MyStatePage> {
// 構建子項
Widget BuildItem(BaseBean item){
return Container(
child:Column(
children: <Widget>[
new Text(
"${item.name}",
style: new TextStyle(fontSize: 18.0, color: Colors.green),
),
new Text(
"${item.age}",
style: new TextStyle(fontSize: 18.0, color: Colors.red),
),
],
),
);
}
// gridview 構造函數構建
Widget GridViewLayout(){
return GridView(
scrollDirection: Axis.vertical,
padding: EdgeInsets.all(20.0),
//
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 5,
),
children:list.map((item)=>BuildItem(item)).toList(),
);
}
// gridView builder 構建視圖列表
Widget gridViewLayoutBuilder(List<BaseBean> list) {
return GridView.builder(
scrollDirection: Axis.vertical, // 主軸防線
padding: EdgeInsets.all(10.0),
itemCount:list.length,// 標明list的長度,不然報錯
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
) ,
// 創建子項widgets
itemBuilder: (context, i) => new Container(
child: new Row(
children: <Widget>[
new Text(
"${list[i].name}",
style: new TextStyle(fontSize: 18.0, color: Colors.red),
),
],
),
));
}
/// gridView count 構建視圖列表
Widget gridViewLayoutCount(List<BaseBean> list) {
return GridView.count(
crossAxisCount: 5,//列數
padding: EdgeInsets.symmetric(vertical: 0),
children: list.map((item)=>BuildItem(item)).toList()
);
}
/// gridView extent 構建視圖列表
Widget gridViewLayoutExtent(List<BaseBean> list) {
return GridView.extent(
maxCrossAxisExtent: 200,// 子項組件大小
children: list.map((item)=>BuildItem(item)).toList()
);
}
/// gridView custom 構建視圖列表
Widget gridViewLayoutCustom(List<BaseBean> list) {
return GridView.custom(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
),
// 構建子項的代理
childrenDelegate: MyChildrenDelegate(
(BuildContext context, int i) {
return new Container(
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new Text(
"${list[i].name}",
style: new TextStyle(fontSize: 18.0, color: Colors.red),
),
],
));
},
childCount: list.length,
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("list body"),),
// body:GridViewLayout(),
// body:gridViewLayoutBuilder(list),
// body:gridViewLayoutCount(list),
// body: gridViewLayoutExtent(list),
body:gridViewLayoutCustom(list),
);
}
}
// 自定義gridview佈局的代理類
class MyChildrenDelegate extends SliverChildBuilderDelegate {
MyChildrenDelegate(
Widget Function(BuildContext, int) builder, {
int childCount,
bool addAutomaticKeepAlive = true,
bool addRepaintBoundaries = true,
}) : super(builder,
childCount: childCount,
addAutomaticKeepAlives: addAutomaticKeepAlive,
addRepaintBoundaries: addRepaintBoundaries);
///監聽 在可見的列表中 顯示的第一個位置和最後一個位置
@override
void didFinishLayout(int firstIndex, int lastIndex) {
print('firstIndex: $firstIndex, lastIndex: $lastIndex');
}
// 可不重寫 重寫不能爲null 默認是true 添加進來的實例與之前的實例是否相同 相同返回true 反之false
// listView 暫時沒有看到應用場景 源碼中使用在 SliverFillViewport 中
@override
bool shouldRebuild(SliverChildBuilderDelegate oldDelegate) {
// TODO: implement shouldRebuild
// return super.shouldRebuild(oldDelegate);
return this !=oldDelegate;
}
}
NestedScrollView
一個可以嵌套其它可滾動widget的widget
- 構造函數
NestedScrollView({
Key key,
this.controller,
this.scrollDirection = Axis.vertical,
this.reverse = false,
this.physics,
@required this.headerSliverBuilder,
@required this.body,
})
- 應用示例
SingleChildScrollView
有一個子widget的可滾動的widget,子內容超過父容器時可以滾動
- 構造函數
SingleChildScrollView({
Key key,
this.scrollDirection = Axis.vertical, //滾動方向,有水平和垂直連個方向
this.reverse = false,// 子項是否倒序排列
this.padding,// 內邊距
bool primary,//控制是否有滑動效果,內容不足的時候,有效果
this.physics,// 滑動類型
this.controller,// 控制滑動位置的控制器
this.child,// 子項
})
- 應用示例
class MyStatePageState extends State<MyStatePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("singleChildScrollView"),),
body:SingleChildScrollView(
scrollDirection:Axis.vertical,
reverse: true,// 子項倒序排列
padding: EdgeInsets.all(10),
primary: true,// 設置爲true,即使內容少,也有滾動效果
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: 500,
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Container(
// A fixed-height child.
color: const Color(0xff808000), // Yellow
height: 120.0,
),
Container(
// Another fixed-height child.
color: const Color(0xff008000), // Green
height: 120.0,
),
],
),
),
),
);
}
}
Scrollable
實現了可滾動widget的交互模型,但不包含UI顯示相關的邏輯
- 構造函數
Scrollable({
Key key,
this.axisDirection = AxisDirection.down,//滾動方向
this.controller,// 控制滾動位置的控制器
this.physics,// 股東類型
@required this.viewportBuilder,// 創建顯示滾動內容窗口的Builder
this.excludeFromSemantics = false,
this.semanticChildCount,// 帶有語義信息的子項widget的數量
})
- 應用示例
ScrollBar
一個Material Design 滾動條,表示當前滾動到了什麼位置
- 構造函數
Scrollbar({
Key key,
@required this.child, // 子項 滾動條,不知道怎麼用呀
})
- 應用示例
CustomScrollView
一個使用slivers創建自定義的滾動效果的ScrollView
- 構造函數
CustomScrollView({
Key key,
Axis scrollDirection = Axis.vertical,// // 滾動方向,水平和垂直
bool reverse = false,// 子項是否倒序排列
ScrollController controller,// 滾動位置控制器
bool primary,// 是否有滾動效果
ScrollPhysics physics,// 滾動類型
bool shrinkWrap = false,
double cacheExtent,
this.slivers = const <Widget>[],// 視圖窗口中的sliver組件塊
int semanticChildCount,
})
- 應用示例
class MyStatePageState extends State<MyStatePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("scrollable"),),
body:CustomScrollView(
// sliver組件塊
slivers: <Widget>[
const SliverAppBar(
pinned: true,
expandedHeight: 250.0,
flexibleSpace: FlexibleSpaceBar(
title: Text('Demo'),
),
),
SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 4.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.teal[100 * (index % 9)],
child: Text('Grid Item $index'),
);
},
childCount: 20,
),
),
SliverFixedExtentList(
itemExtent: 50.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.lightBlue[100 * (index % 9)],
child: Text('List Item $index'),
);
},
),
),
],
),
);
}
}
NotificationListener
一個用來監聽樹上冒泡通知的widget
- 構造函數
NotificationListener({
Key key,
@required this.child,// 子項
this.onNotification,//
})
- 應用示例
ScrollConfiguration
控制可滾動組件在子樹中的表現行爲
- 構造函數
ScrollConfiguration({
Key key,
@required this.behavior,// 子項組件scrollable的相關配置
@required Widget child,// 子項組件
})
- 應用示例
refreshIndicator
Material Design下拉刷新指示器,包裝一個可滾動widget
- 構造函數
RefreshIndicator({
Key key,
@required this.child,
this.displacement = 40.0,
@required this.onRefresh,
this.color,
this.backgroundColor,
this.notificationPredicate = defaultScrollNotificationPredicate,
this.semanticsLabel,
this.semanticsValue,
})
- 應用示例
class MyStatePageState extends State<MyStatePage>{
// TODO: implement build
var curPage = 1;
ScrollController _controller = new ScrollController();
var listData = List<String>.generate(30, (i) => "CL $i");
Future<Null> _pullToRefresh() async {
print("object.........................................");
// curPage = 1;
//下拉刷新做處理
setState(() {
////改變數據,這裏隨意發揮
listData = List<String>.generate(30, (i) => "CL $i");
});
return null;
}
MyStatePageState() {
_controller.addListener(() {
var maxScroll = _controller.position.maxScrollExtent;
var pixels = _controller.position.pixels;
if (maxScroll == pixels && listData.length < 100){
// 上拉刷新做處理
print('load more ...');
// curPage++;
setState(() {
//改變數據,這裏隨意發揮
listData = List<String>.generate(100, (i) => "CL $i");
});
}
});
}
@override
Widget build(BuildContext context) {
// 創建listview 列表
Widget listView = new ListView.builder(
itemCount: listData.length,
itemBuilder: (context,i) {
//這裏填充自己想要的列表UI
return new Container(
height: 45.0,
color: Colors.blue,
child: new Text(
"bbbbbbbbbbbbbbbbbbbbbbb___$i",
style: new TextStyle(fontSize: 15.0),
),
);
},
controller: _controller,
);
return new RefreshIndicator(child: listView, onRefresh: _pullToRefresh);
}
}