【Flutter】功能型组件之对话框详解
前言
对话框本质上也是UI布局,通常一个对话框会包含标题、内容,以及一些操作按钮,为此,Material库中提供了一些现成的对话框组件来用于快速的构建出一个完整的对话框。
接口描述
// 1. AlertDialog
const AlertDialog({
Key key,
// 对话框组件标题
this.title,
// 标题填充
this.titlePadding,
// 标题文本样式
this.titleTextStyle,
// 对话框内容组件
this.content,
// 内容的填充
this.contentPadding = const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0),
// 内容文本样式
this.contentTextStyle,
// 对话框操作按钮组
this.actions,
// 对话框背景色
this.backgroundColor,
// 对话框的阴影
this.elevation,
// 对话框语义化标签(用于读屏软件)
this.semanticLabel,
// 对话框外形
this.shape,
}) : assert(contentPadding != null),
super(key: key);
代码示例
// 对话框详解(dialog)
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
// 1. AlertDialog,消息对话框
Future<bool> showDeleteConfirmDialog(context){
return showDialog<bool>(
context: context,
//点击对话框barrier(遮罩)时是否关闭它
barrierDismissible: false,
builder: (context){
return AlertDialog(
title: Text("提示"),
content: Text("您确定要删除当前文件吗?"),
actions: <Widget>[
FlatButton(
child: Text("取消"),
// 关闭对话框
onPressed: () => Navigator.of(context).pop(),
),
FlatButton(
child: Text("确定"),
onPressed: (){
// 关闭对话框并返回true
Navigator.of(context).pop(true);
},
),
],
);
}
);
}
// 2. SimpleDialog,列表对话框
Future<void> changeLanguageDialog(context) async{
int i = await showDialog<int>(
context: context,
builder: (context){
return SimpleDialog(
title: const Text("请选择语言"),
children: <Widget>[
SimpleDialogOption(
onPressed: (){
// 返回1
Navigator.pop(context, 1);
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 0),
child: const Text("中文简体"),
),
),
SimpleDialogOption(
onPressed: (){
// 返回2
Navigator.pop(context, 2);
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: const Text("美国英语"),
),
),
],
);
}
);
if(i != null){
print("选择了:${i == 1 ? "中文简体" : "美国英语"}");
}
}
// 3. Dialog,对话框
Future<void> showListDialog(context) async{
int index = await showDialog(
context: context,
builder: (context){
var child = Column(
children: <Widget>[
ListTile(title: Text("请选择"),),
Expanded(
child: ListView.builder(
itemCount: 30,
itemBuilder: (BuildContext context, int index){
return ListTile(
title: Text("$index"),
onTap: () => Navigator.of(context).pop(index),
);
},
),
)
],
);
//使用AlertDialog会报错
// 实际上AlertDialog和SimpleDialog都使用了Dialog类。由于AlertDialog和SimpleDialog中使用了IntrinsicWidth来尝试通过子组件的实际尺寸来调整自身尺寸,
// 这就导致他们的子组件不能是延迟加载模型的组件(如ListView、GridView 、 CustomScrollView等)
// return AlertDialog(content: child);
return Dialog(child: child);
}
);
if (index != null) {
print("点击了:$index");
}
}
// 4. showGeneralDialog,自定义非Material风格对话框
Future<T> customDialog<T>({
@required BuildContext context,
bool barrierDismissible = true,
WidgetBuilder builder,
}){
final ThemeData theme = Theme.of(context, shadowThemeOnly: true);
//
return showGeneralDialog(
context: context,
pageBuilder: (BuildContext buildContext, Animation<double> animation, Animation<double> secondaryAnimation){
final Widget pageChild = Builder(builder: builder,);
return SafeArea(
child: Builder(builder: (BuildContext context){
return theme != null
? Theme(data: theme, child: pageChild)
: pageChild;
}),
);
},
barrierDismissible: barrierDismissible,
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
// 自定义遮罩颜色
barrierColor: Colors.black87,
transitionDuration: const Duration(milliseconds: 150),
transitionBuilder: _buildMaterialDialogTransitions,
);
}
Widget _buildMaterialDialogTransitions(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child){
// 使用缩放动画
return ScaleTransition(
scale: CurvedAnimation(
parent: animation,
curve: Curves.easeOut,
),
child: child,
);
}
Future<bool> showCustomDialog(context){
return customDialog<bool>(
context: context,
//点击对话框barrier(遮罩)时是否关闭它
barrierDismissible: false,
builder: (context){
return AlertDialog(
title: Text("提示"),
content: Text("您确定要删除当前文件吗?"),
actions: <Widget>[
FlatButton(
child: Text("取消"),
// 关闭对话框
onPressed: () => Navigator.of(context).pop(),
),
FlatButton(
child: Text("确定"),
onPressed: (){
// 关闭对话框并返回true
Navigator.of(context).pop(true);
},
),
],
);
}
);
}
// 5. 对话框状态管理
Future<bool> showDeleteConfirmDialog1(context) {
bool _withTree = false;
return showDialog<bool>(
context: context,
builder: (context){
return AlertDialog(
title: Text("提示"),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text("您确定要删除当前文件吗?"),
Row(
children: <Widget>[
Text("同时删除子目录"),
// Checkbox(
// // 使用Checkbox组件
// value: _withTree,
// onChanged: (bool value){
// // 此时context为对话框UI的根Element,我们
// // 直接将对话框UI对应的Element标记为dirty
// (context as Element).markNeedsBuild();
// _withTree = !_withTree;
// },
// )
// 通过Builder来获得构建Checkbox的`context`,
// 这是一种常用的缩小`context`范围的方式
Builder(
builder: (BuildContext context) {
return Checkbox(
value: _withTree,
onChanged: (bool value) {
(context as Element).markNeedsBuild();
_withTree = !_withTree;
},
);
},
),
],
)
],
),
actions: <Widget>[
FlatButton(
child: Text("取消"),
onPressed: () => Navigator.of(context).pop(),
),
FlatButton(
child: Text("删除"),
onPressed: (){
// 执行删除操作
Navigator.of(context).pop(_withTree);
},
)
],
);
}
);
}
// 6. 底部菜单列表
Future<int> _showModalBottomSheet(context){
return showModalBottomSheet<int>(
context: context,
builder: (BuildContext context){
return ListView.builder(
itemCount: 30,
itemBuilder: (BuildContext context, int index){
return ListTile(
title: Text("$index"),
onTap: () => Navigator.of(context).pop(index),
);
}
);
}
);
}
// 7. 全屏菜单列表
PersistentBottomSheetController<int> _showBottomSheet(context){
return showBottomSheet<int>(
context: context,
builder: (BuildContext context){
return ListView.builder(
itemCount: 30,
itemBuilder: (BuildContext context, int index){
return ListTile(
title: Text("$index"),
onTap: (){
print("$index");
Navigator.of(context).pop();
}
);
}
);
}
);
}
// 8. Loading框,通过showDialog+AlertDialog实现
showLoadingDialog(context) {
showDialog(
context: context,
// 点击遮罩不关闭对话框
barrierDismissible: false,
builder: (context) {
return AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
CircularProgressIndicator(),
Padding(
padding: const EdgeInsets.only(top: 26),
child: Text("正在加载,请稍后..."),
)
],
),
);
}
);
}
// 9. 自定义对话框长度
// 只使用SizedBox或ConstrainedBox是不行的,原因是showDialog中已经给对话框设置了宽度限制,可以使用UnconstrainedBox先抵消showDialog对宽度的限制,然后再使用SizedBox指定宽度。
showCustomLoadingDialog(context) {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return UnconstrainedBox(
constrainedAxis: Axis.vertical,
child: SizedBox(
width: 280,
child: AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
CircularProgressIndicator(),
Padding(
padding: const EdgeInsets.only(top: 26.0),
child: Text("正在加载,请稍后..."),
)
],
),
),
),
);
}
);
}
// 10. Material风格的日历选择器
Future<DateTime> _showDatePicker(context) {
var date = DateTime.now();
return showDatePicker(
context: context,
initialDate: date,
firstDate: date,
lastDate: date.add(
// 未来30天可选
Duration(days: 30),
)
);
}
// 11. iOS风格的日历选择器
Future<DateTime> _showDatePicker2(context) {
var date = DateTime.now();
return showCupertinoModalPopup(
context: context,
builder: (ctx) {
return SizedBox(
height: 200,
child: CupertinoDatePicker(
mode: CupertinoDatePickerMode.dateAndTime,
minimumDate: date,
maximumDate: date.add(
Duration(days: 30),
),
maximumYear: date.year + 1,
onDateTimeChanged: (DateTime value) {
print(value);
},
),
);
},
);
}
class DialogWidgetRoute extends StatefulWidget{
_DialogWidgetRouteState createState() => _DialogWidgetRouteState();
}
class _DialogWidgetRouteState extends State<DialogWidgetRoute>{
@override
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(
title: Text("对话框"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// AlertDialog
FlatButton(
color: Colors.green,
highlightColor: Colors.green,
splashColor: Colors.red,
child: Text("AlertDialog"),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
onPressed: () async{
// 弹出对话框并等待其关闭
bool delete = await showDeleteConfirmDialog(context);
if(delete == null){
print("取消删除");
} else{
print("确认删除");
}
},
),
// SimpleDialog
FlatButton(
color: Colors.green,
highlightColor: Colors.green,
splashColor: Colors.red,
child: Text("SimpleDialog"),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
onPressed: () async{
changeLanguageDialog(context);
},
),
// Dialog
FlatButton(
color: Colors.green,
highlightColor: Colors.green,
splashColor: Colors.red,
child: Text("Dialog"),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
onPressed: () async{
showListDialog(context);
},
),
// customDialog
FlatButton(
color: Colors.green,
highlightColor: Colors.green,
splashColor: Colors.red,
child: Text("customDialog"),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
onPressed: () async{
// 弹出对话框并等待其关闭
bool delete = await showCustomDialog(context);
if(delete == null){
print("自定义取消删除");
} else{
print("自定义确认删除");
}
},
),
// 对话框状态管理
FlatButton(
color: Colors.green,
highlightColor: Colors.green,
splashColor: Colors.red,
child: Text("对话框状态管理"),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
onPressed: () async{
// 弹出对话框并等待其关闭
bool delete = await showDeleteConfirmDialog1(context);
if(delete == null){
print("取消删除");
} else{
print("确认删除");
}
},
),
// 底部菜单列表
RaisedButton(
child: Text("底部菜单列表"),
onPressed: () async{
int type = await _showModalBottomSheet(context);
print(type);
},
),
// Loading框
RaisedButton(
child: Text("Loading框"),
onPressed: () async{
showLoadingDialog(context);
},
),
// 自定义对话框长度
RaisedButton(
child: Text("自定义对话框长度"),
onPressed: () async{
showCustomLoadingDialog(context);
},
),
// Material风格的日历选择器
RaisedButton(
child: Text("Material风格的日历选择器"),
onPressed: () async{
_showDatePicker(context);
},
),
// iOS风格的日历选择器
RaisedButton(
child: Text("iOS风格的日历选择器"),
onPressed: () async{
_showDatePicker2(context);
},
),
],
),
),
);
}
}
总结
对话框最终都是由showGeneralDialog方法打开的,直接调用Navigator的push方法打开了一个新的对话框路由_DialogRoute,然后返回了push的返回值。可见对话框实际上正是通过路由的形式实现的,这也是为什么我们可以使用Navigator的pop 方法来退出对话框的原因。
【Flutter】功能型组件之对话框详解的更多相关文章
- vc中调用Com组件的方法详解
vc中调用Com组件的方法详解 转载自:网络,来源未知,如有知晓者请告知我.需求:1.创建myCom.dll,该COM只有一个组件,两个接口: IGetRes--方法Hello(), IGet ...
- iOS 组件化流程详解(git创建流程)
[链接]组件化流程详解(一)https://www.jianshu.com/p/2deca619ff7e
- React—组件生命周期详解
React—组件生命周期详解 转自 明明的博客 http://blog.csdn.net/slandove/article/details/50748473 (非原创) 版权声明:转载请注明出处,欢 ...
- Vue组件通信方式全面详解
vue组件通信方式全面详解 众所周知,Vue主要思想就是组件化开发.因为,在实际的项目开发中,肯定会以组件的开发模式进行.形如页面和页面之间需要通信一样,Vue 组件和组件之间肯定也需要互通有无.共享 ...
- Kubernetes学习之路(二十)之K8S组件运行原理详解总结
目录 一.看图说K8S 二.K8S的概念和术语 三.K8S集群组件 1.Master组件 2.Node组件 3.核心附件 四.K8S的网络模型 五.Kubernetes的核心对象详解 1.Pod资源对 ...
- 【转载】BootStrap表格组件bootstrap table详解
(转载,来源“脚本之家”,作者不详) 一.Bootstrap Table的引入 关于Bootstrap Table的引入,一般来说还是两种方法: 1.直接下载源码,添加到项目里面来.由于Bootstr ...
- 中间件:ElasticSearch组件RestHighLevelClient用法详解
本文源码:GitHub·点这里 || GitEE·点这里 一.基础API简介 1.RestHighLevelClient RestHighLevelClient的API作为ElasticSearch备 ...
- Vue基础语法-数据绑定、事件处理和扩展组件等知识详解(案例分析,简单易懂,附源码)
前言: 本篇文章主要讲解了Vue实例对象的创建.常用内置指令的使用.自定义组件的创建.生命周期(钩子函数)等.以及个人的心得体会,汇集成本篇文章,作为自己对Vue基础知识入门级的总结与笔记. 其中介绍 ...
- 【Android】事件总线(解耦组件) EventBus 详解
当Android项目越来越庞大的时候,应用的各个部件之间的通信变得越来越复杂,例如:当某一条件发生时,应用中有几个部件对这个消息感兴趣,那么我们通常采用的就是观察者模式,使用观察者模式有一个弊病就是部 ...
随机推荐
- 算法—— n个骰子的点数
把n个骰子扔在地上,所有骰子朝上一面的点数之和为s.输入n,打印出s的所有可能的值出现的概率. 你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个 ...
- 苹果M1芯片各种不支持,但居然可以刷朋友圈!你会买单吗?
上个月和大家一起分享过,最新的苹果M1芯片上支持的各种开源软件.什么?还没读过?赶紧点这里:一文解读苹果 M1 芯片电脑上的开源软件. 现在已经过去了半个月,想必有不少的同学都已经入手了最新的苹果M1 ...
- 自搭建jetbrains系列ide授权服务器
1.下载 LicenseServer 地址:https://mega.nz/#!7B5UVY6b!Hae2ceTBPIrTowQN0sV9fQ5lGOKzGxas2ug02RZAdGU,里面有不同的服 ...
- python程序的三种控制结构
程序的三种控制结构 什么是控制结构? """ 程序有三种基本结构组成:顺序结构.分支结构.循环结构.任何程序都是由这三种基本结构组成. 顺序结构是程序按照线性顺序依次执行 ...
- 从零开始了解多线程 之 深入浅出AQS -- 上
java锁&AQS深入浅出学习--上 上一篇文章中我们一起学习了jvm缓存一致性.多线程间的原子性.有序性.指令重排的相关内容, 这一篇文章便开始和大家一起学习学习AQS(AbstractQu ...
- 参数文件恢复:RMAN-0617
RMAN> restore spfile from autobackup; Starting restore at 03-APR-19using channel ORA_DISK_1using ...
- PHP比较数组、对象是否为空
PHP简单对比对象.数组是不是为空: 1 <?php 2 /*简单的比较对象和数组是不是为空*/ 3 4 #定义空类EmptyClass 5 class EmptyClass{} 6 7 $em ...
- winform判断程序是否运行,且只能运行一个实例
前言 判断程序是否已经运行,使程序只能运行一个实例有很多方法,下面记录两种. 目前使用的是第一种方法. 方法1:线程互斥 static class Program { private static S ...
- Autofac官方文档翻译--二、解析服务--2隐式关系类型
Autofac 隐式关系类型 Autofac 支持自动解析特定类型,隐式支持组件与服务间的特殊关系.要充分利用这些关系,只需正常注册你的组件,但是在使用服务的组件或调用Resolve()进行类型解析时 ...
- 解决UE4缓存使C盘膨胀的问题
使用UE4的时候会发现C盘越来越小了,那是因为UE4引擎的缓存文件默认保存在C盘的缘故. 概述 一.出现的问题:UE4的缓存文件会导致C盘膨胀. 二.解决的方式:请严格按照下列步骤来执行.1. 更改U ...