1.ListView 的基本使用
ListView 是一個盛放多個孩子的容器。我們從下面的例子開始介入:
1.1:三個構造
使用ListView構造方法
和Flex,Wrap類似,將子元素一個一個按順序排列。
var caverStyle= TextStyle(fontSize: 18, shadows: [//文字樣式
Shadow(
color: Colors.white, offset: Offset(-0.5, 0.5), blurRadius: 0)
]);
var show = ListView(//ListView的構造方法
padding: EdgeInsets.all(8.0),//邊距
children: <Widget>[//孩子們
Container(
height: 50,
color: Color(0xffff0000),
child: Center(child: Text('紅色',style: caverStyle,)),
),
Container(
height: 50,
color: Color(0xffFFFF00),
child: Center(child: Text('黃色',style: caverStyle,)),
),
Container(
height: 50,
color: Color(0xff00FF00),
child: Center(child: Text('綠色',style: caverStyle,)),
),
Container(
height: 50,
color: Color(0xff0000FF),
child: Center(child: Text('藍色',style: caverStyle,)),
),
],
);
複製程式碼
使用ListView.builder方法構造
使用builder方法對List或Map資料進行批量生成。
const colorMap = {//資料來源
0xffff0000: "紅色",
0xffFFFF00: "黃色",
0xff00FF00: "綠色",
0xff0000FF: "藍色",
};
var caverStyle= TextStyle(fontSize: 18, shadows: [//文字樣式
Shadow(
color: Colors.white, offset: Offset(-0.5, 0.5), blurRadius: 0)
]);
var show = ListView.builder(//使用builder方法進行構造
padding: EdgeInsets.all(8.0),
itemCount: colorMap.length,//條目的個數
itemBuilder: (BuildContext context, int index) {//條目構造器
return Container(
height: 50,
color: Color(colorMap.keys.toList()[index]),
child: Center(
child: Text(
'${colorMap.values.toList()[index]}',
style: caverStyle,
)),
);
});
複製程式碼
使用ListView.separated方法構造
separated方法和builder類似,但是可以通過separatorBuilder屬性建立分隔線。
const colorMap = {//資料來源
0xffff0000: "紅色",
0xffFFFF00: "黃色",
0xff00FF00: "綠色",
0xff0000FF: "藍色",
};
var caverStyle = TextStyle(fontSize: 18, shadows: [//文字樣式
Shadow(color: Colors.white, offset: Offset(-0.5, 0.5), blurRadius: 0)
]);
var show = ListView.separated(//使用separated方法進行構造
padding: EdgeInsets.all(8.0),
itemBuilder: (context, index) {//條目構造器
return Container(
height: 50,
color: Color(colorMap.keys.toList()[index]),
child: Center(
child: Text(
'${colorMap.values.toList()[index]}',
style: caverStyle,
)),
);
},
separatorBuilder: (context, index) {//分隔線構造器
return Container();
},
itemCount: colorMap.length);
複製程式碼
2.ListView進階使用
2.1:完成條目的封裝
比較簡單,我也不分析這麼佈局了,直接上程式碼。
資訊描述類
class PoemItem {
ImageProvider image;//圖片
var title;//標題
var author;//作者
var summary;//摘要
PoemItem({this.image, this.title, this.author, this.summary});
}
複製程式碼
條目的封裝
typedef OnItemClickListener = void Function();
class PoemItemView extends StatelessWidget {
final PoemItem data;
final OnItemClickListener onItemClickListener;
PoemItemView({Key key, this.data, this.onItemClickListener})
: super(key: key);
@override
Widget build(BuildContext context) {
var headIcon = Container(//左邊頭部
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.3),
offset: Offset(0.0, 0.0),
blurRadius: 3.0,
spreadRadius: 0.0,
),
],
),
width: 70,
height: 70,
child: Padding(
padding: EdgeInsets.all(3),
child: CircleAvatar(
backgroundImage: data.image,
),
));
var center = Column(//中間介紹
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(data.title,
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
Padding(
padding: EdgeInsets.only(top: 8),
child: Text(
"作者:${data.author}",
style: TextStyle(color: Colors.grey, fontSize: 12),
),
),
],
);
var summary = Text(//尾部摘要
data.summary,
maxLines: 3,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: Colors.grey, fontSize: 12),
);
var item = Row(//條目拼合
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
SizedBox(width: 10),
headIcon,
Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: center,
),
Expanded(
child: summary,
),
SizedBox(width: 10),
],
);
var result = Card(//卡片化+事件監聽
elevation: 5,
child: InkWell(
onTap: onItemClickListener,
child: Padding(
padding: EdgeInsets.all(10),
child: item,
)));
return result;
}
}
複製程式碼
2.2:ListView的使用
在構造器構造條目時,使用資料對條目進行資料填充,側達到資料展示效果
var data = <PoemItem>[];
for (var i = 0; i < 20; i++) {
data.add(PoemItem(
image: AssetImage("images/wy_200x300.jpg"),
title: "$i:以夢為馬",
author: "海子",
summary: "我要做遠方的忠誠的兒子,和物質的短暫情人,和所有以夢為馬的詩人一樣,我不得不和烈士和小丑走在同一道路上"));
}
var show = ListView.builder(
padding: EdgeInsets.all(8.0),
itemCount: data.length, //條目的個數
itemBuilder: (BuildContext context, int index) {
return PoemItemView(//資料填充條目
data: data[index],
onItemClickListener: () {//事件響應
print(index);
},
);
});
複製程式碼
2.3:分隔線的新增
這裡現將Card元件去掉。separated可以很輕鬆的實現下劃線分隔,並且容易改變長短,顏色
如果你願意,也可以去定義分隔的元件,專門作為分隔線。而且還能使用索引進行個性化設計
var data = <PoemItem>[];
for (var i = 0; i < 20; i++) {
data.add(PoemItem(
image: AssetImage("images/wy_200x300.jpg"),
title: "$i:以夢為馬",
author: "海子",
summary: "我要做遠方的忠誠的兒子,和物質的短暫情人,和所有以夢為馬的詩人一樣,我不得不和烈士和小丑走在同一道路上"));
}
var show = ListView.separated(
padding: EdgeInsets.all(8.0),
itemCount: data.length, //條目的個數
itemBuilder: (BuildContext context, int index) {
return PoemItemView(//資料填充條目
data: data[index],
onItemClickListener: () {//事件響應
print(index);
},
);
},
separatorBuilder: (BuildContext context, int index) {
return Padding(padding: EdgeInsets.only(left: 90),child: Divider(height: 1,color: Colors.orangeAccent,),);
},
);
複製程式碼
2.4:一個ListView中不同的條目樣式。
在抽取條目時,可以定義一個type屬性,條目樣式由Widget自身決定。
var random = Random();
var data = <ChartItem>[];
var strs = [
"我是要成為程式設計之王的男人,你是要成為程式設計之王的女人",
"憑君莫話封侯事,一將功成萬骨枯。你覺得如何?",
"在蒼茫的大海上,狂風摺積著烏雲,在烏雲和大海之間,海燕像黑色的閃電,在高傲的飛翔。"
];
for (var i = 0; i < 20; i++) {
data.add(ChartItem(
headIcon: AssetImage(
i.isEven ? "images/wy_200x300.jpg" : "images/icon_head.png"),
text: strs[random.nextInt(strs.length)],
type: i.isEven ? ChartType.left : ChartType.right));
}
var show = ListView.builder(
itemCount: data.length, //條目的個數
itemBuilder: (BuildContext context, int index) {
return ChartWidget(//資料填充條目
chartItem: data[index],
);
},
);
複製程式碼
比如這裡的ChartItem,本身定義了ChartType,在實現ChartWidget的時候,根據型別進行生成相應Widget,從而達到多種樣式的效果。
enum ChartType { right, left }
class ChartItem {
ImageProvider headIcon;
double maxWith;
ChartType type;
String text;
ChartItem({this.headIcon, this.text,this.maxWith = 250,this.type=ChartType.right});
}
class ChartWidget extends StatelessWidget {
final ChartItem chartItem;
ChartWidget({Key key, this.chartItem}) : super(key: key);
@override
Widget build(BuildContext context) {
switch (chartItem.type) {
case ChartType.right:
return _buildRight();
break;
case ChartType.left:
return _buildLeft();
break;
}
}
複製程式碼
3.ListView滑動控制器(上拉重新整理和下拉更新)
3.1:滑動控制器ScrollController的使用
class _ListViewPageState extends State<ListViewPage> {
ScrollController _scrollController = ScrollController();//定義變數及初始化
@override
void initState() {
super.initState();
_scrollController.addListener(() {//新增監聽
print("滑動了:${_scrollController.position.pixels},
離頂部高:${_scrollController.position.maxScrollExtent}");
});
}
@override
void dispose() {
_scrollController.dispose();//銷燬控制器
super.dispose();
}
@override
Widget build(BuildContext context) {
//略同...
var show = ListView.builder(
controller: _scrollController, //使用ScrollController
//略同...
);
}
}
複製程式碼
3.2:上拉重新整理
內建元件:RefreshIndicator,包裹一下即可
return RefreshIndicator(child: show, onRefresh: _onRefresh//使用RefreshIndicator
}
bool isLoading = false;
Future<void> _onRefresh() async {//下拉重新整理,非同步延遲三秒
if (isLoading) {
return;
}
await Future.delayed(Duration(seconds: 3), () {
setState(() {
isLoading = false;
});
});
}
複製程式碼
3.3:下拉載入更多
_scrollController.addListener(() {
if (_scrollController.position.pixels ==//說明滑到底部
_scrollController.position.maxScrollExtent) {
_loadMore();
}
});
bool isLoaded = false;
Future<void> _loadMore() async {
await Future.delayed(Duration(seconds: 3), () {
setState(() {
isLoaded=true;
});
});
}
var show = ListView.builder(
controller: _scrollController,
itemCount: data.length + 1, //條目的個數
itemBuilder: (BuildContext context, int index) {
if (index == data.length) {
return Offstage(//使用Offstage控制顯隱
offstage: isLoaded,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Center(
child: CircularProgressIndicator(),
),
));
} else {
return ChartWidget(
//資料填充條目
chartItem: data[index],
);
}
});
複製程式碼
4. physics屬性
physics對應ScrollPhysics類,其下有6個子類
FixedExtentScrollPhysics()
和NeverScrollableScrollPhysics
ListView無法滑動
複製程式碼
PageScrollPhysics()
會根據你的滑動來自動調節,比如你滑的距離小,則自動還原,
到達一定大小,也會自動下滑一個條目
複製程式碼
BouncingScrollPhysics()
頂底會有而外空間,讓富有彈性
複製程式碼
ClampingScrollPhysics()
和AlwaysScrollableScrollPhysics()
頂底會有藍色陰影
複製程式碼
另外還有
reverse屬性控制資料是否反序
,scrollDirection控制ListView的方向
結語
本文到此接近尾聲了,如果想快速嚐鮮Flutter,《Flutter七日》會是你的必備佳品;如果想細細探究它,那就跟隨我的腳步,完成一次Flutter之旅。
另外本人有一個Flutter微信交流群,歡迎小夥伴加入,共同探討Flutter的問題,本人微訊號:zdl1994328
,期待與你的交流與切磋。