AlertDialog
先来看一下AlertDialog的构造方法
AlertDialog({
Key key,
///标题
this.title,
///标题padding
this.titlePadding,
///标题style
this.titleTextStyle,
///内容
this.content,
///padding
this.contentPadding = const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0),
///style
this.contentTextStyle,
/// 动作 widget 列表,
this.actions,
this.actionsPadding = EdgeInsets.zero,
///action 溢出 方向
this.actionsOverflowDirection,
/// action 溢出水平间距离
this.actionsOverflowButtonSpacing,
///按钮padding
this.buttonPadding,
///背景色
this.backgroundColor,
///阴影
this.elevation,
this.semanticLabel,
this.insetPadding = _defaultInsetPadding,
this.clipBehavior = Clip.none,
this.shape,
///不可滚动,想要滚动需要嵌套SingleChildScrollView
this.scrollable = false,
})
再来看一下创建dialog的方法
Future showDialog({
@required BuildContext context,
WidgetBuilder builder,
///点击外部是否消失
bool barrierDismissible = true,
///外部阴影颜色
Color barrierColor,
///是否使用安全区域
bool useSafeArea = true,
bool useRootNavigator = true,
RouteSettings routeSettings,
@Deprecated(
'Instead of using the "child" argument, return the child from a closure '
'provided to the "builder" argument. This will ensure that the BuildContext '
'is appropriate for widgets built in the dialog. '
'This feature was deprecated after v0.2.3.'
)
Widget child,
})
例子
Future _getAlertDialog(BuildContext context) {
return showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Title'),
///如果内容过长,需要使用SingleChildScrollView
content: Scrollbar(
child: SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Text(
"这个是内容...这个是内容...这个是内容...这个是内容...这个是内容...这个是内容...这个是内容...这个是内容..." *
20),
),
),
actions: [
FlatButton(
child: Text('取消'),
onPressed: () => Navigator.of(context).pop(0),
),
FlatButton(
child: Text('确定'),
onPressed: () => Navigator.of(context).pop(1),
)
],
),
///点击dialog 外部是否消失
barrierDismissible: true);
}
Future showDialog
showDialog
这里唯一需要注意的是关闭dialog 方法, Navigator.of(context).pop({Object result}),这个和路由管理关闭页面的方式是一样的
Future showDialog({
@required BuildContext context,
WidgetBuilder builder,
bool barrierDismissible = true,
Color barrierColor,
bool useSafeArea = true,
bool useRootNavigator = true,
RouteSettings routeSettings,
@Deprecated(
'Instead of using the "child" argument, return the child from a closure '
'provided to the "builder" argument. This will ensure that the BuildContext '
'is appropriate for widgets built in the dialog. '
'This feature was deprecated after v0.2.3.'
)
Widget child,
}) {
assert(child == null || builder == null);
assert(barrierDismissible != null);
assert(useSafeArea != null);
assert(useRootNavigator != null);
assert(debugCheckHasMaterialLocalizations(context));
final ThemeData theme = Theme.of(context, shadowThemeOnly: true);
return showGeneralDialog(
context: context,
pageBuilder: (BuildContext buildContext, Animation animation, Animation secondaryAnimation) {
final Widget pageChild = child ?? Builder(builder: builder);
Widget dialog = Builder(
builder: (BuildContext context) {
return theme != null
? Theme(data: theme, child: pageChild)
: pageChild;
}
);
if (useSafeArea) {
dialog = SafeArea(child: dialog);
}
return dialog;
},
barrierDismissible: barrierDismissible,
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
barrierColor: barrierColor ?? Colors.black54,
transitionDuration: const Duration(milliseconds: 150),
transitionBuilder: _buildMaterialDialogTransitions,
useRootNavigator: useRootNavigator,
routeSettings: routeSettings,
);
}
看了一下showDialog 的源码,是对showGeneralDialog这个方法进行了包装,发现了如果使用了MaterialAppTheme 则该dialog使用 MaterialAppTheme , 其他的都是固定封装,
Future showGeneralDialog
Future showGeneralDialog({
@required BuildContext context,
@required RoutePageBuilder pageBuilder,
bool barrierDismissible,
String barrierLabel,
Color barrierColor,
Duration transitionDuration,
RouteTransitionsBuilder transitionBuilder,
bool useRootNavigator = true,
RouteSettings routeSettings,
}) {
assert(pageBuilder != null);
assert(useRootNavigator != null);
assert(!barrierDismissible || barrierLabel != null);
return Navigator.of(context, rootNavigator: useRootNavigator).push(_DialogRoute(
pageBuilder: pageBuilder,
barrierDismissible: barrierDismissible,
barrierLabel: barrierLabel,
barrierColor: barrierColor,
transitionDuration: transitionDuration,
transitionBuilder: transitionBuilder,
settings: routeSettings,
));
}
而 showGeneralDialog 则是使用 Navigator.push了一个 _DialogRoute ,这里也就解释了关闭dialog 为什么是
Navigator.pop的方法了,
这个例子我为了演示嵌套滑动的效果,我使用SingleChildScrollView
但是我觉得现在大部分dialog的设计应该是这样的
这种方式如果使用actions 和content 来写的话,想到什么办法,只能利用content 来写所有的布局
这是我利用content写出来的结果,
Future _getAlertDialog(BuildContext context) {
return showDialog(
context: context,
builder: (context) => AlertDialog(
// title: Container(
// alignment: Alignment.center,
// child: Text('Title'),
// ),
/// 修改内容边距
contentPadding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
/// 修改圆角大小
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12))),
///如果内容过长,需要使用 SingleChildScrollView
content: SizedBox(
height: 180,
child: Column(
children: [
SizedBox(
height: 120,
///包裹内容,为了使滑动开始位置是从距离30的位置开始
child: Padding(
padding: const EdgeInsets.fromLTRB(20, 30, 20, 10),
child: Scrollbar(
child: SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Text(
"这个是内容...这个是内容...这个是内容...这个是内容...这个是内容...这个是内容...这个是内容...这个是内容..." *
20),
),
),
)
),
/// 水平横线
Divider(
height: 10,
),
/// 为了均分按钮 ,
Row(
children: [
Expanded(
flex: 1,
child: FlatButton(
child: Text(
'取消',
style:
TextStyle(color: Colors.black87, fontSize: 18),
),
onPressed: () => Navigator.of(context).pop(0),
),
),
SizedBox(
height: 30,
child:VerticalDivider(
width:10,
),
),
Expanded(
flex: 1,
child: FlatButton(
child: Text(
'确定',
style: TextStyle(
color: Colors.blueAccent, fontSize: 18),
),
onPressed: () => Navigator.of(context).pop(1),
),
)
],
),
],
),
),
),
///点击dialog 外部是否消失
barrierDismissible: true);
}
其实这中类似ios样式的控件Flutter在设计之初也想到了,在Cupertino 组件中就有CupertinoDialog ,不过到了现在版本已经被放弃了,使用CupertinoAlertDialog替代了CupertinoDialog 使用方式和dialog是一致的,说实话android机器上显示的这个dialog 确实和ios的效果没啥差别,官方合适很良心的
例子
getCupertinoAlertDialog(BuildContext context) {
return showDialog(context: context,builder: (con){
return CupertinoAlertDialog(
title: Text("This is Title"),
content: Text('This is content'*10),
actions: [
FlatButton(
onPressed: (){
Navigator.of(context).pop(0);
},
child: Text('取消'),
),
FlatButton(
onPressed: (){
Navigator.of(context).pop(0);
},
child: Text('中间的'),
),
FlatButton(
onPressed: (){
Navigator.of(context).pop(1);
},
child: Text('确定'),
),
],
);
});
}
效果图
SimpleDialog
再开看一下他的构造方法
SimpleDialog({
Key key,
this.title,
this.titlePadding = const EdgeInsets.fromLTRB(24.0, 24.0, 24.0, 0.0),
this.titleTextStyle,
this.children,
this.contentPadding = const EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 16.0),
this.backgroundColor,
this.elevation,
this.semanticLabel,
this.shape,
})
例子
_getSimpleDialog(BuildContext context) async {
var lan = await showDialog(
context: context,
builder: (context) => SimpleDialog(
title: Text('Title'),
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: SimpleDialogOption(
child: Text('这个是第一个'),
onPressed: () => Navigator.of(context).pop(0),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: SimpleDialogOption(
child: Text('这个是第二个'),
onPressed: () => Navigator.of(context).pop(1),
),
)
],
),
);
printString('language is ${lan}');
}
效果图
这里的子条目使用的是SimpleDialogOption ,他相当如一个button
showModalBottomSheet
在介绍Scaffold 的时候介绍过showModalBottomSheet 创建一个类似bottomsheetdialog,
这里写一个日期选择
先来样式
void selectDate(BuildContext context) async {
DateTime dateTime = DateTime.now();
var date = await showModalBottomSheet(
context: context,
builder: (context) => Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
Expanded(
flex: 1,
child: GestureDetector(
onTap: () {
Navigator.of(context).pop();
},
child: Padding(
padding: const EdgeInsets.all(19),
child: Text('取消'),
),
)),
Expanded(
flex: 1,
child: GestureDetector(
onTap: () {
Navigator.of(context).pop(dateTime);
},
child: Padding(
padding: const EdgeInsets.all(10),
child: Text('确定', textDirection: TextDirection.rtl),
),
)),
],
),
SizedBox(
height: 200,
child: CupertinoDatePicker(
onDateTimeChanged: (date) {
dateTime = date;
},
mode: CupertinoDatePickerMode.date,
maximumDate: DateTime.now().add(Duration(days: 20)),
minimumDate: DateTime.now().add(Duration(days: -20)),
),
)
],
),
);
printString('date:${date ?? ''}');
}
我学习flutter的整个过程都记录在里面了
https://www.jianshu.com/c/36554cb4c804
最后附上demo 地址
https://github.com/tsm19911014/tsm_flutter