前言:作為一名大前端開發者,我的技術棧其實一直都很混亂,從Flutter到小程序再到Vue,我都做過不少項目。原因可能是我一直以來都想做大前端的念想,加上以往公司業務方向,讓我對所有前端技術來著不拒。
但是總有一門技術需要鉆精,于是我果斷選擇了Flutter,這半年來也一直在做Flutter的項目;但想要成為Flutter高級開發者,不會原生豈能說的過去,而我的前端剛好也是大學選修Android而入門的;所以我決定開始學習并記錄Flutter與Android混合開發系列文章。與君共勉~
一、Flutter與Android混合開發的類別
先做下總結,整個系列包括:Flutter package(Flutter純Dart庫)、Flutter Plugin(Flutter原生插件庫)、Android混入Flutter模塊。
以下再做細分:
package是純dart代碼的庫,供Flutter引用;
Plugin是原生插件庫,是一種特殊的package,Plugin涉及這幾個方面:Flutter與原生間相互的數據通信、
Flutter顯示Android 視圖、
Flutter跳轉到Android Activity、Android Fragment。Android混入Flutter模塊:通過引入Flutter Module,分別在Android端的Activity和Fragment中顯示Flutter視圖。
二、Flutter Plugin開發詳解
Plugin即原生插件,開發者需要編寫android、iOS雙端的代碼,然后由Flutter層直接調用。由于筆者只學Android,所以iOS端的功能我能省都會省了。另外,在編寫Flutter插件時,官方已經啟用Flutter Engine來管理插件,因此寫法與網上的舊資料也會有所不同。
這篇文章我將實現:Flutter顯示Android view!
下一篇文章再記錄:1. Flutter與Android的通信(包括MethodChannel、BasicMessageChannel、EventChannel)2. Flutter跳轉到原生Activity、Fragment;
1、 Flutter Plugin的創建
若是使用Android studio開發工具的開發者,可以在菜單欄中直接創建一個plugin項目,根據提示一步步選擇即可;當然我建議你用命令行創建項目(其中-a指定android的開發語言,-i指定ios的開發語言):
flutter create --org com.example --template=plugin --platforms=android,ios -a java -i swift hello
創建好后的目錄結構如圖所示:2. Flutter顯示Android View
-
打開android studio模塊,以便更快捷的編寫代碼;
-
可以看到有兩個android module,其中我們要編寫的是插件的模塊;module目錄結構
- 我們需要創建一個原生視圖布局,在包名下面創建一個新的視圖,這里我只是簡單構建了一個Text控件,這部分屬于原生的能力,筆者也不太熟悉。
package com.karl.wei.my_camera;
import android.content.Context;
import android.view.View;
import android.widget.TextView;
import java.util.Map;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.platform.PlatformView;
public class MyInfoView implements PlatformView {
private final TextView name;
public MyInfoView(Context context, BinaryMessenger messenger, int id, Map<String, Object> params){
TextView textView = new TextView(context);
textView.setText("正在加載...");
this.name = textView;
if(params!=null&¶ms.containsKey("content")){
String t = (String)params.get("content");
textView.setText(t);
}
}
@Override
public View getView() {
return name;
}
@Override
public void dispose() {
}
}
- 之后需要構建一個視圖工廠的類,提供給Engine進行視圖的注冊,其中create生命周期需要retrun對應的視圖文件。
import android.content.Context;
import com.karl.wei.my_camera.MyInfoView;
import java.util.Map;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.StandardMessageCodec;
import io.flutter.plugin.platform.PlatformView;
import io.flutter.plugin.platform.PlatformViewFactory;
public class MyViewFactory extends PlatformViewFactory {
private final BinaryMessenger messenger;
public MyViewFactory(BinaryMessenger messenger) {
super(StandardMessageCodec.INSTANCE);
this.messenger = messenger;
}
@Override
@SuppressWarnings("unchecked")
public PlatformView create(Context context, int id, Object o) {
Map<String, Object> params = (Map<String, Object>) o;
return new MyInfoView(context, messenger, id, params);
}
}
- 然后需要在插件的類中,注冊我們要返回的視圖,注冊時需要傳入唯一標識和視圖工廠,每一個標識對應一個視圖工廠。
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
// 需要注冊的視圖的唯一標識
final String key = "karl_info";
// 創建MethodChannel通道,my_camera與yaml的name是需要對應的
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "my_camera");
channel.setMethodCallHandler(this);
// 注冊原生view,通過注冊視圖工廠(ViewFactory),需要傳入唯一標識和ViewFactory類
flutterPluginBinding.getPlatformViewRegistry().registerViewFactory(key, new MyViewFactory(flutterPluginBinding.getBinaryMessenger()));
}
下面是3、4、5步的文件路徑截圖- 到這里,原生視圖算是簡單編寫完成;接下來還需要Flutter將我們再原生的視圖給暴露出去,讓純Flutter項目可以簡單的接入我們的視圖。回到plugin目錄下,我們新建一個dart文件,通過Flutter提供的AndroidView返回我們的原生布局,AndroidView接收一個viewType參數,這個參數就是試圖的唯一標識,跟第5點相對應。
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class InfoWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
String viewType = 'karl_info'; // 唯一標識符
var creationParams = {
'type': 'info_view',
}; // 視圖創建參數,可以被插件用來傳遞構造函數參數到嵌入式Android視圖
// 視圖創建完畢的回調
PlatformViewCreatedCallback callback = (id) {
print(id);
};
// 判斷設備類型,也可用:defaultTargetPlatform == TargetPlatform.android
if (Platform.isAndroid) {
return AndroidView(
viewType: viewType,
onPlatformViewCreated: callback,
);
} else if (Platform.isIOS) {
return UiKitView(
viewType: viewType,
onPlatformViewCreated: callback,
);
} else {
return Text('Platform is not yet supported by this plugin');
}
}
}
- 現在,Flutter端如何使用我們的視圖?如果由于某些問題,你的插件只能本地引用的時候,首先在yaml配置文件中引入插件,然后在項目的android中主文件注冊插件。
dependencies:
flutter:
sdk: flutter
my_camera:
path: ../
package io.flutter.plugins;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import io.flutter.embedding.engine.FlutterEngine;
/**
* Generated file. Do not edit.
* This file is generated by the Flutter tool based on the
* plugins that support the Android platform.
*/
@Keep
public final class GeneratedPluginRegistrant {
public static void registerWith(@NonNull FlutterEngine flutterEngine) {
flutterEngine.getPlugins().add(new com.karl.wei.my_camera.MyCameraPlugin());
}
}
如果你開發過flutter項目,到這里你應該會疑惑,以前項目引入那么多的開源插件,都不需要手動注冊插件呀,怎么到這里就需要了?其實,當我們執行flutter pub get時,編譯器就自動為我們加入相應插件的注冊了,不信你看看看這個文件,嘿嘿!
- 然后在你的項目中,需要的地方引用即可
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Container(
child: Center(
child: InfoWidget(),
),
),
),
);
}
三、總結
梳理下Flutter Plugin顯示Android View的邏輯:
① 創建完插件后,在原生層新建一個文件繼承自PlatformView,編寫視圖內容;
② PlatformViewFactory創建一個視圖工廠繼承自PlatformViewFactory,其中create生命周期返回對應的視圖文件;
③ 在插件主文件中注冊視圖,并定義好唯一標識;
④ 在Flutter層使用AndroidView通過傳入③中定義好的標識,通過視圖工廠取出視圖;
⑤ Flutter使用者調用即可。
另外,原生視圖同樣可以傳遞參數,跟view的層層傳遞是一起完成的。如AndroidView中是傳遞creationParams,PlatformViewFactory在create中接收并傳遞給真正的PlatformView,這里不做拓展。
四、寫在最后
其實Flutter顯示原生View筆者認為沒有太大意義,因為Flutter本身就是一個非常完美的UI框架,既然選擇使用Flutter,其實用到原生視圖的地方就比較少了。重點將在后續章節:Flutter與原生通信、Flutter調整Activity等功能的學習。
這篇文章僅僅是系列的開端,筆者也是邊學邊記錄,希望能幫到大家,一起學習一起努力。
希望大家多多指導我!