成都网站建设设计

将想法与焦点和您一起共享

flutterdraw的简单介绍

Flutter 启动页的前世今生适配历程

APP 启动页在国内是最常见也是必备的场景,其中启动页在 iOS 上算是强制性的要求,其实配置启动页挺简单,因为在 Flutter 里现在只需要:

在甘井子等地区,都构建了全面的区域性战略布局,加强发展的系统性、市场前瞻性、产品创新能力,以专注、极致的服务理念,为客户提供网站制作、做网站 网站设计制作按需策划设计,公司网站建设,企业网站建设,高端网站设计,全网整合营销推广,成都外贸网站建设,甘井子网站建设费用合理。

一般只要配置无误并且图片尺寸匹配,基本上就不会有什么问题, 那既然这样,还有什么需要适配的呢?

事实上大部分时候 iOS 是不会有什么问题, 因为 LaunchScreen.storyboard 的流程本就是 iOS 官方用来做应用启动的过渡;而对于 Andorid 而言,直到 12 之前 windowBackground 这种其实只能算“民间”野路子 ,所以对于 Andorid 来说,这其中就涉及到一个点:

所以下面主要介绍 Flutter 在 Android 上为了这个启动图做了哪些骚操作~

在已经忘记版本的“远古时期” , FlutterActivity 还在 io.flutter.app.FlutterActivity 路径下的时候,那时启动页的逻辑相对简单,主要是通过 App 的 AndroidManifest 文件里是否配置了 SplashScreenUntilFirstFrame 来进行判断。

在 FlutterActivity 内部 FlutterView 被创建的时候,会通过读取 meta-data 来判断是否需要使用 createLaunchView 逻辑 :

是不是很简单,那就会有人疑问为什么要这样做?我直接配置 Activity 的 android:windowBackground 不就完成了吗?

这就是上面提到的时间差问题, 因为启动页到 Flutter 渲染完第一帧画面中间,会出现概率出现黑屏的情况,所以才需要这个行为来实现过渡 。

经历了“远古时代”之后, FlutterActivity 来到了 io.flutter.embedding.android.FlutterActivity , 在到 2.5 版本发布之前,Flutter 又针对这个启动过程做了不少调整和优化,其中主要就是 SplashScreen 。

自从开始进入 embedding 阶段后, FlutterActivity 主要用于实现了一个叫 Host 的 interface ,其中和我们有关系的就是 provideSplashScreen 。

默认情况下它会从 AndroidManifest 文件里是否配置了 SplashScreenDrawable 来进行判断 。

默认情况下当 AndroidManifest 文件里配置了 SplashScreenDrawable ,那么这个 Drawable 就会在 FlutterActivity 创建 FlutterView 时被构建成 DrawableSplashScreen 。

DrawableSplashScreen 其实就是一个实现了 io.flutter.embedding.android.SplashScreen 接口的类,它的作用就是:

之后 FlutterActivity 内会创建出 FlutterSplashView ,它是个 FrameLayout。

FlutterSplashView 将 FlutterView 和 ImageView 添加到一起, 然后通过 transitionToFlutter 的方法来执行动画,最后动画结束时通过 onTransitionComplete 移除 splashScreenView 。

所以整体逻辑就是:

当然这里也是分状态:

当然这个阶段的 FlutterActivity 也可以通过 override provideSplashScreen 方法来自定义 SplashScreen 。

看到没有,做了这么多其实也就是为了弥补启动页和 Flutter 渲染之间, 另外还有一个优化,叫 NormalTheme 。

通过该配置 NormalTheme ,在 Activity 启动时,就会首先执行 switchLaunchThemeForNormalTheme(); 方法将主题从 LaunchTheme 切换到 NormalTheme 。

大概配置完就是如下样子, 前面分析那么多其实就是为了告诉你,如果出现问题了,你可以从哪个地方去找到对应的点 。

讲了那么多, Flutter 2.5 之后 provideSplashScreen 和 io.flutter.embedding.android.SplashScreenDrawable 就被弃用了,惊不喜惊喜,意不意外,开不开心 ?

通过源码你会发现,当你设置了 splashScreen 的时候,会看到一个 log 警告:

为什么会弃用?

其实这个提议是在 这个 issue 上,然后通过 这个 pr 完成调整。

大概意思就是: 原本的设计搞复杂了,用 OnPreDrawListener 更精准,而且不需要为了后面 Andorid12 的启动支持做其他兼容,只需要给 FlutterActivity 等类增加接口开关即可 。

也就是2.5之后 Flutter 使用 ViewTreeObserver.OnPreDrawListener 来实现延迟直到加载出 Flutter 的第一帧。

为什么说默认情况? 因为这个行为在 FlutterActivity 里,是在 getRenderMode() == RenderMode.surface 才会被调用,而 RenderMode 又和 BackgroundMode 有关心 。

所以在 2.5 版本后, FlutterActivity 内部创建完 FlutterView 后就会执行一个 delayFirstAndroidViewDraw 的操作。

这里主要注意一个参数: isFlutterUiDisplayed 。

当 Flutter 被完成展示的时候, isFlutterUiDisplayed 就会被设置为 true。

所以当 Flutter 没有执行完成之前, FlutterView 的 onPreDraw 就会一直返回 false ,这也是 Flutter 2.5 开始之后适配启动页的新调整。

看了这么多,大概可以看到其实开源项目的推进并不是一帆风顺的,没有什么是一开始就是最优解,而是经过多方尝试和交流,才有了现在的版本,事实上开源项目里,类似这样的经历数不胜数:

左边是“立”字,右边是“风”字,请问这个字怎么读?什么意思?

(飒)

(1) ㄙㄚˋ

(2) 〔~戾〕清凉的样子,如“游清灵之~~兮,服云衣之披披”。

(3) 形容风声:秋风~~。

(4) 郑码:SUQO,U:98D2,GBK:ECAA

(5) 笔画数:9,部首:风,笔顺编号:414313534

详细注解

--------------------------------------------------------------------------------

〔象〕

(1) (形声。从风,立声。本义:风声) 同本义 [rustle;soughing]

飒,翔风也。——《说文》

飒飒,风也。——《广雅》

风飒飒兮木萧萧。——《楚辞·九歌》

有风飒然而至。——宋玉《风赋》

(2) 又如:飒飖(大风);飒飒;飒剌剌(形容风的声音)

词性变化

--------------------------------------------------------------------------------

〔动〕

(1) 狂风拉物 [(of fierce wind) drag;draw]

梦初回,燕尾翻风,乱飒起湘帘翠。——明·汤显祖《牡丹亭》

(2) 凋零;衰落 [wither]

岁聿忽其云暮, 庭草飒以萎黄。——梁·陆倕《思田赋》

(3) 飞舞 [dance in the air;flutter]

飒沓矜。——鲍昭《飞鹤赋》。又如:飒沓(群飞的样子;众盛的样子);飒纚(长袖飞舞的样子)

(4) 飘落 [fade and fall]。如:飒洒(飘然而下的样子)

(5) 风微过 [breeze]

徐行时若风飒芙蓉。——《敦煌变文集》

〔形〕

衰老 [decrept;senile]

知己犹未极,鬓毛飒已苍。——岑参《岑嘉州诗》

常用词组

--------------------------------------------------------------------------------

飒然

sàrán

(1) [soughing]∶形容风吹时沙沙作响

有风飒然而至

(2) [brisk and neat]∶爽利的样子

或如刀剑铁骑,飒然浮空。——清·黄宗羲《柳敬亭传》

飒飒

sàsà

[rustle;sough] 形容风吹动树木枝叶等的声音

秋风飒飒

寒雨飒飒

飒爽

sàshuǎng

[valiant] 豪迈的样子

英姿飒爽

汉译英

--------------------------------------------------------------------------------

English

--------------------------------------------------------------------------------

( 飒 )

S à

Fresh and cool appearance of[ 飒戾 ], such as" swim the pure working properly its 飒飒兮 , the 披披 of a cloud dress ".

Description information.

Flutter的setState(状态刷新)

Flutter有两个常用的状态类:

标记为dirty,执行的markNeedsBuild,定义在Element类中:

当前Element节点被标记为dirty,同时调用owner的scheduleBuildFor方法:

将element元素添加到全局的“脏”链表里。

BuildOwner用来管理哪些需要更新的Widget。这个owner最开始被初始化的地方在WidgetsBinding的initInstances方法中,随后初始化了onBuildScheduled方法,对应执行的是_handleBuildScheduled,定义在WidgetsBinding类中:

ensureVisualUpdate 方法定义在SchedulerBinding类中:

在提交下一帧绘制的时候会调用到scheduleFrame方法,提交给引擎绘制,看看scheduleFrame方法,也定义在SchedulerBinding类中:

提交给引擎绘制之后,会收到onDrawFrame的回调,最终执行到_handleDrawFrame方法中,对应的是handleDrawFrame方法,定义在SchedulerBinding类中:

在RendererBinding的initInstances方法中添加了一个回调到这个List中,对应的是RenderBinding的drawFrame方法,对应的节点进行绘制渲染操作。

WidgetsBinding中的drawFrame方法:

看看这里的buildScope方法,定义在BuildOwner方法中。在上面 scheduleBuildFor 方法介绍中有提到:"scheduleBuildFor 是把一个 element 添加到 _dirtyElements 链表,以便当[WidgetsBinding.drawFrame]中调用 buildScope 的时候能够重构 element。onBuildScheduled()是一个 BuildOwner 的回调"。在 drawFrame 中调用 buildOwner.buildScope(renderViewElement)更新 elements。

_dirtyElements列表在遍历的过程中执行rebuild方法,此时将所有标记为dirty的Element节点依次执行rebuild,preformRebuild,build,updateChild,update方法,执行界面更新。完成build,update操作完成之后,后续会将需要绘制的RenderObject添加到需要layout的列表中,等待绘制渲染。所有绘制完成之后将_dirtyElments列表清空,_inDirtyList标记位置为false。

提交给引擎绘制渲染

看看super.drawFrame(),这里就执行到了RendererBinding类中,定义如下:

这里就是将最终需要绘制渲染的画面提交给引擎的地方了,绘制完成之后就在界面显示更新后的视图了。

flutter生命周期

Flutter跟安卓的Activity、iOS的ViewController一样拥有自己的生命周期, Flutter中一切都是Widget,渲染方式有点像H5的DOM树。

Flutter生命周期可以分为3个阶段:

1、实例化组件并添加到树, 即Navigator.push;

2、状态变化,即打开新的widget或者依赖的上级widget发生变化;

3、从树中移除, 即Navigator.pop。

在Flutter中Widget都是不可变的, 但实际上需要根据对应的状态刷新Widget。 从而产生了StatelessWidget和StatefulWdiget, StatefulWidget是由2个对象Widget和State组成的。

为什么将State和Widget分开呢?

答案是性能, State管理状态(可以理解为Controller),Widget是UI(即View)。 根据状态变化每次生成Widget(即View)可以节省内存,即不必每次创建状态对象State。

构造函数:

同其它高级语言, 只执行一次;

initState:

插入到渲染树时调用,只执行一次。(类似Android Fragment的onCreateView函数)

didChangeDependencies:

1、在初始化initState后执行; 2、显示/关闭其它widget。 3、可执行多次;

didUpdateWidget:

上级节点rebuild widget时, 即上级组件状态发生变化时会触发子widget执行didUpdateWidget;

deative:

有点像Android的onStop函数, 在打开新的Widget或回到这个widget时会执行; 可执行多次;

dispose:

类似于Android的onDestroy, 在执行Navigator.pop后会调用该办法, 表示组件已销毁;

reassemble:

点击闪电会执行,只用于调试时的hot reload。 release版本不会执行该函数。

常见业务场景:

Widget A打开Widget B: Navigator.push(B)

B构造函数---B initState---B didChangeDependencies---B build---A deactive---A didChangeDependencies.

Widget B退出: Navigator.pop

A deactive---A didChangeDependencies---A build---B deactive---B dispose

可以看出, Flutter打开、关闭Widget时跟安卓、iOS的时序一样, 都是先处理即将显示的界面。

activity生命周期和Flutter对应关系:

Flutter提供了WidgetsBindingObserver来监听AppLifecycleState, 而AppLifecycleState有4种状态:

1、 resumed 界面可见, 同安卓的onResume。

2、inactive界面退到后台或弹出对话框情况下, 即失去了焦点但仍可以执行drawframe回调;同安卓的onPause;

3、paused应用挂起,比如退到后台,失去了焦点且不会收到drawframe回调;同安卓的onStop;

4、suspending, iOS中没用,安卓里就是挂起,不会再执行drawframe回调;

下面是生命周期:

1、初次打开widget时,不执行AppLifecycleState的回调;

2、按home键或Power键, AppLifecycleState inactive----AppLifecycleState pause

3、从后台到前台:AppLifecycleState inactive---ApplifecycleState resumed

4、back键退出应用: AppLifecycleState inactive---AppLifecycleState paused

flutter贝塞尔曲线

1.要绘制贝塞尔线,我们需要四个点: 起点 , 终点 和 两个控制点 ,如下图所示。移动控制点会改变曲线的斜率。您可以在此 在线工具中 使用控制点。

我们可以使用类Path的cubicTo方法绘制贝塞尔曲线:

使用控制点(x1,y1)和(x2,y2)添加从当前点到给定点(x3,y3)的曲线的三次贝塞尔曲线段。

如您所见,该cubicTo方法接受三个参数。其中两个是控制点,最后一个参数是终点。起点是您的笔已经位于画布上的位置。

不要忘记在画布坐标中,左上角是(0,0)点,右下角是(size.width,size.height)。因此,请尝试相应地调整四点:

请记住,paint对象就像我们的笔,我们将其颜色设置为蓝色,宽度设置为3。

我们用path对象描述了bezier路径。该moveTo方法已用于将笔移动到路径的起点。然后我们调用cubicTo方法来定义控制点和终点。之后,我们使用该drawPath方法绘制了路径。

贝塞尔曲线参考:

///推荐一些曲线图/折线图/柱状图参考:

all first_rank_v2~rank_v25-2-95632571.nonecaseutm_term=flutter%20%E5%8A%A8%E6%80%81%E7%BB%98%E5%88%B6%E6%9B%B2%E7%BA%BF

Flutter 56: 图解自定义 BubbleWidget 气泡插件

小菜在学习时需要用到气泡效果,为了更加灵活,小菜封装了一个简单的 flutter_bubble 气泡插件,方便日常的使用;

小菜准备用 Canvas 的 drawPath 进行绘制,主要分为三个部分,圆角弧线,普通直线,尖角折线,均可由 drawPath 自带方法绘制;小菜以前整理过关于 Canvas 绘制的小博客,实现很简单;

小菜绘制了一个简陋的原型图,整体黑框为 Bubble Widget 整体范围;蓝色圆弧为圆角位置;红色尖角可根据上下左右参数进行配置,且只可展示一个,尖角的高度和角度可自由配置,当确定一个尖角位置时,其余三个方向宽高延伸到黑框部分;而橙线则是连接圆角与尖角等直线;中间空余部分为子 Widget 位置; Tips: Child Widget 宽高小于等于 Bubble Widget ;

首先在边角处绘制四个圆弧,直接用 arcTo 即可,需要注意的是:小菜整体以 drawPath 方式实现,准备从左上角开始顺时针绘制,所以绘制圆弧时也是顺时针方向;

小菜理解, Rect 为绘制圆角的矩形,包括位置及大小; startAngele 为起始角度; sweepAngle 为绘制弧形角度;小菜需要的四个圆弧大小均为 pi/2 ,只需调整矩形位置与起始角度即可;

其次绘制尖角,小菜的尖角是由 lineTo 两段直线拼接起来的,只需要处理起点与终点即可;小菜为了更加灵活,可以设置尖角高度与尖角角度(0 ~ 180),通过三角函数进行计算;

最后就是将处理好的连接起来,小菜为了适应更多场景,尖角位置也可自由配置,长度为到圆角的距离,默认为边框中间位置;

小菜将配置逻辑编辑好发布到 Pub 库,基本 BubbleWidget 便完成,简单分析一下可配置项;

自定义 Bubble Widget 是小菜发布的第二款 Pub 插件,还有很多不完善的地方,如有错误请多多指导!


当前题目:flutterdraw的简单介绍
网页网址:http://chengdu.cdxwcx.cn/article/dsseccj.html