美文网首页Android开发经验谈Android技术知识Android开发
在Android项目中接入Flutter,在Flutter使用安

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

作者: Android高级开发 | 来源:发表于2019-08-06 15:58 被阅读26次

    开头

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

    手把手教你在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

    原本的依赖如下

    image

    将报错部分替换为androidx的版本

    import androidx.annotation.NonNull;
    import androidx.fragment.app.Fragment;
    

    修改Flutter.java

    原本的依赖如下

    手把手教你在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的计数器页面。实际效果如下:

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

    效果如下:

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

    效果如下:

    image.png

    最后

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

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

    image

    相关文章

      网友评论

        本文标题:在Android项目中接入Flutter,在Flutter使用安

        本文链接:https://www.haomeiwen.com/subject/zotwdctx.html