当前位置: 游戏平台 > 互联网科技 > 正文

Flutter Exception降到万分之几的秘密

时间:2020-03-16 10:11来源:互联网科技
闲鱼技术团队于2018年上半年率先引入了Flutter技术实现客户端开发,到目前为止成功改造并上线了复杂的商品详情和发布业务。随着flutter比重越来越多,我们开始大力治理flutter的excep

闲鱼技术团队于2018年上半年率先引入了Flutter技术实现客户端开发,到目前为止成功改造并上线了复杂的商品详情和发布业务。随着flutter比重越来越多,我们开始大力治理flutter的exception,起初很长一段时间内闲鱼内flutter的exception率一直在千分之几左右。经过我们的整理和解决,解决了90%以上的flutter exception。

Flutter作为一种全新的响应式,跨平台,高性能的移动开发框架。从开源以来,已经得到越来越多开发者的喜爱。闲鱼是最早一批与谷歌展开合作,并在重要的商品详情页中使用上线的公司。一路走来,积累了大量的开发经验。虽然越来越多的技术大牛在flutter世界中弄得风声水起,但是肯定有很多的flutter小白希望能快速上手,享受flutter编程的乐趣。本文就是面向刚刚踏上futter的同学,从Flutter体系中最基本的一个概念widget入手学习Flutter。希望能助力每一位初学者。

我们对exception进行了归类,大头主要分为两大类,这两大类堆栈数量很多,占到整体90%左右:

可能大家要问的第一个问题是为什么从Widget开始?

1.第一大类的堆栈都指向了setstate

图片 1

#0 State.setState (package:flutter/src/widgets/framework.dart:1141)#1 _DetailCommentWidgetState.replyInput.<anonymous closure>.<anonymous closure> (package:fwn_idlefish/biz/item_detail/fx_detail_comment.dart:479)#2 FXMtopReq.sendReq.<anonymous closure> (package:fwn_idlefish/common_lib/network/src/mtop_req.dart:32)#3 NetService.requestWithModel.<anonymous closure> (package:fwn_idlefish/common_lib/network/src/net_service.dart:58)#4 _rootRunUnary (dart:async/zone.dart:1132)#5 _CustomZone.runUnary (dart:async/zone.dart:1029)#6 _FutureListener.handleValue (dart:async/future_impl.dart:129)

从flutter的架构图中不难看出widget是整个视图描述的基础。Flutter 的核心设计思想便是

2.第二大类堆栈都与buildContext直接或者间接相关

图片 2

#0 Navigator.of (package:flutter/src/widgets/navigator.dart:1270)#1 Navigator.pop (package:flutter/src/widgets/navigator.dart:1166)#2 UploadProgressDialog.hide (package:fwn_idlefish/biz/publish/upload_progress_dialog.dart:35)#3 PublishSubmitReducer.doPost.<anonymous closure> (package:fwn_idlefish/biz/publish/reducers/publish_submit_reducer.dart:418)<asynchronous suspension>#4 FXMtopReq.sendReq.<anonymous closure> (package:fwn_idlefish/common_lib/network/src/mtop_req.dart:32)#5 NetService.requestWithModel.<anonymous closure> (package:fwn_idlefish/common_lib/network/src/net_service.dart:58)#6 _rootRunUnary (dart:async/zone.dart:1132)#7 _CustomZone.runUnary (dart:async/zone.dart:1029)

即一切即Widget。在flutter的世界里,包括views,view controllers,layouts等在内的概念都建立在Widget之上。widget是flutter功能的抽象描述。所以掌握Flutter的基础就是学会使用widget开始。

第一类明显与element和sate的生命周期有关。第二类与buildContext有关。

本文会从大家熟悉的UI绘制视角来介绍flutter组件和布局的基础知识。首先罗列了UI开发中最为常用,最为基础的组件。下面逐一进行介绍。

buildContext是什么?

图片 3

下面是一段state中获取buildContext的实现

1.1 Text

Element get _currentElement => _registry[this];BuildContext get currentContext => _currentElement;

Text几乎是UI开发中最为重要的组件之一了,UI上面文字的展示基本上都要靠Text组件来完成。Flutter提供了原生的Text组件。Text的配置属性是很丰富的,属性主要分为两个部分一个是对齐&显示控制相关的在Text类的属性中,另一类是样式相关的属性使用单独的类TextStyle进行控制。跟native控件相比(以android为例),Text的组件基本上提供了同等的能力,并且提供了更加丰富的样式装饰能力。详细的属性可以参考官方文档flutter text.

很明显buildContext其实就是element实例。buildContext是一个接口,element是buildContext的具体实现。

1.1.1 实践Coding

所以上面的exception都指向了flutter element和state的生命周期

设置文字&文字大小&颜色&行数限制&文本对齐

1.state生命周期

图片 4

图片 5

效果如下:

2. element 与state生命周期

图片 6

element是由widget createElement所创建。state的生命周期状态由element调用触发。

1.2 Image

图片 7

图片也是UI部分开发最为重要的组件之一。在能看图随看文字的年代,图片是页面展示的重中之重!Flutter同样原生提供了Image组件。下面重点介绍一下几个重点:

最核心的是在new elment的时候element的state的双向绑定正式建立。在umount的时候element和state的双向绑定断开。

1.2.1 缩放

3. activity生命周期与state关系

怎样设置图片显示的缩放方式呢?

flutter提供WidgetsBindingObserver给开发者来监听AppLifecycleState。AppLifecycleState有4中状态

Flutter中的图片缩放是fit字段来控制的。这是对最终图片展示效果影响很大的一个参数,也是容易出错的点。下面逐个分析一下flutter Image组件提供的缩放方式。

1.resumed界面可见,比如应用从后台到前台2.inactive页面退到后台或者弹出dialog等情况下这种状态下接收不到很任何用户输入,但是还会有drawframe的回调3.paused应用挂起,比如退到后台。进入这种状态代表不在有任何drawframe的回调4.suspendingios中没用,puased之后进入的状态,进入这种状态代表不在有任何drawframe的回调

缩放属性值在BoxFit枚举中

看下android生命周期和appLifecycleState、state关系

下面列出的图片是flutter官方对各种缩放做的图片示例。基本上都表述很清楚了,就整理出来供大家查阅。

  1. 创建

图片 8

图片 9

1.2.2 图片获取

2.按home键退到后台

怎样从各种来源加载图片?

图片 10

默认的Image组件不能直接显示图片,他需要一个ImageProvider来提供具体的图片资源的(即Image中的image字段需要赋值)。咋一看这确实非常麻烦,但是实际上ImageProvider并不需要完全重新自己实现。在Image类中目前提供了一下几个实现好的ImageProvider,基本能满足常见的需求。

3.从后台回到前台

图片 11

图片 12

Image同样支持GIF图片

4.back键退出当前页面(route pop)

网络请求Image是大家最常见的操作。这里重点说明两个点:

图片 13

• 缓存

5.back键退出应用

ImageCache是ImageProvider默认使用的图片缓存。ImageCache使用的是LRU的算法。默认可以存储1000张图片。如果觉得缓存太大,可以通过设置ImageCache的maximumSize属性来控制缓存图片的数量。也可以通过设置maximumSizeBytes来控制缓存的大小(默认缓存大小10MB)。

图片 14

• CDN优化

1.在工程开发中,我们最容易忽略了state的dispose状态。

如果想要使用cdn优化,可以通过url增加后缀的方式实现。默认实现中没有这个点,但是考虑到cdn优化的可观收益,建议大家利用好这个优化。

看一段例子:

1.2.3 FadeInImage

图片 15

在实际开发中,考虑到图片加载速度可能不能达到预期。所以希望能增加渐入效果&增加placeHolder的功能。Flutter同样提供的这样的组件——FadeInImage。

这个例子可能会在某些情况下excetion。在state dispose后,element会和state断开相互引用,如果在这个时候开发者去拿element的位置信息或者调用setstate 刷新布局时就会报异常。

FadeInImage也提供了从多种渠道加载图片的能力。这块跟上面所说差异不大。这里不再赘述。

最常见的是在一些timer、animate、网络请求等异步逻辑后调用setstate导致的excetion。安全的做法是在调用setstate前判断一下state是否是mounted状态。如下:

1.2.4 实践Coding

图片 16

• 从网络获取图片保持图片比例并尽可能大的放入

2.buildcontext使用错误

图片 17

看一段错误使用buildcontext例子

• 效果如下:

图片 18

图片 19

上面的错误在于在跨堆栈使用了buildcontext。由于outcontext的生命周期与buttomcontext不一致,在弹出bottomsheet的时候outcontext可以已经处于umount或者deactivite。上面例子正确的做法是使用bottomcontext获取focusScopeNode。

1.3 Container

我们在跨堆栈传递参数(如bottomsheet、dialog、alert、processdialog等)场景时特别要注意buildcontext的使用。

Flutter的设计思想就是完全的widget化。这也就是说连最基本的padding,Center都是widget。设想一下如果每次写view,连padding,Center都要自己包一个组件是一种怎样的体验?作为一个工程师,别给只给我谈思想,实际操作的操作效率也同样非常重要。flutter 官方也意识到了这个问题,他们从实际编写效率的角提供了一个友好高效的封装,这就是Container!首先没有任何疑问,Container 本身也是一个widget。但是他却提供了对基础widget的封装,提高了UI基础装饰能力的表达效率。Container类似于android中的ViewGroup。

不过瘾?如果你还想了解更多关于flutter开发更多有趣的实战经验,就来关注微信公众号 "闲鱼技术"。

相信大部分的属性大家都会感觉非常亲切,结合代码注释都比较容易理解,这里就不再赘述。其中需要重点解释一下的是:Decoration和BoxConstraints。

1.3.1 装饰

Decoration是对Container进行装饰的描述。其概念类似与android中的shape。一般实际场景中会使用他的子类BoxDecoration。BoxDecoration提供了对背景色,边框,圆角,阴影和渐变等功能的定制能力。

本文作者:闲鱼技术-虚白

需要注意几个点:

阅读原文

• BoxDecoration的image属性相当于设置的是背景图。但是image会绘制在color 和gradient之上。

本文为云栖社区原创内容,未经允许不得转载。

• image是需要一个DecorationImage类的实现。DecorationImage的属性和Image组件比较类似,可以复用Image组件中的ImageProvider。

1.3.2 大小

BoxConstraints其实是对Container组件大小的描述。BoxConstraints属性比较简单。如果不太清楚可以研究一下盒子模型。这里有个点需要重点说明一下:

• 如何表达尽可能大这样的意思?(类似于android中的match_parent)Flutter中可以使用double.infinity来做出类似的表达。

1.3.3 实践Coding

• 设置边框&padding&margin&圆角&背景图

图片 20

• 效果如下:

图片 21

1.4 手势操作

手势操作是最常见的UI交互操作。在Flutter中手势识别也是一个widget!这点对新人来说又是一个新鲜的地方。通常来说可以通过GestureDetector类来完成点击事件的处理。使用时只需要将GestureDetector包裹在目标widget外面,再实现对应事件的函数即可。从点击到长按,从缩放到拖动,这个类基本上都有相应的实现。具体可以参见组件文档。

页面布局应该是UI编写最为根本的知识,其主要的描述的是父子组件子子组件之间的位置关系。首先我们理解一下官方文档的逻辑:

图片 22

将布局分为单孩子和多孩子是Flutter布局的一大特色。这点对native研发同学来说会比较新鲜。单孩子组件主要继承自SingleChildRenderObjectWidget。这些组件能提供丰富的装饰能力(例如container),也能提供部分特定的布局能力。多孩子组件继承自MultiChildRenderObjectWidget,能提供更加丰富的布局能力(Flex,Stack,flow),但几乎没有装饰的能力。下面介绍几个重点布局:

2.1 Flex

Flutter在布局上也提供了完整的Flex布局能力。但是在Flutter官方文档中Layout Widgets,是看不到任何Flex的影子的。映入眼帘的却是Row,Column,这些是什么鬼?其实不难发现类似Row,Column 这样的组件,他们的基类都是Flex。Row和Column差别是设置了不同的flex-direction。而之所这么设计,是因为Flutter的widget从开始设计之初就考虑到UI布局语义保持的重要性。这块应该部分借鉴了前端的经验,极力避免一个div搞定全部页面的尴尬(当然flutter也可以使用Flex来做同样的事情,但是并不建议这么做)。

Flutter使用的Flex模型基本上跟传统的Css类似。这块前端同学可以快速上手。但是Flex对于客户端同学来说是一种全新的布局方式。Flex的基础知识可以参看flex布局基础。由于篇幅有限这里不展开叙述。这里只重点强调一个点:

如下图flex布局概念如下:

图片 23

flex通过direction设置了flex的主轴方向即main axis。和主轴垂直的方向叫做cross axis。flex布局中对子布局的控制是从main axis 和cross axis两个方向上进行的。例如居中有main axis居中和cross axis居中。两者都居中才是容器的完全居中。这点是客户端同学可能会容易弄混的地方。重点关注一下。

2.1.1 实践Coding

ok,看完这些知识,我们实际需求角度实际操作几个case来熟悉一下Flex。

• 居中

图片 24

• 效果如下:

图片 25

• weight left:right=2:1 通过设置Flexible的flex值大小完成比例设置

图片 26

• 效果如下:

图片 27

2.2 stack

在实际开发中,还是需要在一些Widgets的上面再覆盖上新的Widgets。这时候就需要层式布局了。这种布局在Native上,以android为例,类似于relativeLayout 或者FrameLayout。在Flutter中使用的是Stack。

实际使用中Stack中的子Widgets分为两种:

• positioned

是包裹在组件Positioned中的组件

编辑:互联网科技 本文来源:Flutter Exception降到万分之几的秘密

关键词: