flutter开发实战-flutter实现类似iOS的Alert提示框与sheet菜单效果
在开发过程中,经常使用到提示框Dialog,与sheet,使用到了flutter的showDialog与showModalBottomSheet
我这里类似alert弹窗直接调用 flutter 中提供的showDialog()函数显示对话框。
我这里类似Sheet底部弹窗调用 flutter 中提供的showModalBottomSheet从屏幕下方弹出一个对话框。
效果图如下
一、定义dialog内容
定义dialog内容,实现标题、内容描述、按钮操作等功能。
class DialogItem {
String? title;
String? color;
DialogItem({this.title, this.color});
}
class ShowAlertDialog extends StatefulWidget {
const ShowAlertDialog({
Key? key,
this.contentAlign = TextAlign.left,
required this.onTap,
this.itemTitles,
this.content,
this.title,
this.children,
}) : super(key: key);
// 内容区域布局
final TextAlign contentAlign;
final String? title;
final String? content;
// 点击返回index 0 1
final Function onTap;
//按钮
final List<DialogItem>? itemTitles;
final List<Widget>? children;
State<ShowAlertDialog> createState() => _ShowAlertDialogState();
}
class _ShowAlertDialogState extends State<ShowAlertDialog> {
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
child: Center(
// ClipRRect 创建圆角矩形 要不然发现下边button不是圆角
child: ClipRRect(
borderRadius: BorderRadius.circular(4.0),
child: Container(
color: Color(0xFFFFFFFF),
width: 300,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(
height: 25.0,
),
_buildTitleWidget(),
SizedBox(
height: (_hasTitleAndContent() ? 10.0 : 0.0),
),
_buildContentWidget(),
SizedBox(height: 25),
Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Color(0xFFf5f5f5),
width: 1,
),
),
),
),
_buildItemWidget(),
],
),
),
),
),
);
}
bool _hasTitle() {
if (widget.title != null && widget.title!.isNotEmpty) {
return true;
}
return false;
}
bool _hasContent() {
if (widget.content != null && widget.content!.isNotEmpty) {
return true;
}
return false;
}
bool _hasTitleAndContent() {
if (_hasTitle() && _hasContent()) {
return true;
}
return false;
}
Widget _buildTitleWidget() {
bool aHasTitle = _hasTitle();
if (aHasTitle) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
"${widget.title}",
textAlign: TextAlign.center,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
fontStyle: FontStyle.normal,
color: Color(0xFF1A1A1A),
decoration: TextDecoration.none,
),
),
);
}
return Container();
}
Widget _buildContentWidget() {
bool aHasContent = _hasContent();
if (aHasContent) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
"${widget.content}",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
fontStyle: FontStyle.normal,
color: Color(0xFF333333),
decoration: TextDecoration.none,
),
),
);
}
return Container();
}
Widget _buildItemWidget() {
if (widget.children != null && widget.children!.isNotEmpty) {
return Container(
height: 44.0,
child: Row(
children: widget.children!.map((res) {
int index = widget.children!.indexOf(res);
return Expanded(
flex: 1,
child: GestureDetector(
onTap: () {
Navigator.pop(context);
widget.onTap(index);
},
child: Container(
height: 44,
alignment: Alignment.center,
child: res,
decoration: BoxDecoration(
border: Border(
right: BorderSide(
color: Color(0xFFF5F5F5),
width: 1,
),
),
),
),
),
);
}).toList(),
),
);
}
if (widget.itemTitles != null && widget.itemTitles!.isNotEmpty) {
return Container(
height: 44,
child: Row(
children: widget.itemTitles!.map((res) {
int index = widget.itemTitles!.indexOf(res);
return buildItem(res, index);
}).toList(),
),
);
}
return Container();
}
Widget buildItem(DialogItem item, int index) {
String? title = item.title;
String? color = item.color;
Color textColor = ColorUtil.hexColor(0x333333);
if (color != null && color.isNotEmpty) {
textColor = ColorUtil.hexColorString(color);
}
return Expanded(
flex: 1,
child: GestureDetector(
onTap: () {
Navigator.pop(context);
widget.onTap(index);
},
child: Container(
height: 44,
alignment: Alignment.center,
child: Text(
"${title}",
style: TextStyle(
color: textColor,
fontSize: 16,
),
),
decoration: BoxDecoration(
border: Border(
right: BorderSide(
color: Color(0xFFF5F5F5),
width: 1,
),
),
),
),
),
);
}
}
二、定义sheet底部弹窗内容
底部弹窗内容一般为选项,分为多个选项,最后为取消按钮。
class ShowSheetDialog extends StatefulWidget {
const ShowSheetDialog({
Key? key,
required this.onTap,
required this.items,
required this.title,
this.cancelTitle,
}) : super(key: key);
//按钮title
final List<DialogItem> items;
//点击事件回调 0开始
final Function onTap;
//标题 可选
final String title;
//取消 可选
final String? cancelTitle;
State<ShowSheetDialog> createState() => _ShowSheetDialogState();
}
class _ShowSheetDialogState extends State<ShowSheetDialog> {
Widget build(BuildContext context) {
double viewPaddingBottom = MediaQuery.of(context).viewPadding.bottom;
return ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8.0),
topRight: Radius.circular(8.0),
),
child: Container(
color: ColorUtil.hexColor(0xf7f7f7),
padding: EdgeInsets.only(bottom: viewPaddingBottom),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
_itemTitle(context),
Column(
mainAxisSize: MainAxisSize.min,
children: widget.items.map((item) {
int index = widget.items.indexOf(item);
return GestureDetector(
onTap: () {
Navigator.pop(context);
if (widget.onTap != null) {
widget.onTap(index);
}
},
child: _itemCreate(item),
);
}).toList(),
),
GestureDetector(
child: Padding(
padding: EdgeInsets.only(top: 10),
child: _itemCreate(DialogItem(
title: widget.cancelTitle ?? "取消", color: "333333")),
),
onTap: () {
Navigator.pop(context);
},
)
],
),
),
);
}
Widget _itemTitle(BuildContext context) {
//有标题的情况下
if (widget.title != null && widget.title.isNotEmpty) {
return Container(
alignment: Alignment.center,
width: MediaQuery.of(context).size.width,
height: 40,
child: Text(
"${widget.title}",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
fontStyle: FontStyle.normal,
color: ColorUtil.hexColor(0x777777),
decoration: TextDecoration.none,
),
),
);
}
return Container();
}
Widget _itemCreate(DialogItem item) {
String title = item.title ?? "";
String? color = item.color;
Color textColor = ColorUtil.hexColor(0x333333);
if (color != null && color.isNotEmpty) {
textColor = ColorUtil.hexColorString(color);
}
return Container(
height: 50,
width: MediaQuery.of(context).size.width,
child: Center(
child: Text(
"${title}",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
fontStyle: FontStyle.normal,
color: textColor,
decoration: TextDecoration.none,
),
),
),
decoration: BoxDecoration(
color: Colors.white,
border: Border(
top: BorderSide(
color: ColorUtil.hexColor(0xf7f7f7),
width: 1.0,
),
),
),
);
}
}
三、调用flutter的showDialog方法弹窗
showDialog方法弹窗
/// 显示对话框的内容
class ShowDialogContent {
String? title;
String? message;
String? ok;
String? cancel;
String? okColor;
String? cancelColor;
ShowDialogContent({this.title, this.message, this.ok, this.cancel});
ShowDialogContent.fromJson(Map<String, dynamic> json) {
title = json['title'];
message = json['message'];
ok = json['ok'];
okColor = json['okColor'];
cancel = json['cancel'];
cancelColor = json['cancelColor'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['title'] = this.title;
data['message'] = this.message;
data['ok'] = this.ok;
data['okColor'] = this.okColor;
data['cancel'] = this.cancel;
data['cancelColor'] = this.cancelColor;
return data;
}
}
class DialogUtil {
//显示中间弹窗
static void popDialog(BuildContext context, Widget widget) {
showDialog(
context: context,
barrierDismissible: true,
builder: (ctx) {
return widget;
});
}
//返回上一级
static void pop(BuildContext context) {
Navigator.pop(context);
}
// 显示处理弹出框
static void showAlertDialog(
{required ShowDialogContent dialogContent, OnDialogCallback? callback}) {
DialogUtil.popDialog(
OneContext().context!,
ShowAlertDialog(
title: dialogContent.title,
content: dialogContent.message,
onTap: (int index) {
Map result = Map();
result["index"] = index;
if (callback != null) {
callback(result);
}
},
children: [
Text(
dialogContent.cancel ?? "",
textAlign: TextAlign.center,
softWrap: true,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
fontStyle: FontStyle.normal,
color: dialogContent.cancelColor != null
? ColorUtil.hexColorString(dialogContent.cancelColor!)
: ColorUtil.hexColorString("333333"),
decoration: TextDecoration.none,
),
),
Text(
dialogContent.ok ?? "",
textAlign: TextAlign.center,
softWrap: true,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
fontStyle: FontStyle.normal,
color: dialogContent.okColor != null
? ColorUtil.hexColorString(dialogContent.okColor!)
: ColorUtil.hexColorString("333333"),
decoration: TextDecoration.none,
),
),
],
),
);
}
}
四、调用flutter的bottomSheetDialog方法底部弹窗
class DialogUtil {
//显示底部弹窗
static void bottomSheetDialog(
BuildContext context,
Widget widget, {
bool? isScrollControlled,
bool? enableDrag,
Color? backgroundColor,
}) {
showModalBottomSheet(
context: context,
isScrollControlled: isScrollControlled ?? true,
enableDrag: enableDrag ?? true,
backgroundColor: backgroundColor ?? Colors.white,
builder: (ctx) {
return widget;
},
);
}
// 显示处理chekbox
static void showCheckBox(
{required String title,
required List<Map> optionList,
String? cancelTitle,
OnDialogCallback? callback}) {
// {"title":"标题","titleColor":"FFFFFF"}
List<DialogItem> items = [];
for (Map map in optionList) {
String? title = map["title"];
String? titleColor = map["titleColor"];
DialogItem item = DialogItem(title: title, color: titleColor);
items.add(item);
}
DialogUtil.bottomSheetDialog(
OneContext().context!,
ShowSheetDialog(
onTap: (int index) {
Map result = Map();
result["index"] = index;
if (callback != null) {
callback(result);
}
},
items: items,
title: title,
cancelTitle: cancelTitle,
),
enableDrag: false,
backgroundColor: Colors.transparent,
);
}
}
五、小结
flutter开发实战-flutter实现类似iOS的Alert提示框与sheet菜单效果,使用到了flutter的showDialog与showModalBottomSheet来显示对话框与屏幕下方弹出一个对话框功能。
学习记录,每天不停进步。