一、基礎(chǔ)概念
Flutter中的彈框分為兩種類型:Dialog和BottomSheet。Dialog一般指具有模態(tài)的彈框,使用showDialog函數(shù)實(shí)現(xiàn);而B(niǎo)ottomSheet一般指非模態(tài)的彈框,使用showModalBottomSheet實(shí)現(xiàn)。除此之外,還有一些其他類型的彈框,如底部菜單彈框,選擇器彈框等,這里就不一一列舉了。
Flutter的彈框構(gòu)造器通常包括以下幾個(gè)部分:彈框主體Widget、背景遮罩Widget、動(dòng)畫控制器、彈框位置等等。接下來(lái)我們會(huì)重點(diǎn)講述這些部分的實(shí)現(xiàn)過(guò)程。
二、Dialog的實(shí)現(xiàn)
Dialog是模態(tài)的彈框類型,即彈框出現(xiàn)時(shí),背景被遮擋且無(wú)法操作。
1、彈框主體Widget
在Flutter中,Dialog通常由一個(gè)AlertDialog Widget或SimpleDialog Widget構(gòu)成。其中AlertDialog可以自定義其標(biāo)題、內(nèi)容和按鈕;而SimpleDialog只包含多個(gè)選項(xiàng)。
AlertDialog的構(gòu)建過(guò)程類似下面的代碼:
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("彈框標(biāo)題"),
content: Text("彈框內(nèi)容"),
actions: [
FlatButton(
child: Text("取消"),
onPressed: () {
Navigator.of(context).pop(); // 關(guān)閉彈框
},
),
FlatButton(
child: Text("確定"),
onPressed: () {
// 實(shí)現(xiàn)確認(rèn)邏輯
},
),
],
);
},
);
2、背景遮罩Widget
這里我們使用了showDialog函數(shù)來(lái)構(gòu)建Dialog,同時(shí)此函數(shù)還允許我們自定義彈框顯示時(shí)的遮罩背景。這可以通過(guò)指定barrierColor、barrierOpacity和barrierDismissible等屬性來(lái)實(shí)現(xiàn)。其中,barrierColor和barrierOpacity用于設(shè)置遮罩的顏色和透明度,而barrierDismissible則用于控制是否允許用戶點(diǎn)擊遮罩背景來(lái)關(guān)閉彈框(即點(diǎn)擊空白處關(guān)閉彈框)。
showDialog(
context: context,
barrierColor: Colors.black12, // 遮罩顏色
barrierOpacity: 0.5, // 遮罩透明度
barrierDismissible: false, // 是否允許點(diǎn)擊空白處關(guān)閉彈框
builder: (BuildContext context) {
return AlertDialog(
//...
);
},
);
3、動(dòng)畫控制器
想要Dialog具有更好的動(dòng)畫效果,我們可以使用Flutter內(nèi)置的動(dòng)畫庫(kù)Animations。通過(guò)AnimatedWidget或AnimatedBuilder等頂層Widget,我們可以方便地構(gòu)建各種動(dòng)畫效果。這里以FadeTransition為例,演示如何實(shí)現(xiàn)Dialog的淡入淡出效果。
class MyDialog extends StatefulWidget {
const MyDialog({Key key}) : super(key: key);
@override
_MyDialogState createState() => _MyDialogState();
}
class _MyDialogState extends State with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
_animation = CurvedAnimation(parent: _controller, curve: Curves.easeOut);
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return FadeTransition(
opacity: _animation,
child: Center(
child: AlertDialog(
title: Text("彈框標(biāo)題"),
content: Text("彈框內(nèi)容"),
actions: [
FlatButton(
child: Text("取消"),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: Text("確定"),
onPressed: () {
//...
},
),
],
),
),
);
}
}
void showMyDialog(BuildContext context) {
showDialog(
context: context,
builder: (BuildContext context) {
return MyDialog();
},
);
}
三、BottomSheet的實(shí)現(xiàn)
BottomSheet是非模態(tài)的彈框類型,即彈框出現(xiàn)時(shí),背景不被遮擋且可以進(jìn)行操作。BottomSheet有兩種類型:PersistentBottomSheet和ModalBottomSheet。其中,PersistentBottomSheet會(huì)一直保持顯示直至手動(dòng)關(guān)閉,而ModalBottomSheet會(huì)在點(diǎn)擊某個(gè)按鈕后彈出,在外部區(qū)域進(jìn)行操作時(shí)自動(dòng)隱藏。
1、彈框主體Widget
在Flutter中,BottomSheet的構(gòu)建器是builder屬性,它是個(gè)函數(shù)類型,返回一個(gè)Widget。另外我們也可以使用官方提供的BottomSheet Widget。與Dialog不同,BottomSheet不包含標(biāo)題和按鈕等元素,通常只包含一部分滾動(dòng)內(nèi)容。
ModalBottomSheet(
builder: (BuildContext context) {
return Container(
height: 200.0,
child: ListView(
children: [
ListTile(title: Text('選項(xiàng)1')),
ListTile(title: Text('選項(xiàng)2')),
ListTile(title: Text('選項(xiàng)3')),
ListTile(title: Text('選項(xiàng)4')),
],
),
);
},
);
2、背景遮罩Widget
與Dialog類似,BottomSheet也有相應(yīng)的遮罩背景設(shè)置屬性。但由于其本身不是模態(tài)的,所以不需要只讀化背景,也就意味著無(wú)需設(shè)置barrierDismissible屬性。
showModalBottomSheet(
context: context,
backgroundColor: Colors.black12, // 設(shè)置背景色
builder: (BuildContext context) {
return Container(
height: 200.0,
child: ListView(
children: [
//...
],
),
);
},
);
3、動(dòng)畫控制器
維護(hù)動(dòng)畫控制器實(shí)現(xiàn)BottomSheet的進(jìn)入和退出動(dòng)畫。
class MyBottomSheet extends StatefulWidget {
const MyBottomSheet({Key key}) : super(key: key);
@override
_MyBottomSheetState createState() => _MyBottomSheetState();
}
class _MyBottomSheetState extends State with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
_animation = CurvedAnimation(parent: _controller, curve: Curves.easeOut);
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SlideTransition(
position: Tween(
begin: Offset(0.0, 1.0),
end: Offset(0.0, 0.0),
).animate(_controller),
child: Container(
height: 200.0,
child: ListView(
children: [
//...
],
),
),
);
}
}
void showMyBottomSheet(BuildContext context) {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return MyBottomSheet();
},
);
}
四、結(jié)語(yǔ)
以上就是Flutter彈框的實(shí)現(xiàn)方案。除了這兩種類型外,還有很多其他類型的彈框類型,每種類型都有自己的實(shí)現(xiàn)方式。我們需要具體問(wèn)題具體分析,靈活運(yùn)用。在實(shí)踐中不斷探索,準(zhǔn)確地找到需要用到的彈框類型,并實(shí)現(xiàn)對(duì)應(yīng)的構(gòu)造器。