android真是项目采用的布局,在Android项目中接入Flutter,在Flutter使用安卓布局

开头

在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,可能你会遇到下面的这种问题

d94e108b75b8

手把手教你在Android项目中接入Flutter,在Flutter使用安卓布局

这种问题明显是因为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

原本的依赖如下

d94e108b75b8

手把手教你在Android项目中接入Flutter,在Flutter使用安卓布局

将报错部分替换为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的计数器页面。实际效果如下:

d94e108b75b8

image.png

那么android接入flutter就结束了,下面是在flutter中接入android

在Flutter中接入android界面

我们可以新建一个flutter项目,用于测试这个例子

因为用到了kotin,所以使用以下命令

flutter create -a kotlin counter_native

项目创建好之后,就可以开始了,在开始之前,我们首先可以了解以下如何在flutter中拿到android中的数据

获取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;

});

}

效果如下:

d94e108b75b8

image.png

获取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,

);

}

}

效果如下:

d94e108b75b8

image.png

最后给大家分享一份移动架构大纲,包含了移动架构师需要掌握的所有的技术体系,大家可以对比一下自己不足或者欠缺的地方有方向的去学习提升;

需要高清架构图以及图中视频资料的可以加入我的技术交流群:825106898私聊群主小姐姐免费获取

d94e108b75b8

QQ图片20190414204449.jpg

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值