前言:简书菜鸟一枚,主要用于记录。如有侵权,望告知,我会下架文章。
Flutter发展快速现在的稳定版已经是1.20.2了。而且我前同事跳槽去做flutter专职了,这是一个有钱景的技术。之前我们公司APP接入FlutterModule还是在他主导的,技术着实厉害,是我入坑引导人。不过我们当时接入Flutter时,SDK才1.9.1.现在都好几个大版本更新了。所以我最近想要升级一下SDK,我现在的是1.17.5。
一、升级SDK常用方式:
1.命令:执行 flutter upgrade 常用:
在已配置好Flutter环境的情况下在终端、编辑器都可执行命令;
也可以执行git 命令先拉去stable分支,然后选定回滚到指定版本[在GitHub找]。
2.下载最新或者对应版本的压缩包解压替换。[我最近买了未拆封2018款Mac,这也是我想要升级SDK的原因之一,新电脑还用旧的SDK ? 不!,所以我新下载了SDK,也简单说一下我的Mac配置环境过程,Mac新手也记录了一下基础Mac使用]。
二、Mac 配置Flutter
参考:https://flutter.dev/docs/development/tools/sdk/releases?tab=macos#macos
、https://zhuanlan.zhihu.com/p/71854245等等
1.下载SDK:git clone -b stable https://github.com/flutter/flutter.git
GitHub不稳定,经常clone失败【受阻】。
后来发现打开SDK官网https://flutter.dev/docs/development/tools/sdk/releases?tab=macos#macos。直接下载zip还更好一些。
在GitHub搜索flutter,去到flutter项目详情页,Documentation 目录下的 Install Flutter->macOS->Get the Flutter SDK 目录【有一个zip,就是stable分支最新的SDK,也可以选择 SDK archive(档案),去查看更多的SDK 版本与分支,选择自己想要的然后下载,注:下载有时也不稳定,不需翻墙,过一会再试即可。】下载好zip后放到自定义的文件夹,如SDK_FLutter。进入到该文件夹,Mac中打开(双击)zip即解压。
2.配置环境:
终端(window中我习惯叫控制台,// 代表注释,用于记录)
cd ~ // 回到Mac用户root目录
vim ~/.bash_profile // 在终端打开并编辑.bash_profile文件,输入文件开始几个字母就可以按tab键"->|"自动拼全。
英文输入下按 i ,进入编辑模式;
添加好后点击esc退出编辑模式,然后输入:wq退出文件编辑返回终端命令,
执行 source ~/.bash_profile 使刚配置的环境生效。
执行flutter --help 试试。有多行命令样例出来就是配置好环境了。类似command not found 就代表配置失败。配好后执行flutter doctor 检查flutter开发条件,会有对应编辑器的提示。就不再说了。
三、SDK升级后
参考:注:参考地址容易打不开timeout,不是需要翻墙。经实验:打开失败后切换网络再次打开容易成功打开。
activity:https://flutter.dev/docs/development/add-to-app/android/add-flutter-screen
fragment:https://flutter.dev/docs/development/add-to-app/android/add-flutter-fragment?tab=add-fragment-kotlin-tab
重大改动:以前的Flutter.CreateView()在SDK1.12开始已弃用,不再建议使用FlutterView。所以使用了Flutter module的Android项目需要对应整理代码适配。其实弄完这个交互模块后给我的感觉就是:
Native与Flutter的对接桥梁变了,以前是用Flutter.CreateView()的FlutterView,将该FlutterView,add到布局后,原生就可以通过这个桥梁打开Flutter的lib下的main.dart的main()方法【这个方法是flutter项目/内容的入口】从而将flutter代码用到了项目中。现在的SDK改变了内部封装,将桥梁换成FlutterActivity和FlutterFragment,在使用上多了个划分、解耦、更便捷了。对与跳转到纯Flutter页面可以使用FlutterActivity即可,对相应的生命周期回调处理也相对方便直接,甚至不需更多交互的页面可以一个语句几行代码搞定。对应的FlutterFragment的使用场景与之前的FlutterView比较像。其实最赞的应该是FlutterEngine的变化,现在版本使用FlutterEngine能有秒启动页面的效果速度几近原生。以下是一些较具体的说明及使用样例,其实也是按照上述两个参考地址摘录出来的[/大写的尴尬]。
1.FlutterActivity
步骤1.将FlutterActivity在Manifest.xml清单文件中注册,就像我们的MainActivity一样,至于其中的theme、softInputMode等属性根据需求自己去增添就好。参考资料的代码:
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:theme="@style/自己的Theme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
/>
步骤2.启动Flutter页面。
(1) 直接启动默认路由“/”的页面:
context.startActivity(FlutterActivity.createDefaultIntent(currentActivity))
(2) 启动指定路由“page00”的页面:
startActivity( FlutterActivity.withNewEngine().initialRoute("page00").build(context))
(3) 使用FlutterEngine,神器,推荐使用,
注意:提前创建好FlutterEngine并设置对应路由,不设置默认“/”,然后放到FlutterEngineCache中。一般在自己程序入口即自定义的XXApplication【记得在manifest.xml中引用一下】的onCreate中;本着减少APP启动初始好消耗,优化加速启动过程的想法,我试了一下在子线程中创建,但报错了,报错信息明确了要在main线程中初始化。这本来也是很轻量级的,那就继续放在application了。代码如下:
override fun onCreate() {
super.onCreate()
// 初始化Engine
var flutterEngine = FlutterEngine(this)
// + 配置一个初始路由:
flutterEngine.navigationChannel.setInitialRoute("/")
// 预执行dart入口代码,其实这时已经在执行了dart的部分初始化流程了。所以引用CacheEngine不能动态更改路由,改用NewEngine。
flutterEngine.dartExecutor.executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault())
// 存入FlutterEngineCache【类似一个全局Map】便于管理
FlutterEngineCache.getInstance().put("自定义key1",,flutterEngine)
// 通过 key-value的方式理论上可以添加多个CacheEngine 经验证有效
var flutterEngine2 = FlutterEngine(this)
flutterEngine2.navigationChannel.setInitialRoute("page01")
flutterEngine2.dartExecutor.executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault())
FlutterEngineCache.getInstance().put("自定义一个key2",flutterEngine2)
}
使用:
startActivity(FlutterActivity.withCachedEngine("自定义key1").build(currentActivity));
(4) 背景主题,有时候Activity间跳转切换需要过渡动画可能用上。或者能视觉穿透的Activity层叠【公司项目中旧代码有出现过一个独立软键盘输入Activity上半部分全透明的层叠在一个有输入框的Activity上】。
· 在style.xml资源文件中增加对应的主题如:
<style name="MyTheme" parent="@style/MyParentTheme">
<item name="android:windowIsTranslucent">true</item>
</style>
然后在mainfest.xml中注册FlutterActivity时使用该theme。即加属性:android:theme="@style/MyTheme"
· FlutterActivity构建(build)intent时使用.backgroundMode(FlutterActivity.BackgroundMode.transparent) 。如:
startActivity(FlutterActivity
//.withNewEngine() // 新Engine
.withCachedEngine("自定义key1") // 缓存Engine
.backgroundMode(FlutterActivity.BackgroundMode.transparent) // 透明模式
.build(context)
);
贼坑,昨晚补充的两个点不见了。。。20200901。仅有的创造(记录)激情也没有了!!!!
看了一下浏览器记录这是个大坑。。。我是昨晚凌晨1点左右完成的,然后居然没有简书的浏览记录!!!估计是我没关电脑、浏览器的原因。。。。
当然这也让我意识到记录的重要性。我已经记不清我昨晚具体写的内容量了。所以笔记记录是非常必要的!!!!
2.Fragment
FlutterFragment,我们先来看看应用场景,与普通Fragment、WebView应用场景一样。我一直对Fragment的使用有着一个主观认知:是一个带有生命周期的View,有初始化过程和销毁过程,能更合理、高效配置数据与资源回收。类比一下WebView,我发现其实他们也是有着共同点的,一个承载Web页面、一个承载Flutter页面,他们都是原生与第三方页面的通信桥梁。其中webview会设置WebViewClient、WebChromeClient等界定一定的基础通信,还可以使用addJavascriptInterface(mBaseWebDecorator, "Android")来拓展交互。而FlutterFragment则是由现在版本封装在底层的NativeChannel、和之前版本创建FlutterView时传入的Lifecycle,来界定一些生命周期同信,当然也有其他界定同信,也可以自定义Channel来拓展交互【这个稍晚一些抽时间也弄个文章记录一下】。以下来点简单事例:
(1)路由等设置为默认的方式:
// 默认方式,直接打开flutter module中的main的runapp()且默认路由"/"
// flutterFragment 全局变量,R.id.flutter02_flContent布局中承载fragment的FrameLayout,
// TAG_FRAGMENTFragment知识用于复用Fragment
flutterFragment = FlutterFragment.createDefault()
supportFragmentManager.beginTransaction()
.add(R.id.flutter02_flContent, flutterFragment!!, TAG_FRAGMENT)
.commit()
(2)使用FlutterEngineCache、(3)使用newEngine、主题等于FlutterActivity的基本一样就不再一一举例了,可参考项目源码。这里说一下我在事例中发现的一个奇怪点:TransparencyMode 透明模式。[Demo中的Flutter02Activity.kt、DefaultPage.dart]
布局:
<View
android:layout_width="match_parent"
android:layout_height="150dp"
android:background="#F40B0B"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="50dp"/>
<FrameLayout
android:id="@+id/flutter02_flContent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#00D8D8D8"
android:padding="50dp"
app:layout_constraintTop_toBottomOf="@id/flutter02_tvTitle"
app:layout_constraintBottom_toBottomOf="parent"
/>
<Button
android:layout_width="match_parent"
android:layout_height="75dp"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="50dp"
android:text="底部按钮"
/>
现象:图1.TransparencyMode.transparent:图2:TransparencyMode.opaque
lutterFragment = FlutterFragment.withNewEngine()
// opaque 会根据布局的层叠次序,但是Flutter内容的透明无效。transparent 不安层叠次序,Flutter透明有效。
.transparencyMode(TransparencyMode.transparent) // // 我目前的SDK版本已经不需要设置了。没有不同。
.shouldAttachEngineToActivity(false)
// 是否把引擎绑定到宿主Activity,即通过引擎是否可以交互,默认是true。如何交互,继续学习
.build()
supportFragmentManager.beginTransaction()
.add(R.id.flutter02_flContent,flutterFragment!!, TAG_FRAGMENT)
.commit()
即:opaque 会根据布局的层叠次序,但是Flutter内容的透明无效。transparent 不安层叠次序,默认在最上层,Flutter内容透明有效。
3.对应依赖(插件)的升级。
SDK升级后再跑一下之前的项目可能会遇到这样的bug:
The plugin `fluttertoast` is built using an older version of the Android plugin API
这是执行一下:flutter pub upgrade 可以看到有多少插件需要升级,可以选择去插件库https://pub.flutter-io.cn/packages/ 【能搜索】查看对应的合适版本,或者再执行一下:flutter pub outdated 看到对应的版本状态。然后修改pubspec.yaml文件中的依赖就好。
父目录:first_module walke$ flutter pub outdated
Dependencies Current Upgradable Resolvable Latest
fluttertoast *3.1.3 *3.1.3 7.0.4 7.0.4
...
最后:附上源码地址:
Android:Learning/KtPractice/app/src/main/java/com/walke/ktpractice/with_flutter/
Flutter:Learning/Flutter/Module/first_module/lib/
最近才开始写文章,如有相同,纯属意外,无意侵权,望告知。