使用flutter难免会借助原生view实现相关功能,直接在项目的FlutterActivity中实现不利于管理和组件化的实现,以下通过flutter plugin进行实现进行记录。
1. 新建flutter plugin项目
以android studio为例,new -> new flutter project,project type选择plugin,填写项目相关信息,然后选择finish。
2. 原生代码的实现(以android、kotlin为例)
(1)打开原生Android目录
构建项目后,studio会给我们创建好相关文件,我们需要在studio菜单栏 -> File -> Open打开plugin目录里的android项目(直接使用右键flutter插件打开是example的,不是我们需要的)。
(2)gradle配置
打开后我们需要在build.gradle中添加以下配置(原生代码修改完后记得将这些代码进行删除)
//获取local.properties配置文件
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') {
reader -> localProperties.load(reader)
}
}
//获取flutter的sdk路径
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
dependencies {
implementation files("$flutterRoot/bin/cache/artifacts/engine/android-arm/flutter.jar")
}
如果你的kotlin代码方法参数里使用了Unit,请将kotlin版本改为1.5.10以上(flutter sdk在2.10.x 以下的话)
配置gradle.properties文件:默认是没有这个文件的,需要创建并粘贴以下参数(如果你不用androidX,android ndk,并对gradle编译并不需要优化就不用添加)
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx8126m -Dfile.encoding=UTF-8
Android.useDeprecatedNdk=true
android.useAndroidX=true
android.enableJetifier=true
#Android.overridePathCheck=true
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
android.nonTransitiveRClass=true
# 使用并行编译
org.gradle.parallel=true
# 启用新的孵化模式
org.gradle.configureondemand=true
# 编译时使用守护进程
org.gradle.daemon=true
(3)开始实现原生功能
创建一个ViewPlugin类:
class ViewPlugin(val context: Context?, binaryMessenger: BinaryMessenger) : PlatformView, MethodChannel.MethodCallHandler {
private var channel: MethodChannel
private lateinit var view: TextView
init {
channel = MethodChannel(binaryMessenger, "rainblog.cn/view_plugin")
channel.setMethodCallHandler(this)
context?.let {
view = TextView(it)
view.text = "xxx"
}
}
override fun getView(): View = view
override fun dispose() {
channel.setMethodCallHandler(null)
//需要释放的相关资源
}
override fun onMethodCall(call: MethodCall, p1: MethodChannel.Result) {
//创建后flutter端与原生端通讯,执行相关操作
when (call.method) {
"init" -> {
view.text = "aaa"
}
}
}
}
这个类的实现我们需要引入PlatformView, MethodChannel.MethodCallHandler接口并实现相关方法,将原生view通过getView()方法进行返回;dispose()方法对资源进行释放;onMethodCall(call: MethodCall, p1: MethodChannel.Result)实现flutter与原生android的通信执行原生相关操作;以上代码原生view的实现就完成了,接下来需要实现工厂方法:
class ViewFactory(private val binaryMessenger: BinaryMessenger) : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(context: Context?, p1: Int, message: Any?): PlatformView {
return ViewPlugin(context, binaryMessenger)
}
}
继承PlatformViewFactory并重写create方法,将刚才的ViewPlugin进行初始化返回,在create的方法中context是我们构建view所需要的,message为flutter端AndroidView的creationParams参数返回,因为这个实例用的是MethodChannel进行,就没有使用,如果用不上MethodChannel也不需要中途与原生的通信就可以用creationParams参数返回进行相关的初始化。p1为flutter端AndroidView的creationParamsCodec返回。接下来就需要注册我们的ViewPlugin原生端的实现就大功告成了~
进入flutter默认创建好的Plugin中进行注册:
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
...
flutterPluginBinding.platformViewRegistry.registerViewFactory("rainblog.cn/view", ViewFactory(flutterPluginBinding.binaryMessenger))
}
在onAttachedToEngine的方法中加入我们刚刚写好的ViewFactory,就大功告成了。
3. 在flutter中实现
在lib中创建dart文件并实现继承StatefulWidget的weidget对原生view进行实现,再创建Controller类对原生view进行控制。
class AndroidWidget extends StatefulWidget {
const AndroidWidget({Key? key, required this.controller}) : super(key: key);
final AndroidWidgetController controller;
@override
State<AndroidWidget> createState() => _AndroidWidgetState();
}
class _AndroidWidgetState extends State<AndroidWidget> {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) => widget.controller,
builder: (context, child) {
Provider.of<AndroidWidgetController>(context, listen: false);
return const SizedBox(
width: double.infinity,
height: double.infinity,
child: AndroidView(viewType: 'rainblog.cn/view',),
);
},
);
}
}
class AndroidWidgetController with ChangeNotifier, BaseController {
late MethodChannel channel;
AndroidWidgetController() {
channel = const MethodChannel('rainblog.cn/view_plugin');
}
void init() async {
channel.invokeMethod('init');
}
}
这里使用了Provider组件进行状态更新,不需要可以去掉Provider包相关内容;
AndroidWidgetController 类实现了对AndroidView的控制,注册MethodChannel,通过MethodChannel发送消息控制;AndroidWidget通过返回AndroidView进行实现,viewType就是我们刚刚原生代码中注册填入的名字。建议在AndroidView外套入SizedBox进行大小控制。
通过以上流程实现了android view的接入,通过example -> lib -> main.dart将widget引入并run就可以在Android手机上查看效果了。
原生webrtc在flutter的实现效果图,以后有时间再补上Android webrtc的实现。