在Android项目中接入Flutter,在Flutter使用安卓布局

开头

在flutter开发中,始终会有下面两个没法避免的问题:css

  • 原生项目往flutter迁移,就须要在原生项目中接入flutter
  • flutter项目中要使用到一些比较成熟的应用,就没法避免去用到原生的各类成熟库,好比音视频之类的

这篇文章,将会对上面两种状况,分别进行介绍java

在Android中接入flutter界面

在android项目中须要将flutter以module的形式接入android

建立flutter module

进入当前android项目,在根目录运行以下命令:web

flutter create -t module my_flutter 

上面表示建立一个名为 my_flutter 的flutter moduleruby

以后运行bash

cd my_flutter cd .android/ ./gradlew flutter:assembleDebug 

同时,确保你的在你的android项目目录下 app/build.gradle ,有添加以下代码:app

android {
 compileSdkVersion 28 defaultConfig { ... } buildTypes { ... } //flutter相关声明 compileOptions { sourceCompatibility 1.8 targetCompatibility 1.8 } } 

接着,在 android项目 根目录下的 settings.gradle 中添加以下代码async

include ':app' setBinding(new Binding([gradle: this])) evaluate(new File( rootDir.path + '/my_flutter/.android/include_flutter.groovy' )) 

最后,须要在android项目下的 app/build.gradle 中引入 my_flutteride

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, ); } } 

效果以下:

相关文章
相关标签/搜索