当前位置:首页 > 安卓源码 > 技术博客 >

Android工程内嵌Flutter,跨平台的渐进式解决方案

时间:2019-01-11 14:54 来源:互联网 作者:源码搜藏 浏览: 收藏 挑错 推荐 打印

其实2017年的时候就已经接触Flutter了,但也只是写了个HelloWorld,一方面是Flutter在那时候还只是preview版本,另一方面ReactNative在那时候非常火热,忙于用ReactNative重构项目,错过了入坑Flutter的第一梯队。 在谷歌的2018IO大会上Flutter再一次成为了
Android工程内嵌Flutter,跨平台的渐进式解决方案

 

 

其实2017年的时候就已经接触Flutter了,但也只是写了个HelloWorld,一方面是Flutter在那时候还只是preview版本,另一方面ReactNative在那时候非常火热,忙于用ReactNative重构项目,错过了入坑Flutter的第一梯队。

在谷歌的2018IO大会上Flutter再一次成为了跨平台方案的焦点,而ReactNative也在随着Airbnb的弃用热度逐渐冷却,其实在写下这篇文章的时候我已经再次入坑了不短的一段时间,Flutter的各种特性也基本上都接触到了,demo项目也写了一些,但致使我迫不及待的写下这篇文章的直接原因是Flutter的这个能力:

Flutter能够无感知的嵌入到Android工程中,不管是从开发者角度还是用户角度,你甚至可以只从一个view开始来让Flutter参与到你的项目中去,接着替换或者开发某一个页面甚至功能,然后你就会对它爱不释手,让你会有用它重构项目和开发新项目的冲动。

  • 用户:毫秒级的加载速度,无论是view还是页面,基本上和原生无异。
  • 开发:只作为一个module引入工程,代码入侵极小,Android工程和Flutter工程互不相干。
Android工程内嵌Flutter,跨平台的渐进式解决方案

 

注意:当前日期是2018-07-29,flutter的beta版本还没有加入这个新功能,使用命令flutter channel [分支]切换到dev或master分支才能使用,如果你阅读本篇文章离这个时间点是很久之后可以忽略这段。

创建一个Android工程模拟你的现有工程

为了让Android工程和Flutter工程互不干扰,这里不再以Android工程为工程的跟目录,而是让Android工程和平级的Flutter工程的公共目录作为根目录。

最终的目录结构应该是下面这样的

你的项目根目录(随便什么你喜欢的地方)
 ├── 原生安卓工程(FlutterInAndroid)
 └── Flutter工程 (my_flutter)

所以首先在你的项目根目录下用AS创建一个新的Android原生项目,可以勾选上kotlin支持,这样更舒服。

创建完成后你会得到一个这样的结构

你的项目根目录(随便什么你喜欢的地方)
 └── FlutterInAndroid

 

FlutterInAndroid目录内是一个完整的Android工程

module模式创建Flutter工程

接下来使用Flutter命令来创建module工程,在你的项目根目录下执行:

flutter create -t module my_flutter

 

创建完成后你会得到一个这样的结构

你的项目根目录(随便什么你喜欢的地方)
 ├── FlutterInAndroid
 └── my_flutter

my_flutter是一个Flutter的module工程,用来供Android项目引入

在Android工程中引入依赖

在FlutterInAndroid这个Android工程的setting.gradle文件中追加flutter工程的引入

你的项目跟目录/FlutterInAndroid/setting.gradle

include ':app'
//加入下面配置
setBinding(new Binding([gradle: this]))
evaluate(new File(
 settingsDir.parentFile,
 'my_flutter/.android/include_flutter.groovy'
))

在app的build.gradle文件中加入工程依赖

你的项目跟目录/FlutterInAndroid/app/build.gradle

...
dependencies {
 ...
 // 加入下面配置
 implementation project(':flutter')
}

使用AS打开FlutterInAndroid工程,重新构建项目,即可成功的将Flutter加入Android工程。

在Android工程中创建Flutter的View

Flutter提供了两种方式让Android工程来引用组件,一种是View,一种是Fragment,这里选用View来进行讲解,Fragment同理。

这里我们用两种方式来引入FLutter,本质是还是是作为一个view引入布局还是将FlutterView作为Activity的根View。

以单个view引入布局

val flutterView = Flutter.createView(this,lifecycle,"route1")

 

通过上面很简单的一个方法,我们就能通过Flutter创建出一个view,这个方法提供三个参数,第一个是Activity,第二个参数是一个Lifecycle对象,我们之间取Activity的lifecycle即可,第三个参数是告诉Flutter我们要创建一个什么样的view,这个字符串参数可以在Flutter工程中获取得到。

创建出这个FlutterView之后就可以按常规的操作来将它加入到任何你想要的布局中去了。

以根view作为Activity

创建一个空的Activity,用Flutter创建一个View作为页面的根View:

class FlutterActivity : AppCompatActivity() {
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 setContentView(R.layout.activity_flutter)
 val flutterView = Flutter.createView(this@FlutterActivity,lifecycle,"route1")
 val layout = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
 addContentView(flutterView, layout)
 }
}

这里我们并没有使用setContentView而是是用了addContentView这个方法,原因是这样的:

虽然FLutter的加载速度非常快,但是这个过程依然存在,在创建FLutterView之前我们先给ContentView设置了一个R.layout.activity_flutter布局,这个布局可以作为FlutterView加载完成之前展示给用户的界面,当然大部分情况下用户根本感知不到这个界面Flutter已经加载完成了,但我们仍需要它,因为debug模式下造成Flutter的加载速度并不是非常快,这个界面可以给开发人员看,还有就是如果没有这个界面的话在Activity的加载过程会出现一个黑色的闪屏,而这个情况对用户来说并不友好。

在Flutter工程中根据不同的route创建不同的组件

用AndroidStudio在你的项目跟目录/my_flutter打开Flutter工程,这时候AndroidStudio插件会识别到Flutter工程并以Flutter工程进行加载。

忽略掉.android和.ios文件夹之后你会发现,这个FLutter工程和完整的Flutter工程并没有任何不同,你依然能够以完整Flutter工程的流程来进行Flutter开发并启动调试,这是一个非常人性化的设计。

上面我们在原生Android工程中以View的形式调用了Flutter,而Flutter本质上是只有一个入口的,也就是main.dart文件中的main函数:

void main() => runApp(new MyApp());

 

我们的目的是根据原生工程的调用让Flutter生成不同的组件作为View来供原生工程使用,那么我们就可以从这个main函数来入手。

通过文档我们可以通过window的全局变量中获取到当前的routeName,这个值正是上面通过原生工程传给Flutter的标识,有了这个标识就可以简单的做判断来进行不同的组件创建了:

import 'dart:ui';
import 'package:flutter/material.dart';
void main() => runApp(_widgetForRoute(window.defaultRouteName));
//根据不同的标识创建不同的组件给原生工程调用
Widget _widgetForRoute(String route) {
 switch (route) {
 case 'route1':
 return SomeWidget(...);
 case 'route2':
 return SomeOtherWidget(...);
 default:
 return Center(
 child: Text('Unknown route: $route', textDirection: TextDirection.ltr),
 );
 }
}

让Flutter模块支持热加载

首先在Flutter目录下启动监听服务,在你的项目根目录/my_flutter下执行:

flutter attach

 

执行后,监听服务会等待并监听debug应用中flutter的状态

然后在打开FlutterInAndroid项目的AS中以正常方式调试运行,在真机或模拟器中运行app后并不会立即出发flutter的监听服务,当flutter的view或Fragment激活时才会触发。

当flutter的监听服务和app建立连接后,终端会出现如下输出:

$ flutter attach -d W8
Waiting for a connection from Flutter on PLK UL00...
Done.
Syncing files to device PLK UL00... 8.7s
 To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R".
An Observatory debugger and profiler on PLK UL00 is available at: http://127.0.0.1:54218/
For a more detailed help message, press "h". To quit, press "q".

这时我们修改flutter工程中的dart代码文件,保存后在终端中点击r键即可进行热加载,R键进行热重启。

签名打包

引入flutter工程后,对Android原生工程的构建基本上没有影响,打包按常规操作即可。

Flutter创建的module工程中的Android工程与纯Flutter工程的中Android工程的比较

Android工程内嵌Flutter,跨平台的渐进式解决方案

为了方便描述我们称前者为module工程,后者为完整工程。

由此可见,虽然module工程中提供了名为Flutter的module供原生工程调用,但仍然保留了app工程,这样非常大程度的方便了flutter工程师来单独开发flutter项目,无需依赖任何原生的调用,自身即可启动调试。

Android工程内嵌Flutter,跨平台的渐进式解决方案 转载https://www.codesocang.com/appboke/39210.html

技术博客阅读排行

最新文章