Flutter找不到Android模拟器解决

Flutter配置好后,在Android Studio中找不到设备

完成Flutter的Android配置之后,连上设备,运行flutter doctor,发现已经识别了一个可用设备了

但是用Android Studio新建Flutter项目之后,却一直显示未找到设备。

最后在Stack Overflow上找到了解决方案:

https://stackoverflow.com/questions/49222658/device-list-doesnt-shows-in-android-studio-using-flutter

就是要配置一下flutter关联的Android sdk路径和Android Studio文件夹,我的设置完sdk路径就可以正常找到device了,问题解决~。

粗体部分替换成自己的Android sdk路径:

粗体部分替换成自己的android studio文件夹路径(我的不用配置这个就成功了):

注意,上面的操作在重启之后无效,如果希望重启之后也生效,则编辑 ~/.flutter_settings ,增加如下配置即可:

参考链接


三阶贝塞尔曲线cubicTo

flutter中绘制基础引言

Flutter 中实现绘制的主要是CustomPainter类

然后放在父控件的child里用 CustomPaint 包裹使用

三阶贝塞尔曲线

在 flutter 通过 Canvas 来结合 Path 来实现绘制 三阶贝塞尔曲线,三阶贝塞尔曲线就是说两个点之间的线 有两个控制点。

例如我们要绘制上述的椭圆,其中 A、B、C 就是我们的目标点,我们绘制的路径就是 从A到B再到C,然后控制点如下

  • a1 、 b1 点是 A B 的控制点
  • b2 、c2 点是 B C 的控制点

那么我们要绘制出如上图中的效果,代码如下

在 flutter 中,通过 path 的 cubicTo 函数来实现三阶贝塞尔曲线

void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3) 点(x1,y1)、(x2,y2) 为控制点 (x3,y3) 为目标点

参考链接


flutter绘图基础之三阶贝塞尔曲线cubicTo

Flutter绘制贝塞尔曲线 、折线 、柱状图,支持触摸

之前写过一篇Android原生绘制曲线图的博客,动画效果不要太丝滑,那么现在到了Flutter,该如何实现类似的效果呢?如果你熟悉Android的Canvas,那么恭喜你, 你将很快上手Flutter的Canvas绘制各种图形,因为实现方式基本上与Android是一模一样。

先看下要实现的基本效果:

Flutter中如果想要自定义绘制,那么你需要用到 CustomPaint 和 CustomPainter ; CustomPaint是Widget的子类,先来看下构造方法

我们只需要关心三个参数,painterforegroundPainterchild , 这里需要说明一下,painter 是绘制的 backgroud 层,而child 是在backgroud之上绘制,foregroundPainter 是在 child 之上绘制,所以这里就有了个层级关系,这跟android里面的backgroudforeground是一个意思,那这两个painter的应用场景是什么呢?假如你只是单纯的想绘制一个图形,只用painter就可以了,但是如果你想给绘制区域添加一个背景(颜色,图片,等等),这时候如果使用 painter是会有问题的,painter的绘制会被child 层覆盖掉,此时你只需要将painter替换成foregroundPainter,然会颜色或者图片传递给child即可。

如果是Android绘制几何图形,应该是重写View的onLayout() 和 onDraw方法。但是Flutter实现绘制,必须继承CustomPainter并重写 paint(Canvas canvas, Size size)和 shouldRepaint (CustomPainter oldDelegate)方法 ,第一个参数canvas就是我们绘制的画布了(跟Android一模一样),paint第二个参数Size就是上面CustomPaint构造方法传入的size, 决定绘制区域的宽高信息

既然Size已经确定了,现在就定义下绘制区域的边界,一般我做类似的UI,都会定义一个最基本的padding, 一般取值为16 , 因为绘制的内容与坐标轴之间需要找到一个基准线,这样更容易绘制,而且调试边距也很灵活

然后在paint()方法中拿到Size,确定绘制区域的坐标

maxMin是定义存储曲线中最大值和最小值的

初始化画笔 .. 是dart中的独特语法,代表使用对象的返回值调用属性或方法

绘制坐标轴,这里在确定好的边界基础上再次xy轴横向和纵向各自增加一倍的padding,不然显得太紧凑

绘制 X 轴刻度,定义为最多绘制7组数据 ,rulerWidth就是刻度的长度定义为8

这里要说明一点,Flutter绘制文本,并不能像android那样调用canvas.drawText(), 而是通过TextPainter来渲染的,

构造TextPainter 你必须指定文字的方向 textDirection 和 宽度 layout ,最后调用paint方法,指定坐标进行绘制

绘制 Y 轴刻度,y轴的刻度数量并不需要跟随数据源的长度,只需要按照一定数量(yNum )平分y轴最大值即可

现在坐标轴和刻度已经绘制完成了,基本上与原生一致,只是代码方式有些区别,接下来的曲线也是一模一样的,绘制贝塞尔曲线其实也不难,主要是找到起点和两个坐标之间的辅助点, 贝塞尔曲线的原理可以参考这里

遍历数据源的第一个元素时,需要做个判断,index=0时,需要将path move到此处

添加后面的坐标时,需要找辅助点

如果是要画折线而非曲线,第一步还是path.moveTo ,折线不需要找辅助点,所以后续可以直接添加坐标,path.lineTo

最后将path绘制出来

虽然曲线已经成功绘制,但是这样显得很枯燥,如果可以看到绘制过程那就会更加有趣味性,这时候就需要通过动画来更新曲线的path的长度了,一般Android中我会用ValueAnimator.ofFloat(start ,end ) 来开启一个动画 ,在Flutter中,动画也是非常简单实用

动画执行过程中,我们会及时获取到当前的动画进度 _value, 此时就需要一段完整的path跟随动画值等比绘制了,之前在Android中我们可以用 PathMeasure 来测量path ,然后根据动画进度不断地截取,就实现了像贪吃蛇一样的效果, 但是在Flutter中,我并没有找到PathMeasure 这个类,相反的,PathMeasure 在Flutter竟然是个私有的类 _PathMeasure ,经过一通百度 和 Google,也没有找到类似的案例。难道没有人给造轮子,就必须要停止我前进的步伐了嘛,不急,显然Path这个类里面有很多方法,就这样我走上了一条反复测试的不归路...

幸运的是,在翻阅了Google 官方Flutter Api 后,终于找到了突破口

哈哈,藏得还挺深呐,就是这个 PathMetrics 类,path.computeMetrics() 的返回值 ,是用来将path解析成矩阵的一个工具

有个参数 forceClosed , 表示是否要连接path的起始点 ,我们这里当然不要啦 ,computeMetrics方法返回的是PathMetrics对象,调用 toList (),可以获取到 多个path组成的 List<PathMetric> ; 集合中的每个元素代表一段path的矩阵 , 奇怪,为什么是多个path 呢 ???

当时我也是懵着猜测的,历史总是惊人的相似,被我给猜对了,不晓得你们有没有发现,Path有个方法可以添加多个Path ,

当我每调用一次 addPath() 或者 moveTo(),lsit . length就增加1,所以上面提到的多个path的集合 就不难理解了 ,因为我们这里只有一个path,所以我们的 list 中只有一个元素 , 元素中包含一段path, 现在我们获取到了描述path的矩阵PathMetric

PathMetric.length 就是这段path的长度了,唉,为了找到你 ,我容易吗 !

另外还有个关键的方法,可以将pathMetric按照给定的位置区间截取,最后返回这段path, 这就跟Android中的PathMeasure.getSegment()是一样

现在是时候将前面获取到的当前动画值 value 用起来了,找到当前path的length乘以value即是当前path的最新长度

走到这里,好像跨过了山和大海,得了,困死了,睡了、睡了...

现在曲线和折线都已经绘制完成了,不过刚开始的demo里还有个渐变色的部分没有完成,貌似有了渐变色以后,显得不那么单调了,其实,我们绘图所用到的Paint还有一个属性shader,可以绘制线条或区域的渐变色,LinearGradient可实现线性渐变的效果,默认为从左到右绘制,你可以通过begin和end属性自定义绘制的方向,我们这里需要指定为从上至下,并且颜色类型为数组的形式,所以你可以传入多个颜色值来绘制

值得注意的是,通过 createShader的方式创建shader,你需要指定绘制区域的边界,我们这里要实现的是从上至下,所以就以y轴为基准,指定从上至下的绘制方向

既然是绘制渐变色,所以画笔的样式必须设置为填充状态

另外,渐变色的区域我们是通过path来指定上面的边界的,所以我们还需要指定path下面部分的起点和终点,这样形成一个闭环,才能确定出完整的区域

至此,即可实现带有渐变色的曲线或者折线,也许你有个疑问,画折线为什么也要用path呢,不是可以直接drawLine吗 ?机智如我,添加到path以后,可以更方便的绘制,添加动画也很方便

另附上最终的实现效果,至于触摸操作就不打算阐述了,可以参考以下代码

代码已发布到 Dart社区 https://pub.dev/flutter/packages?q=flutter_chart

GitHub仓库链接 https://github.com/good-good-study/flutter_chart

参考链接


Sqflite数据存储模型&无必要使用Isolate操作数据库

Sqflite存储模型如下:

依据上图Sqflite插件的DB存储模型会有2个等待队列,一个是Flutter层同步执行队列,一个是Native层的线程执行队列

  • Android实现机制是HandlerThread,底层使用同一个静态变量,因此Query/Save读写在会同一线程队列中,不同数据库的操作也需要在同一个线程队列中排队。具体参考代码 SqflitePlugin.java

  • 其iOS的实现是通过 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),...),把任务调度到系统的后台线程排队顺序执行,原理类似于HandlerThread。只是这样会造成一个问题,如果其他功能也使用系统队列,会造成不必要的延迟。iOS定义一个数据库专用线程会更好一些。具体参考代码 SqflitePlugin.m

我们在系统层调用 Sqlite 的时候,需要注意 Sqlite 调用的耗时阻塞问题,一般都是在子线程中操作数据库,而由于 Sqflite 系统层后台线程队列的存在,在 Flutter 层,其实没必要使用 isolate 对耗时操作进行规避,这点与原生开发不同。

注意

如果确实需要在 isolate 中执行 Sqflite 相关的调用,一般建议使用 FlutterIsolate 创建 isolate ,主要原因还是 Sqlite 相关的调用都是 Platform Channel 的调用。默认情况下,在 isolate 是不允许调用这些函数的。

FlutterIsolate allows creation of an Isolate in flutter that is able to use flutter plugins. It creates the necessary platform specific bits (FlutterBackgroundView on android & FlutterEngine on iOS) to enable the platform channels to work inside an isolate.

参考链接


Dart语言构造函数中存在异步方法时的处理方法

dart语言构造函数中如果存在异步方法,编程时如果不注意特殊处理的话,很容易使代码出现未定义行为。例如下面的代码:

本意是想先输出“hello“ 然后再输出”done",然而输出顺序刚好相反。原因在于init是异步方法,本应该在调用前使用await等待异步方法返回。然而在构造函数中是无法使用await的。那么该如何处理呢?下面是一种方法,使用工厂函数产生类的对象。而工厂函数是异步的,需要配合await使用。虽然能够解决异步构造的问题,但总觉得不够优雅。

输出:

参考链接


Flutter(able) 的单例模式

一个类只允许创建一个实例,那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。

作为最简单的一种设计模式之一,对于单例本身的概念,大家一看就能明白,但在某些情况下也很容易使用不恰当。相比其他语言,Dart 和 Flutter 中的单例模式也不尽相同,本篇文章我们就一起探究看看它在 Dart 和 Flutter 中的应用。

继续阅读Flutter(able) 的单例模式

Dart并发机制详解

Dart 通过 async-await、isolate 以及一些异步类型概念 (例如 Future 和 Stream) 支持了并发代码编程。本篇文章会对 async-await、Future 和 Stream
进行简略的介绍,而侧重点放在 isolate 的讲解上。

在应用中,所有的 Dart 代码都在 isolate 中运行。每一个 Dart 的 isolate 都有独立的运行线程,它们无法与其他 isolate 共享可变对象。在需要进行通信的场景里,isolate 会使用消息机制。尽管 Dart 的 isolate 模型设计是基于操作系统提供的进程和线程等更为底层的原语进行设计的,但在本篇文章中,我们不对其具体实现展开讨论。

大部分 Dart 应用只会使用一个 isolate (即 主 isolate),同时你也可以创建更多的 isolate,从而在多个处理器内核上达成并行执行代码的目的。

多平台使用时注意

所有的 Dart 应用都可以使用 async-await、Future 和 Stream
而 isolate 仅针对 原生平台的使用 进行实现。
使用 Dart 构建的网页应用可以 使用 Web Workers 实现相似的功能。

继续阅读Dart并发机制详解

Widget的物理模拟动画效果

物理模拟能够让应用富有真实感和更好的交互性。例如,你可能会为一个 widget 添加动画,让它看起来就像安着弹簧,或是在随重力下落。

这个指南演示了如何将 widget 从拖动的点移回到中心,并使用弹簧模拟效果。

这个演示将进行下面几步:

  1. 创建一个动画控制器
  2. 使用手势移动 widget
  3. 对 widget 进行动画
  4. 计算速度以模拟弹跳运动

继续阅读Widget的物理模拟动画效果

Flutter手势交互 ( 跟随手指运动的小球 )

Flutter 手势 - 跟随手指运动的小球

设置小球坐标变量 : 其中 currentX 是距离左侧边界的距离 , currentY 是距离右侧边界的距离 ;

小球的位置 : 小球是在 Stack 帧布局中的 Positioned 组件 , 其 lefttop 字段值设置其坐标 , 分别对应 currentXcurrentY 值 ;

监听事件 : 监听 GestureDetector 组件的 onPanUpdate 事件 , 其回调方法是 void Function(DragUpdateDetails details) 类型的 方法 , 可以从 DragUpdateDetails 类型参数中获取当前 x , y 的移动距离 , 该距离需要与之前的距离累加 , 才能得到准确的坐标值 ;

在回调方法中调用 setState 方法 , 修改成员变量 currentXcurrentY , 从而修改 Positioned 组件的位置 , 以达到小球移动的目的 ;

代码示例 :

完整代码示例

完整代码示例 :

继续阅读Flutter手势交互 ( 跟随手指运动的小球 )