Android工程内嵌Flutter

Posted 骑猪撞交警

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android工程内嵌Flutter相关的知识,希望对你有一定的参考价值。

本文记录一下android主工程中嵌入部分Fluttter页面的实现方法。

创建一个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工程的比较

区别Flutter的module工程中的Android工程纯Flutter工程中的Android工程
文件夹名称 .android android
包含的module app和Flutter app
说明1 app只提供了入口Activity,Flutter包含了插件扩展及原生工程调用的接口 app包含入口Activity及插件扩展
说明2 app供Flutter自身开发调试,Flutter作为module供Android原生调用 app作为Android工程运行及打包

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

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













以上是关于Android工程内嵌Flutter的主要内容,如果未能解决你的问题,请参考以下文章

FlutterFlutter 混合开发 ( 关联 Android 工程与 Flutter 工程 | 安卓页面中嵌入 Flutter 页面 | 安卓中启动 Flutter 页面 )

错误记录Flutter 混合开发获取 BinaryMessenger 报错 ( FlutterActivityAndFragmentDelegate.getFlutterEngine() )(代码片段

Android Lint——内嵌于Android Studio的代码优化工具

Andriod使用webview控件往APP里内嵌网页

flutter Web端支持内嵌加载网页

Flutter工程解析篇 + 工程代码部分解析记录