1.如何用 Flutter 实现混合开发?闲鱼公开源代码实例
2.Flutter 小技巧之不一样的源码思路实现炫酷 3D 翻页折叠动画
3.在Flutter中使用setState时的6个简单技巧
4.手机dart是什么意思?
5.GitHub 上有哪些值得推荐的开源电子书?
6.搭建一个摄像头应用程序 应用程序内部摄像头
如何用 Flutter 实现混合开发?闲鱼公开源代码实例
阿里妹导读:具有一定规模的 App 通常有一套成熟通用的基础库,尤其是源码阿里系 App,一般需要依赖很多体系内的源码基础库。那么使用 Flutter 重新从头开发 App 的源码成本和风险都较高。所以在 Native App 进行渐进式迁移是源码 Flutter 技术在现有 Native App 进行应用的稳健型方式。
今天我们来看看,源码暴雪2017源码闲鱼团队如何在这个实践过程中沉淀出一套独具特色的源码混合技术方案。
现状及思考
闲鱼目前采用的源码混合方案是共享同一个引擎的方案。这个方案基于这样一个事实:任何时候我们最多只能看到一个页面,源码当然有些特定的源码场景你可以看到多个 ViewController ,但是源码这些特殊场景我们这里不讨论。
我们可以这样简单去理解这个方案:我们把共享的源码 Flutter View 当成一个画布,然后用一个 Native 的源码容器作为逻辑的页面。每次在打开一个容器的源码时候我们通过通信机制通知 Flutter View 绘制成当前的逻辑页面,然后将 Flutter View 放到当前容器里面。源码
这个方案无法支持同时存在多个平级逻辑页面的情况,因为你在页面切换的时候必须从栈顶去操作,无法再保持状态的同时进行平级切换。举个例子:有两个页面A,B,当前B在栈顶。切换到A需要把B从栈顶 Pop 出去,此时B的状态丢失,如果想切回B,我们只能重新打开B之前页面的状态无法维持住。
如在 pop 的过程当中,可能会把 Flutter 官方的 Dialog 进行误杀。而且基于栈的操作我们依赖对 Flutter 框架的一个属性修改,这让这个方案具有了侵入性的特点。
新一代混合技术方案 FlutterBoost
重构计划
在闲鱼推进 Flutter 化过程当中,更加复杂的页面场景逐渐暴露了老方案的局限性和一些问题。所以我们启动了代号 FlutterBoost(向C++ Boost库致敬)的新混合技术方案。这次新的混合方案我们的主要目标有:
跟老方案类似,新的方案还是采用共享引擎的模式实现。主要思路是由 Native 容器 Container 通过消息驱动 Flutter 页面容器 Container,从而达到 Native Container与 Flutter Container 的同步目的。我们希望做到 Flutter 渲染的内容是由 Naitve 容器去驱动的。
简单的理解,我们想做到把 Flutter 容器做成浏览器的感觉。填写一个页面地址,私彩源码分享然后由容器去管理页面的绘制。在 Native 侧我们只需要关心如果初始化容器,然后设置容器对应的页面标志即可。
主要概念
Native 层概念
Dart 层概念
关于页面的理解
在 Native 和 Flutter 表示页面的对象和概念是不一致的。在 Native,我们对于页面的概念一般是 ViewController,Activity。而对于 Flutter 我们对于页面的概念是 Widget。我们希望可统一页面的概念,或者说弱化抽象掉 Flutter 本身的 Widget 对应的页面概念。换句话说,当一个 Native 的页面容器存在的时候, FlutteBoost 保证一定会有一个 Widget 作为容器的内容。所以我们在理解和进行路由操作的时候都应该以 Native 的容器为准, Flutter Widget 依赖于 Native 页面容器的状态。
那么在 FlutterBoost 的概念里说到页面的时候,我们指的是 Native 容器和它所附属的 Widget。所有页面路由操作,打开或者关闭页面,实际上都是对 Native 页面容器的直接操作。无论路由请求来自何方,最终都会转发给 Native 去实现路由操作。这也是接入 FlutterBoost 的时候需要实现 Platform 协议的原因。
另一方面,我们无法控制业务代码通过 Flutter 本身的 Navigator 去 push 新的 Widget。对于业务不通过 FlutterBoost 而直接使用 Navigator 操作 Widget 的情况,包括 Dialog 这种非全屏 Widget,我们建议是业务自己负责管理其状态。这种类型 Widget 不属于 FlutterBoost 所定义的页面概念。
理解这里的页面概念,对于理解和使用 FlutterBoost 至关重要。
与老方案主要差别
前面我们提到老方案在 Dart 层维护单个 Navigator 栈结构用于 Widget 的切换。而新的方案则是在 Dart 侧引入了 Container 的概念,不再用栈的结构去维护现有的页面,而是通过扁平化 key-value 映射的形式去维护当前所有的页面,每个页面拥有一个唯一的 id。这种结构很自然的支持了页面的查找和切换,不再受制于栈顶操作的问题,之前的微挚 活动 源码一些由于 pop 导致的问题迎刃而解。也不需要依赖修改 Flutter 源码的形式去进行页面栈操作,去掉了实现的侵入性。
实际上我们引入的 Container 就是 Navigator 的,也就是说一个 Native 的容器对应了一个 Navigator。那这是如何做到的呢?
多 Navigator 的实现
Flutter 在底层提供了让你自定义 Navigator 的接口,我们自己实现了一个管理多个 Navigator 的对象。当前最多只会有一个可见的 Flutter Navigator,这个 Navigator 所包含的页面也就是我们当前可见容器所对应的页面。
Native 容器与 Flutter 容器(Navigator)是一一对应的,生命周期也是同步的。当一个 Native 容器被创建的时候,Flutter 的一个容器也被创建,它们通过相同的 id 关联起来。当 Native 的容器被销毁的时候,Flutter 的容器也被销毁。Flutter 容器的状态是跟随 Native 容器,这也就是我们说的 Native 驱动。由 Manager 统一管理切换当前在屏幕上展示的容器。
我们用一个简单的例子描述一个新页面创建的过程:
这就是一个新页面创建的主要逻辑,销毁和进入后台等操作也类似有 Native 容器事件去进行驱动。
官方提出的混合方案
基本原理
Flutter 技术链主要由 C++ 实现的 Flutter Engine 和 Dart 实现的 Framework 组成(其配套的编译和构建工具我们这里不参与讨论)。Flutter Engine 负责线程管理,Dart VM 状态管理和 Dart 代码加载等工作。而 Dart 代码所实现的 Framework 则是业务接触到的主要 API,诸如 Widget 等概念就是在 Dart 层面 Framework 内容。
一个进程里面最多只会初始化一个 Dart VM。然而一个进程可以有多个 Flutter Engine,多个 Engine 实例共享同一个 Dart VM。
我们来看具体实现,在 iOS 上面每初始化一个 FlutterViewController 就会有一个引擎随之初始化,也就意味着会有新的线程(理论上线程可以复用)去跑 Dart 代码。Android 类似的 Activity 也会有类似的效果。如果你启动多个引擎实例,注意此时Dart VM 依然是共享的,只是不同 Engine 实例加载的代码跑在各自独立的 Isolate。
官方建议
引擎深度共享
在混合方案方面,我们跟 Google 讨论了可能的一些方案。Flutter 官方给出的建议是从长期来看,我们应该支持在同一个引擎支持多窗口绘制的号码查询系统源码能力,至少在逻辑上做到 FlutterViewController 是共享同一个引擎的资源的。换句话说,我们希望所有绘制窗口共享同一个主 Isolate。
但官方给出的长期建议目前来说没有很好的支持。
多引擎模式
我们在混合方案中解决的主要问题是如何去处理交替出现的 Flutter 和 Native 页面。Google 工程师给出了一个 Keep It Simple 的方案:对于连续的 Flutter 页面(Widget)只需要在当前 FlutterViewController 打开即可,对于间隔的 Flutter 页面我们初始化新的引擎。
例如,我们进行下面一组导航操作:
我们只需要在 Flutter Page1 和 Flutter Page3 创建不同的 Flutter 实例即可。
这个方案的好处就是简单易懂,逻辑清晰,但是也有潜在的问题。如果一个 Native 页面一个 Flutter 页面一直交替进行的话,Flutter Engine 的数量会线性增加,而 Flutter Engine 本身是一个比较重的对象。
多引擎模式的问题
因此,综合多方面考虑,我们没有采用多引擎混合方案。
总结
目前 FlutterBoost 已经在生产环境支撑着在闲鱼客户端中所有的基于 Flutter 开发业务,为更加负复杂的混合场景提供了支持,稳定为亿级用户提供服务。
我们在项目启动之初就希望 FlutterBoost 能够解决 Native App 混合模式接入 Flutter 这个通用问题。所以我们把它做成了一个可复用的 Flutter 插件,希望吸引更多感兴趣的朋友参与到 Flutter 社区的建设。在有限篇幅中,我们分享了闲鱼在 Flutter 混合技术方案中积累的经验和代码。欢迎兴趣的同学能够积极与我们一起交流学习。
扩展补充
在两个 Flutter 页面进行切换的时候,因为我们只有一个 Flutter View 所以需要对上一个页面进行截图保存,如果 Flutter 页面多截图会占用大量内存。这里我们采用文件内存二级缓存策略,在内存中最多只保存 2-3 个截图,其余的写入文件按需加载。这样我们可以在保证用户体验的同时在内存方面也保持一个较为稳定的水平。
页面渲染性能方面,Flutter 的 AOT 优势展露无遗。在页面快速切换的时候,Flutter 能够很灵敏的响应页面的切换,在逻辑上创造出一种 Flutter 多个页面的androidstudio2048源码感觉。
项目开始的时候我们基于闲鱼目前使用的 Flutter 版本进行开发,而后进行了 Release 1.0 兼容升级测试目前没有发现问题。
只要是集成了 Flutter 的项目都可以用官方依赖的方式非常方便的以插件形式引入 FlutterBoost,只需要对工程进行少量代码接入即可完成接入。详细接入文档,请参阅 GitHub 主页官方项目文档。
Flutter 小技巧之不一样的思路实现炫酷 3D 翻页折叠动画
在Flutter动画实现中,有一个有趣的话题——如何制作一个3D折叠动画效果?许多人可能会首先想到使用Dart中的矩阵变换和Canvas来实现。
这种效果在当前的小说阅读器场景中非常常见,而类似的翻页效果通常是通过这种思路实现的。例如,我曾经尝试过的《炫酷的3D卡片和帅气的°展示效果》以及《用纯代码实现立体Dash和3D掘金Logo》都是利用矩阵变换实现的视觉3D效果。
然而,今天我要介绍一个名为riveo_page_curl的项目,它提供了一个与众不同的实现方法,即通过自定义Fragment Shaders来实现动画。这种方式允许开发者使用GLSL语言进行编程,从而通过GPU渲染出更丰富的图形效果。
在介绍这个项目之前,我们先来了解一下Fragment Shader。自Flutter 3.7版本开始,就提供了Fragment Shader API。这个API允许开发者直接介入到Flutter渲染管道的渲染流程中。
直接使用Fragment Shader而不是Dart矩阵变换的好处是,它可以减少CPU的耗时。通过图形语言(GLSL)直接给GPU发送指令,性能上可以得到提升,并且实现起来更简洁。不过,加载着色器可能会带来一定的开销,因此需要在运行时将其编译为特定于平台的着色器。
在Flutter中使用Fragment Shader也有一些条件限制,例如需要引入特定的头文件,以及只支持.frag格式的文件。
如果需要将已有的GLSL效果(如shadertoy上的代码)搬运到Flutter中,可能需要进行一些代码改造。以下是一段渐变动画的着色器示例,以及它在Flutter中的对应代码。
通过这些片段着色器,我们可以获得极其丰富的渲染效果。在Flutter中,渐变动画的完整代码可以在GitHub上的相应仓库中找到。
riveo_page_curl项目中的折叠着色器包含了一些矩阵变换和三角函数计算。它的核心在于计算弯曲部分的弧度,并增加阴影投影以提高视觉效果。
在Dart层,除了ShaderBuilder之外,还可以通过flutter_shaders的AnimatedSampler来实现更简洁的shader、image和canvas的配合。
完整项目可以在GitHub上查看。相比在Dart层实现3D翻页折叠,使用FragmentShader实现的代码更加简洁,性能体验也更优秀。此外,通过简单的移植适配,ShaderToy里的着色器代码可以直接在Flutter中使用,这对于Flutter在游戏场景的实现非常有帮助。
最后,Flutter 3.版本之后,Flutter Web也支持了fragment shaders,因此着色器在Flutter的实现已经相对成熟。如果将之前通过Flutter实现的《霓虹灯文本的「故障」效果》的逻辑转换成fragment shaders来完成,性能和代码简洁程度也可能会有所提升。
在Flutter中使用setState时的6个简单技巧
setState函数是在Flutter应用程序中管理状态的最基本方法。以下是一些保持应用可维护性的最佳实践。StatefulWidget的setState函数是一种在Flutter应用程序中管理状态的简单方法。但是,当您希望您的应用程序正常工作和高性能时,您需要避免几个陷阱。以下是您应该坚持的一些最佳实践。
setState有什么用?setState是Flutter发出rebuild(重建)当前widget及其后代的方式。在rebuild过程中,最新的变量值将被用于创建用户界面。比方说,一个用户将一个开关从打开切换到关闭。该开关有一个存储该值的支持变量,所以在改变之后,它被设置为false。开关本身并不反映这一变化,直到它被重建为新的支持字段值。
更改值
调用setState()
用户界面已更新
?技巧1:保持##widgets小!setState触发了对你当前所在的小组件的重建。如果你的整个应用程序只包含一个widget,那么整个widget将被重建,这将使你的应用程序变得缓慢。请看下面的例子。
import'package:flutter/material.dart';classHomeextendsStatefulWidget{ constHome({ Key?key}):super(key:key);@overrideState<Home>createState()=>_State();}class_StateextendsState<Home>{ bool_tile1=false;bool_tile2=false;bool_tile3=false;bool_tile4=false;bool_tile5=false;@overrideWidgetbuild(BuildContextcontext){ print("builtmethodHome");//<--setStatetriggersbuildhere!returnScaffold(appBar:AppBar(title:constText("Demo")),body:Center(child:Column(crossAxisAlignment:CrossAxisAlignment.center,mainAxisAlignment:MainAxisAlignment.center,children:<Widget>[SwitchListTile(activeColor:Colors.green,inactiveThumbColor:Colors.red,title:Text("Switchis${ _tile1?"on":"off"}"),value:_tile1,onChanged:(_){ setState((){ _tile1=!_tile1;});}),SwitchListTile(activeColor:Colors.green,inactiveThumbColor:Colors.red,title:Text("Switchis${ _tile2?"on":"off"}"),value:_tile2,onChanged:(_){ setState((){ _tile2=!_tile2;});}),SwitchListTile(activeColor:Colors.green,inactiveThumbColor:Colors.red,title:Text("Switchis${ _tile3?"on":"off"}"),value:_tile3,onChanged:(_){ setState((){ _tile3=!_tile3;});}),SwitchListTile(activeColor:Colors.green,inactiveThumbColor:Colors.red,title:Text("Switchis${ _tile4?"on":"off"}"),value:_tile4,onChanged:(_){ setState((){ _tile4=!_tile4;});}),SwitchListTile(activeColor:Colors.green,inactiveThumbColor:Colors.red,title:Text("Switchis${ _tile5?"on":"off"}"),value:_tile5,onChanged:(_){ setState((){ _tile5=!_tile5;});})])));}}这里我们在一个Column中有5个SwitchListTile小部件,它们都是同一个小部件的一部分。
如果您切换任何控件,整个屏幕都会被重建。Scaffold,AppBar,Column,...但只要重建已更改的小部件就足够了。让我们看下一个代码示例:
import'package:flutter/material.dart';classHome2extendsStatefulWidget{ constHome2({ Key?key}):super(key:key);@overrideState<Home2>createState()=>_State();}class_StateextendsState<Home2>{ @overrideWidgetbuild(BuildContextcontext){ print("builtmethodHome2");returnScaffold(appBar:AppBar(title:constText("Demo")),body:Center(child:Column(crossAxisAlignment:CrossAxisAlignment.center,mainAxisAlignment:MainAxisAlignment.center,children:const<Widget>[Switch(),Switch(),Switch(),Switch(),Switch()])));}}classSwitchextendsStatefulWidget{ constSwitch({ Key?key}):super(key:key);@overrideState<StatefulWidget>createState()=>_SwitchState();}class_SwitchStateextendsState<Switch>{ bool_value=false;@overrideWidgetbuild(BuildContextcontext){ print("buildmethodSwitch");//<--setStatetriggersbuildhere!returnSwitchListTile(activeColor:Colors.green,inactiveThumbColor:Colors.red,title:Text("Switchis${ _value?"on":"off"}"),value:_value,onChanged:(_){ setState((){ _value=!_value;});});}}在这里,我们将SwitchListTile包装在单个StatefulWidget中。页面看起来相同,但如果您单击此示例中的任何开关,则只有单击的小部件将重建。
?技巧2:不要在构建方法中调用setState来自FlutterAPI文档
这个方法有可能在每一帧中被调用,除了建立一个小部件外,不应该有任何副作用。
build方法旨在构建小部件树,因此我们应该保持这种方式。不要在这里做花哨的事情,它会减慢你的应用程序。对setState的调用可能会触发额外的重建,在最坏的情况下,你可能最终会出现一个异常,告诉你目前有一个重建正在进行。
?技巧3:不要在initState方法中调用setStateinitState将在完成后触发重建,因此无需在此方法中调用setState。此方法旨在初始化与状态相关的属性,例如设置默认值或订阅流。不要在这里做任何其他事情!
?技巧4:setState()和setState(...)是相等的像这样使用setState没关系
setState((){ _text=“Hello”;});或者像这样
_text=“Hello”;setState((){ });结果是一样的。
?技巧5:setState(...)代码必须很小不要在setState内做任何大的计算,因为它将阻止你的应用程序重绘。请看下面的示例代码:
setState((){ for(vari=0;i<;i++)print(i);_value=true;});只有在打印语句之后,小部件才会重建。在这段时间里,你的应用程序不会对用户的操作做出反应,它将在之后执行这些操作。因此,如果用户因为没有视觉反馈而多次点击一个控件,多次重建就会堆积起来,会使应用程序的速度更慢。
一个更好的方法是在执行一个长期运行的操作时显示一个进度指示器,这样用户就知道正在发生一些事情,他需要等待完成。
?技巧6:setState(...)代码不能是异步的运行代码时
setState(()async{ awaitFuture.delayed(constDuration(seconds:5));});你最终会得到一个类似这样的异常信息:
在方法之外执行异步操作,然后调用它。
结束我希望这些见解能帮助你更好地理解Flutter中setState的机制。坚持这些技巧,你会有更少的问题和更快的应用程序。源代码例子可以在GitHub上找到。
原文:/post/
手机dart是什么意思?
Dart是一种由Google开发的编程语言,被广泛用于开发Web、移动应用、桌面应用和服务器应用。它是一种执行性能高、安全、可扩展、面向对象的语言,它拥有丰富的库和工具,可让开发人员更加容易地构建出高质量的应用。
Dart与其他编程语言相比有哪些优点?
Dart相对于其他编程语言而言,具有许多优点。首先,它具有高性能,可快速编译为本机代码。其次,它是一种强类型语言,使得运行时错误更少。此外,Dart有一个优秀的工具集,包括编辑器、调试器和包管理器,大大提高了生产力和代码质量。最后,它还拥有异步编程的良好支持,这在构建高并发应用时非常有用。
Dart未来的发展前景如何?
在谷歌的积极推动下,Dart拥有良好的生态体系,它在Web、移动应用、桌面应用和服务器应用等多个领域都有广泛应用和成功案例。未来,Dart将继续完善其生态体系,增加新的特性,提高性能,以适应不断增长的应用需求。据调查显示,Dart在GitHub上的使用人数逐年增加,越来越多的开发者青睐于使用Dart进行应用开发。我们相信,Dart一定会与时俱进,成为未来应用开发的重要工具。
GitHub 上有哪些值得推荐的开源电子书?
在GitHub的海洋中,无数的开源电子书犹如璀璨的繁星,为编程爱好者们点亮知识的道路。以下是一些备受推荐的开源资源,涵盖了多种编程语言、框架、技术领域,助你快速提升技能和理解。前端与网络
- Chrome开发者工具指南
- Grunt工具实战
- 移动Web前端入门
- Node.js深入探索
大数据与人工智能
- 大数据挖掘实战
- 推荐系统原理
- 机器学习入门教程
- Apache Spark官方文档
编程艺术与理论
- 内存管理与游戏编程原理
- OpenWrt智能路由器开发教程
语言与框架
- AWK编程基础
- C/C++并发编程指南
- Java EE深入学习
- Dart语言实战
- Python库和框架文档
- PHP开发最佳实践
- Swift编程实践
CSS与HTML
- CSS布局与设计
- Emmet代码编写技巧
- React Native文档
Web开发
- Vue.js官方文档
- Flask快速入门
- React Router教程
移动开发
- iOS开发进阶教程
- Android开发者指南
后端与框架
- Django高级应用开发
- Ruby on Rails核心概念
- Go语言高级编程
科学计算
- R语言数据处理
- Haskell函数式编程实践
- Scala并发编程与设计模式
移动与工具
- Swift编程进阶
- Vim与Emacs定制教程
编程资源与学习笔记
- 《编程珠玑》读书笔记
- 编译原理习题解析
- 专业编程指南和实践案例
自动化测试
- Appium测试框架指南
每一本书籍和教程都是开源社区智慧的结晶,通过这些开源电子书,你可以深入了解技术细节,跨越学习曲线,成为编程领域的佼佼者。探索这些资源,让知识的力量驱动你的编程之旅。
搭建一个摄像头应用程序 应用程序内部摄像头
在许多应用程序中,集成一个应用程序内部的摄像头功能是可能的。Flutter 提供了一个名为 camera 的 pub.dev/packages/camera 插件,它允许我们在项目中实现这一需求。首先,在 pubspec.yaml 文件中添加 camera 插件以安装到项目中。确保设备支持 iOS .0 或更高版本,并在 Info.plist 文件中设置相应的参数。对于 Android,将 app/build.gradle 文件中 Android SDK 的最小版本更新为 或更高。
接下来,创建两个屏幕:CameraScreen 和 GalleryScreen。CameraScreen 将显示相机输出并允许用户拍摄,而 GalleryScreen 则用于在网格视图中显示捕获的。
在 main.dart 文件中,通过加载相机预览并将其传递给 CameraScreen。使用 camera 插件提供的方法初始化选定的相机,如后置摄像头。然后在 CameraScreen 中创建实时相机预览,并添加三个按钮以支持切换相机、拍摄照片和查看画廊功能。
为了实现摄像头在前后之间的切换,可使用相同的初始化方法并动态更改 cameraIndex。点击捕获按钮时,使用相机控制器拍摄照片,并将添加到数组中。点击显示画廊按钮,导航至 GalleryScreen 并显示已捕获的。
在 GalleryScreen 中,获取捕获的图像列表并在 GridView 中显示。通过这些屏幕和功能,您已经具备了一个基本的内部摄像头应用程序。
使用 camera 插件,您还能捕捉视频。通过 startVideoRecording、pauseVideoRecording 和 stopVideoRecording 方法,可以实现视频录制功能。完成项目构建后,即可看到最终效果。
该项目的源代码已发布在 GitHub,供您参考和学习。
github.com/jagrut-/fl...