萌萌の初音
萌萌の初音
发布于 2022-04-26 / 856 阅读
0

Flutter插件实现AndroidView

使用flutter难免会借助原生view实现相关功能,直接在项目的FlutterActivity中实现不利于管理和组件化的实现,以下通过flutter plugin进行实现进行记录。

1. 新建flutter plugin项目

以android studio为例,new -> new flutter project,project type选择plugin,填写项目相关信息,然后选择finish。
new flutter project

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手机上查看效果了。
Screenshot_2022_04_26.jpg
原生webrtc在flutter的实现效果图,以后有时间再补上Android webrtc的实现。