Flutter之Dialog使用和踩坑
简单介绍
最近使用了Flutter的展示对话框的功能,踩了一点坑,顺便做下总结,方便各位以后少踩坑,如果有说错的地方,还请大家指出来。
下面将介绍对话框的几种场景和踩坑。
- 展示普通对话框
- 展示包含列表项的对话框
- 对话框界面需要动态刷新
- 自定义对话框。
先理解一些东西
对话框本质上是属于一个路由的页面route,由Navigator进行管理,所以控制对话框的显示和隐藏,也是调用Navigator.of(context)的push和pop方法。
在Flutter中,对话框会有两种风格,调用showDialog()方法展示的是material风格的对话框,调用showCupertinoDialog()方法展示的是ios风格的对话框。 而这两个方法其实都会去调用showGeneralDialog()方法,可以从源码中看到最后是利用Navigator.of(context, rootNavigator: true).push()一个页面。
基本要传的参数:context上下文,builder用于创建显示的widget,barrierDismissible可以控制点击对话框以外的区域是否隐藏对话框。
- 你会注意到,showDialog()方法返回的是一个Future对象,可以通过这个future对象来获取对话框所传递的数据。 比如我们想知道想知道用户是点击了对话框的确认按钮还是取消按钮,那就在退出对话框的时候,利用Navigator.of(context).pop("一些数据");
Future<T> showCupertinoDialog<T>({@required BuildContext context,@required WidgetBuilder builder,
});Future<T> showDialog<T>({@required BuildContext context,bool barrierDismissible = true,WidgetBuilder builder,
})Future<T> showGeneralDialog<T>({@required BuildContext context,@required RoutePageBuilder pageBuilder,bool barrierDismissible,String barrierLabel,Color barrierColor,Duration transitionDuration,RouteTransitionsBuilder transitionBuilder,
}) {assert(pageBuilder != null);assert(!barrierDismissible || barrierLabel != null);return Navigator.of(context, rootNavigator: true).push<T>(_DialogRoute<T>(pageBuilder: pageBuilder,barrierDismissible: barrierDismissible,barrierLabel: barrierLabel,barrierColor: barrierColor,transitionDuration: transitionDuration,transitionBuilder: transitionBuilder,));
}
复制代码
简单的显示对话框
Flutter中的Dialog主要是SimpleDialog和AlertDialog。
- SimpleDialog,一般可以利用多个SimpleDialogOption为用户提供了几个选项。
- AlertDialog,警告对话框。警告对话框有一个可选标题title和一个可选列表的actions选项。
展示一个简单的SimpleDialog,代码如下:
void showMySimpleDialog(BuildContext context) {showDialog(context: context,builder: (context) {return new SimpleDialog(title: new Text("SimpleDialog"),children: <Widget>[new SimpleDialogOption(child: new Text("SimpleDialogOption One"),onPressed: () {Navigator.of(context).pop("SimpleDialogOption One");},),new SimpleDialogOption(child: new Text("SimpleDialogOption Two"),onPressed: () {Navigator.of(context).pop("SimpleDialogOption Two");},),new SimpleDialogOption(child: new Text("SimpleDialogOption Three"),onPressed: () {Navigator.of(context).pop("SimpleDialogOption Three");},),],);});}
复制代码
展示一个简单的Material风格的AlertDialog,代码如下:
void showMyMaterialDialog(BuildContext context) {showDialog(context: context,builder: (context) {return new AlertDialog(title: new Text("title"),content: new Text("内容内容内容内容内容内容内容内容内容内容内容"),actions: <Widget>[new FlatButton(onPressed: () {Navigator.of(context).pop();},child: new Text("确认"),),new FlatButton(onPressed: () {Navigator.of(context).pop();},child: new Text("取消"),),],);});}
复制代码
展示一个简单的IOS风格的AlertDialog,代码如下:
void showMyCupertinoDialog(BuildContext context) {showCupertinoDialog(context: context,builder: (context) {return new CupertinoAlertDialog(title: new Text("title"),content: new Text("内容内容内容内容内容内容内容内容内容内容内容"),actions: <Widget>[new FlatButton(onPressed: () {Navigator.of(context).pop("点击了确定");},child: new Text("确认"),),new FlatButton(onPressed: () {Navigator.of(context).pop("点击了取消");},child: new Text("取消"),),],);});}
复制代码
展示列表项对话框(踩坑了)
构造对话框的时候,我们都需要传一个content对象,来构造对话框的主要内容。一般情况下,如果content里面只是简单的一些内容,那问题不大,可以正常显示。 但是有时候,我们需要展示一个列表对话框。这个时候,如果列表项比较多,就会出现一些问题。
- 使用Column+SingleChildScrollView来显示列表对话框。
当Column的列表项数据比较多的时候,屏幕已经放不了,就会出现overflow错误了,所以这个时候需要在外部嵌套一个SingleChildScrollView控件,使内部child控件可以滚动, 不会出现overflow错误。(哈哈,可以使用下面的代码跑一跑,然后去掉SingleChildScrollView,对比运行结果)
void showMyDialogWithColumn(BuildContext context) {showDialog(context: context,builder: (context) {return new AlertDialog(title: new Text("title"),content: new SingleChildScrollView(child: new Column(children: <Widget>[new SizedBox(height: 100,child: new Text("1"),),new SizedBox(height: 100,child: new Text("1"),),new SizedBox(height: 100,child: new Text("1"),),new SizedBox(height: 100,child: new Text("1"),),new SizedBox(height: 100,child: new Text("1"),),new SizedBox(height: 100,child: new Text("1"),),new SizedBox(height: 100,child: new Text("1"),),new SizedBox(height: 100,child: new Text("1"),),new SizedBox(height: 100,child: new Text("1"),),new SizedBox(height: 100,child: new Text("1"),),new SizedBox(height: 100,child: new Text("1"),),new SizedBox(height: 100,child: new Text("1"),),],),),actions: <Widget>[new FlatButton(onPressed: () {},child: new Text("确认"),),new FlatButton(onPressed: () {},child: new Text("取消"),),],);});}
复制代码
- 使用ListView+指定宽度和高度的Container来显示对话框
要将ListView包装在具有特定宽度和高度的Container中。 如果Container没有定义这两个属性的话,会报错,无法显示ListView。(目前我也是这样解决的,不知道有没有人有其他更好的方法哈。) 报错如下:
void showMyDialogWithListView(BuildContext context) {showDialog(context: context,builder: (BuildContext context) {return new AlertDialog(content: new Container(/*暂时的解决方法:要将ListView包装在具有特定宽度和高度的Container中如果Container没有定义这两个属性的话,会报错,无法显示ListView*/width: MediaQuery.of(context).size.width * 0.9,height: MediaQuery.of(context).size.height * 0.9,child: new ListView.builder(itemBuilder: (context, index) {return new SizedBox(height: 100,child: new Text("1"),);},itemCount: 10,shrinkWrap: true,),));},);//如果直接将ListView放在dialog中,会报错,比如//下面这种写法会报错:I/flutter (10721): ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════// I/flutter (10721): The following assertion was thrown during performLayout():// I/flutter (10721): RenderShrinkWrappingViewport does not support returning intrinsic dimensions.// I/flutter (10721): Calculating the intrinsic dimensions would require instantiating every child of the viewport, which// I/flutter (10721): defeats the point of viewports being lazy.// I/flutter (10721): If you are merely trying to shrink-wrap the viewport in the main axis direction, you should be able// I/flutter (10721): to achieve that effect by just giving the viewport loose constraints, without needing to measure its// I/flutter (10721): intrinsic dimensions.// I/flutter (10721):// I/flutter (10721): When the exception was thrown, this was the stack:// I/flutter (10721): #0 RenderShrinkWrappingViewport.debugThrowIfNotCheckingIntrinsics.<anonymous closure> (package:flutter/src/rendering/viewport.dart:1544:9)// I/flutter (10721): #1 RenderShrinkWrappingViewport.debugThrowIfNotCheckingIntrinsics (package:flutter/src/rendering/viewport.dart:1554:6)// I/flutter (10721): #2 RenderViewportBase.computeMaxIntrinsicWidth (package:flutter/src/rendering/viewport.dart:321:12)// I/flutter (10721): #3 RenderBox._computeIntrinsicDimension.<anonymous closure> (package:flutter/src/rendering/box.dart:1109:23)// I/flutter (10721): #4 __InternalLinkedHashMap&_HashVMBase&MapMixin&_LinkedHashMapMixin.putIfAbsent (dart:collection/runtime/libcompact_hash.dart:277:23)// I/flutter (10721): #5 RenderBox._computeIntrinsicDimension (package:flutter/src/rendering/box.dart:1107:41)// I/flutter (10721): #6 RenderBox.getMaxIntrinsicWidth (package:flutter/src/rendering/box.dart:1291:12)// I/flutter (10721): #7 _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.computeMaxIntrinsicWidth (package:flutter/src/rendering/proxy_box.dart:81:20)// showDialog(context: context, builder: (context) {// return new AlertDialog(title: new Text("title"),// content: new SingleChildScrollView(// child: new Container(// height: 200,// child: new ListView.builder(// itemBuilder: (context, index) {// return new SizedBox(height: 100, child: new Text("1"),);// }, itemCount: 10, shrinkWrap: true,),// ),// ),// actions: <Widget>[// new FlatButton(onPressed: () {}, child: new Text("确认"),),// new FlatButton(onPressed: () {}, child: new Text("取消"),),// ],);// });复制代码
需要动态更新界面的对话框
利用StatefulBuilder来实现一些对话框场景,需要对话框动态更新界面的。
比如在对话框里面显示一个checkbox,然后点击会修改checkbox的显示状态。如果是跟之前一样的实现对话框方法, 是无法实现动态去刷新对话框的界面的。
StatefulBuilder可以包含一个child,具有状态,可以调用setState刷新界面。
builder参数,用于创建想要显示的widget,可以调用StateSetter类型的setState参数来进行刷新界面。
typedef StatefulWidgetBuilder = Widget Function(BuildContext context, StateSetter setState);const StatefulBuilder({Key key,@required this.builder}) : assert(builder != null),super(key: key);
复制代码
实例的代码如下:
void showMyDialogWithStateBuilder(BuildContext context) {showDialog(context: context,builder: (context) {bool selected = false;return new AlertDialog(title: new Text("StatefulBuilder"),content:new StatefulBuilder(builder: (context, StateSetter setState) {return Container(child: new CheckboxListTile(title: new Text("选项"),value: selected,onChanged: (bool) {setState(() {selected = !selected;});}),);}),);});}
复制代码
自定义Dialog
比如我想显示一个菊花的loading加载框,那么用上面的方法都是行不通的。这个时候就需要我们去自定义一个对话框。
首先我们可以先去看一下Dialog的源码实现,然后只需再照着源码的实现,修改一下就行了。大部分代码是保持一致的,所以 对话框的显示效果比如动画,主题都是一致的。
下面是Dialog源码中的build方法实现。简单的修改下child属性所传的参数就行了。
@overrideWidget build(BuildContext context) {final DialogTheme dialogTheme = DialogTheme.of(context);return AnimatedPadding(padding: MediaQuery.of(context).viewInsets + const EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0),duration: insetAnimationDuration,curve: insetAnimationCurve,child: MediaQuery.removeViewInsets(removeLeft: true,removeTop: true,removeRight: true,removeBottom: true,context: context,//所以我们其实只需要修改child这个属性了,改成我们想要展示的widget就行了。child: Center(child: ConstrainedBox(constraints: const BoxConstraints(minWidth: 280.0),child: Material(elevation: 24.0,color: _getColor(context),type: MaterialType.card,child: child,shape: shape ?? dialogTheme.shape ?? _defaultDialogShape,),),),),);}
复制代码
下面是一个自定义加载框Dialog的例子,就是将AlertDialog的源码进行刚才所说的修改就行了。
void showMyCustomLoadingDialog(BuildContext context) {showDialog(context: context,barrierDismissible: false,builder: (context) {return new MyCustomLoadingDialog();});}class MyCustomLoadingDialog extends StatelessWidget {@overrideWidget build(BuildContext context) {Duration insetAnimationDuration = const Duration(milliseconds: 100);Curve insetAnimationCurve = Curves.decelerate;RoundedRectangleBorder _defaultDialogShape = RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2.0)));return AnimatedPadding(padding: MediaQuery.of(context).viewInsets +const EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0),duration: insetAnimationDuration,curve: insetAnimationCurve,child: MediaQuery.removeViewInsets(removeLeft: true,removeTop: true,removeRight: true,removeBottom: true,context: context,child: Center(child: SizedBox(width: 120,height: 120,child: Material(elevation: 24.0,color: Theme.of(context).dialogBackgroundColor,type: MaterialType.card,//在这里修改成我们想要显示的widget就行了,外部的属性跟其他Dialog保持一致child: new Column(mainAxisSize: MainAxisSize.min,crossAxisAlignment: CrossAxisAlignment.center,mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[new CircularProgressIndicator(),Padding(padding: const EdgeInsets.only(top: 20),child: new Text("加载中"),),],),shape: _defaultDialogShape,),),),),);}
}
复制代码
Demo的Github地址
github.com/LXD31256949…
公众号
最近弄了个学习Flutter的公众号(入魔的冬瓜),一部分是搬砖一些国外文章再加些自己的理解,一部分是自己平时的总结。
希望与大家在2019一起学习,共同进步!
祝大家狗年快乐!
Flutter之Dialog使用和踩坑相关推荐
- flutter boost使用简介(踩坑记录,持续更新)
前言 最近在研究android和flutter的混合开发.插件模式调用原生的混合模式已经在项目中使用,也暴露出一些问题, 最突出的问题就是flutter中对webview的支持,会出现各种各样奇怪的问 ...
- 安卓 Native+Flutter 应用开发入门资料、亲身实战及踩坑记录
安卓 Native+Flutter 应用开发实战及踩坑记录,练手入门项目:FluLearn 入门资料 第三方共享包检索(国内).第三方共享包检索(国外) Flutter开发环境搭建(中文版).Flut ...
- 【Flutter混合开发踩坑日记之‘applicationVariants‘ for extension ‘android‘】
Flutter混合开发踩坑日记之'applicationVariants' for extension 'android' 正文 坑一:Could not get unknown property ' ...
- Android-DialogFragmen踩坑,调用dismiss后回到activity再次出现dialog的问题
Android DialogFragmen踩坑 问题如何出现? 报错原因 报错如何解决? 更多其他页面-自定义View-实用功能合集:点击查看 Exception message: Back trac ...
- 【Vue】Vue1.0+Webpack1+Gulp项目升级构建方案的踩坑路
最近半年在维护公司的一个管理后台项目,搭建之初的技术栈比较混乱,构建方案采用了Gulp中调用Webpack的方式,Gulp负责处理.html文件,Webpack负责加载.vue..js等.而在这一套构 ...
- Angular4+ng2-ckeditor踩坑
机缘巧合,刚好公司准备做一个新闻版块. 在网上找了很多富文本插件感觉都跟angular4不大契合,最终踩坑CKEditor,这款强到爆炸得富文本插件! 发现国内几乎没有分享这方面踩坑得文章,所以最终决 ...
- iOS 入门开发踩坑实录
其实人生也没有什么道理可讲,但是我们不必丧气,还是要期待,人生有奇遇. 前言 苹果开发者:iOS Developer 最近因为工作需要要开始搞iOS了,简单记录下我收集和学习的过程. 学习资料准备 组 ...
- 2021-11-01 富文本编辑器Vue-Quill-Editor 踩坑之路
Vue-Quill-Editor 基于 Quill.适用于 Vue 的富文本编辑器,支持服务端渲染和单页应用. 相对于ssr,spa是通过component进行工作 ssr和spa的区别 1 踩坑之路 ...
- uniapp引入vantweapp踩坑笔记
vue-cli创建uniapp项目引入vantweapp踩坑笔记 uni-app中引入vantweapp vue-cli创建uniapp项目引入vantweapp踩坑笔记 一.环境准备 二.项目搭建 ...
- 【golang程序包推荐分享】分享亿点点golang json操作及myJsonMarshal程序包开发的踩坑经历 :)
目录[阅读时间:约5分钟] 一.概述 1.Json的作用 2.Go官方 encoding/json 包 3. golang json的主要操作 二.Json Marshal:将数据编码成json字符串 ...
最新文章
- 淘宝店、淘宝商城排名规则及流量提升培训教程
- python怎么用数据修改,如何更改数据框Python中的值
- keyshot手机渲染教程_keyshot灯光渲染基础入门教程【英】
- oracle ora-00026,oracle错误代码
- Ext JS 6学习文档-第6章-高级组件
- REST与RESTful
- 如何估算太坊交易的gas消耗量
- codeforces-984D——XOR-pyramid(DP)
- PHP Cookbook读书笔记 – 第13章Web自动化
- ORACLE对字符串去空格处理(trim)
- xshell 连接vbox 虚拟机
- 计算机网络应用云计算,计算机网络云计算技术应用
- 试位法的matlab程序步骤,MATLAB程序设计导论简介,目录书摘
- Win10下配置PHP环境变量
- Python进制转换与ASCII转换
- 2.1 电子计算机的兴起
- 数学知识的一些常用公式
- yunos6 是android几,成功脱离安卓?阿里首款国产操作系统YunOS6或将发布!
- IEEE 802.15介绍
- Windows 生成双向认证的自签证书(本地测试使用)
热门文章
- 揭秘React同构应用
- 几种常见的微服务架构方案——ZeroC IceGrid、Spring Cloud、基于消息队列、Docker Swarm...
- 谁在使用我的网站——用户分类
- 程序员2009精华本 有哪些精彩值得期待
- 路径读取os.path.abspath、os.path.dirname、os.path.basename、os.path.split
- HTML - 元素/标签和属性基础
- Python模块:bisect二分算法模块
- qq浏览文件服务器,腾讯浏览服务
- batchupdate一次多少条合适_中药材半夏地下茎块膨大剂,中药材半夏一次冲施多少肥料合适?...
- html页面如何复用,html代码用js实现复用