开头
在 flutter 开发中,始终会有下面两个无法避免的问题:
- 原生项目往 flutter 迁移,就需要在原生项目中接入 flutter
- flutter 项目中要使用到一些比较成熟的应用,就无法避免去用到原生的各种成熟库,比如音视频之类的
这篇文章,将会对上面两种情况,分别进行介绍
在 Android 中接入 flutter 界面
在 android 项目中需要将 flutter 以 module 的形式接入
创建 flutter module
进入当前 android 项目,在根目录运行如下命令:
flutter create -t module my_flutter
上面表示创建一个名为 my_flutter 的 flutter module
之后运行
cd my_flutter
cd .android/
./gradlew flutter:assembleDebug
同时,确保你的在你的 android 项目目录下 app/build.gradle , 有添加如下代码:
android {
compileSdkVersion 28
defaultConfig {...}
buildTypes {...}
//flutter 相关声明
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
}
接着,在 android 项目 根目录下的 settings.gradle 中添加如下代码
include ':app'
setBinding(new Binding([gradle: this]))
evaluate(new File(rootDir.path + '/my_flutter/.android/include_flutter.groovy'))
最后,需要在 android 项目下的 app/build.gradle 中引入 my_flutter
dependencies {
...
// 导入 flutter
implementation project(':flutter')
}
到这里,基本上就可以开始接入 flutter 的内容了
不过这时候还有一个问题需要注意,如果你的 android 项目已经迁移到了 androidx,可能你会遇到下面的这种问题
这种问题明显是因为 flutter 创建 moudle 时,并未做到 androidx 的转换,因为创建 moudle 的命令还不支持 androidx
下面就开始解决这个问题
解决 androidx 带来的问题
首先,如果你原先的 android 项目已经迁移到了 androidx,那么在根目录下的 grale.properties 一定有如下内容
# 表示使用 androidx
android.useAndroidX=true
# 表示将第三方库迁移到 androidx
android.enableJetifier=true
下面进入到 my_flutter 目录下,在 你的 android 项目 /my_flutter/.android/Flutter/build.gradle 中对库的依赖部分进行修改
如果默认的内容如下:
dependencies {
testImplementation 'junit:junit:4.12'
implementation 'com.android.support:support-v13:27.1.1'
implementation 'com.android.support:support-annotations:27.1.1'
}
将所有依赖修改为 androidx 的版本:
dependencies {
testImplementation 'junit:junit:4.12'
implementation 'androidx.legacy:legacy-support-v13:1.0.0'
implementation 'androidx.annotation:annotation:1.0.0'
}
在 android studio 上点击完 Sync Now 同步之后
再进入下面的目录 你的 android 项目 /my_flutter/.android/Flutter/src/main/java/io/flutter/facade/ 目录下,对 Flutter.java 和 FlutterFragment.java 分别进行修改
修改 FlutterFragment.java
原本的依赖如下
将报错部分替换为 androidx 的版本
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
修改 Flutter.java
原本的依赖如下
将报错部分替换为 androidx 的版本
import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
那么现在,androidx 带来的问题就解决了,下面就开始准备正式接入 Flutter
在 flutter 中编辑入口
进入 my_flutter 目录中的 lib 目录,可以看到会有系统自带的 main.dart 文件,这是一个默认的计数器页面,我们修改一部分:
void main() => runApp(getRouter(window.defaultRouteName));
Widget getRouter(String name) {switch (name) {
case 'route1':
return MyApp();
default:
return Center(child: Text('Unknown route: $name', textDirection: TextDirection.ltr),
);
}
}
将入口更换为通过“route1″ 命名进入进入
接下来就是在 android 中进行操作了
在 android 中接入 flutter
进入到 android 项目,在 MainActivity 中,我们做如下操作:
bt_flutter.setOnClickListener {
val flutterView = Flutter.createView(
this@MainActivity,
lifecycle,
"route1"
)
val layout = ConstraintLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
layout.leftMargin = 0
layout.bottomMargin = 26
addContentView(flutterView, layout)
}
从上面的代码可以看到,我们通过一个按钮的点击事件去展示了 flutter 的计数器页面。实际效果如下:
那么 android 接入 flutter 就结束了,下面是在 flutter 中接入 android
在 Flutter 中接入 android 界面
我们可以新建一个 flutter 项目,用于测试这个例子
因为用到了 kotin,所以使用以下命令
flutter create -a kotlin counter_native
flutter create -a kotlin counter_native
获取 android 数据
关于如何去获取数据,主要还是使用 MethodChannel
看一下 android 中 MainActivity 的代码
class MainActivity: FlutterActivity() {
private val channelName = "samples.flutter.io/counter_native";
override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
MethodChannel(flutterView, channelNameTwo).setMethodCallHandler { methodCall, result ->
when(methodCall.method){
"getCounterData" -> {result.success(getCounterData())
}
else -> {result.notImplemented();
}
}
}
}
private fun getCounterData():Int{return 100;}
}
在 MethodChannel 的结果回调中,我们进行了筛选,如果方法名是 getCounterData 就直接返回 100
接下来在 flutter 中编写下面的代码:
static const platform =
const MethodChannel('samples.flutter.io/counter_native');
void getCounterData() async {
int data;
try {final int result = await platform.invokeMethod('getCounterData');
data = result;
} on PlatformException catch (e) {data = -999;}
setState(() {counterData = data;});
}
效果如下:
获取 android 的数据就说到这里,下面就是去获取 android 的页面了
获取 android 的布局
相较于数据而言,拿到 android 的布局就要复杂的多
创建 android 视图
在 android 项目里面,创建一个想要展示在 flutter 中的布局,这里,我们结合 xml 文件来创建布局,不过使用 xml 的方式,会出现 R 文件找不到的情况,这时候编译器会报错,暂时不用去管:
class CounterView(context: Context, messenger: BinaryMessenger, id: Int)
: PlatformView, MethodChannel.MethodCallHandler {
private var methodChannel: MethodChannel =
MethodChannel(messenger, "samples.flutter.io/counter_view_$id")
private var counterData: CounterData = CounterData()
private var view: View = LayoutInflater.from(context).inflate(R.layout.test_layout, null);
private var myText: TextView
init {methodChannel.setMethodCallHandler(this)
myText = view.findViewById(R.id.tv_counter)
}
override fun getView(): View {return view}
override fun dispose() {}
override fun onMethodCall(methodCall: MethodCall, result: MethodChannel.Result) {when (methodCall.method) {
"increaseNumber" -> {
counterData.counterData++
myText.text = "当前 Android 的 Text 数值是:${counterData.counterData}"
result.success(counterData.counterData)
}
"decreaseNumber" -> {
counterData.counterData--
myText.text = "当前 Android 的 Text 数值是:${counterData.counterData}"
result.success(counterData.counterData)
}
"decreaseSize" -> {if(myText.textSize > 0){
val size = myText.textSize
myText.setTextSize(TypedValue.COMPLEX_UNIT_PX,size-1)
result.success(myText.textSize)
} else{result.error("出错", "size 无法再小了!", null)
}
}
"increaseSize" -> {if(myText.textSize < 100){
val size = myText.textSize
myText.setTextSize(TypedValue.COMPLEX_UNIT_PX,size+1)
result.success(myText.textSize)
} else{result.error("出错", "size 无法再大了!", null)
}
}
"setText" -> {myText.text = (methodCall.arguments as String)
result.success(myText.text)
}
else -> {result.notImplemented();
}
}
}
}
上面的 CounterData 类是用于存储数据创建的一个类:
class CounterData(var counterData: Int = 0) {}
接下来,我们创建一个 CounterViewFactory 类用于获取到布局:
class CounterViewFactory(private val messenger: BinaryMessenger)
: PlatformViewFactory(StandardMessageCodec.INSTANCE) {override fun create(context: Context, id: Int, o: Any?): PlatformView {return CounterView(context, messenger, id)
}
}
最后创建一个 CounterViewPlugin.kt 文件,它用于注册视图,相当于初始化入口
class CounterViewPlugin{fun registerWith(registrar: Registrar) {registrar.platformViewRegistry().registerViewFactory("samples.flutter.io/counter_view", CounterViewFactory(registrar.messenger()))
}
}
创建完成后,在 MainActivity 中进行视图注册:
override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)
CounterViewPlugin().registerWith(flutterView.pluginRegistry.registrarFor("CounterViewPlugin"))
...
}
接下来,就是在 flutter 中需要做的一些事情了
在 flutter 中获取 android 视图
在 flutter 里面,想要拿到 android 的视图,需要通过 AndroidView 去获取
Widget build(BuildContext context) {if (Platform.isAndroid) {
return AndroidView(
viewType: 'samples.flutter.io/counter_view',
onPlatformViewCreated: _onPlatformViewCreated,
);
}
return Text('$defaultTargetPlatform 还不支持这个布局');
}
在 onPlatformViewCreated 方法中,我们需要创建 MethodChannel,用于调用 android 中编写的方法,我们可以封装一个 Controller 去处理这些逻辑:
final CounterController counterController;
void _onPlatformViewCreated(int id) {if (widget.counterController == null) {return;}
widget.counterController.setId(id);
}
下面是 CounterController
typedef void CounterViewCreatedCallBack(CounterController controller);
class CounterController {
MethodChannel _channel;
void setId(int id){_channel = new MethodChannel('samples.flutter.io/counter_view_$id');
print("id");
}
Future increaseNumber() async {
final int result = await _channel.invokeMethod('increaseNumber',);
print("result:${result}");
}
Future decreaseNumber() async {
final int result = await _channel.invokeMethod('decreaseNumber',);
}
Future increaseSize() async {
final result = await _channel.invokeMethod('increaseSize',);
}
Future decreaseSize() async {
final result = await _channel.invokeMethod('decreaseSize',);
}
Future setText(String text) async {
final result = await _channel.invokeMethod('setText',text,);
}
}
效果如下:
最后给大家分享一份移动架构大纲,包含了移动架构师需要掌握的所有的技术体系,大家可以对比一下自己不足或者欠缺的地方有方向的去学习提升
需要高清架构图以及图中视频资料的请查看我主页