Android与Flutter混合开发-UI交互

前言

  • 本人是作android的,这边只介绍下Android和Flutter的混合开发。
  • 关于原生和Flutter的混合开发,网上有不少相关的文章了,基本上都是使用FlutterView和FlutterFragment的方式去作的,可是在新版Flutter SDK 1.12版本上,Flutter团队把io.flutter.facade.Flutter这个包给删了,上面俩种方式直接凉了,根本没法在Android项目里拿到Flutter对象。因此只能去看官方文档,跑下新的集成方法,这边作个备忘,也同时说下会踩的坑。
  • Flutter官方文档:flutter.cn/docs/develo…
  • 本文章适用于Flutter SDK 1.12版本

注:说明下,这个连接是中文文档,可是还有不少章节仍是没翻译,例如将Flutter module集成到Android项目的章节。ios的已被翻译,大佬们都是喜欢玩ios,因此首先翻译ios?java

准备工做

新建一个Android项目

  • 这个随意,新建一个Android项目或者使用已有的Android项目均可以。

配置Android项目

  • Flutter 目前只支持 armeabi-v7a 和 arm64-v8a 架构。
android {
  //...
  defaultConfig {
    ndk {
      // 限制下架构,Flutter仅支持:armeabi-v7a 和 arm64-v8a 架构(目前1.12版本)
      abiFilters 'armeabi-v7a', 'arm64-v8a'
    }
  }
}
复制代码
  • Flutter使用了 Java 8的特性,设置支持Java 8
android {
  //...
  compileOptions {
    sourceCompatibility 1.8
    targetCompatibility 1.8
  }
}
复制代码

新建一个Flutter Module

  • 新建Flutter Module

新建Module

  • 设置Flutter Module

导入Flutter Module

自动导入

注:Android Studio支持自动导入,前提条件是:Android项目和Flutter Module在同一文件夹下。android

  • 选择:“New Module”
  • 选择 “Import Flutter Module”
  • 选择下你的Flutter Module项目文件夹就好了

手动导入

注:只须要设置下就好了,上面自动导入,就是默认生成代码的。ios

  • MyApp/app/build.gradle,添加下依赖
    dependencies {
      implementation project(':flutter')
    }
    复制代码
  • MyApp/settings.gradle,settings.gradle加上下述代码,“flutter_module”是项目名称,写上你本身的项目名称(官网上的代码)
    include ':app'                                   
    setBinding(new Binding([gradle: this]))                            
    evaluate(new File(                                                      
      settingsDir.parentFile,                                              
      'flutter_module/.android/include_flutter.groovy'                         
    ))                                                                    
    复制代码
    通常默认生成的是下述代码形式
    include ':app'
    setBinding(new Binding([gradle: this]))
    evaluate(new File(
      settingsDir,
      '../flutter_module/.android/include_flutter.groovy'
    ))
    复制代码
  • 说明下路径符号相关区别
    • / :根目录
    • ./ :当前目录
    • ../ : 父级目录(上一级目录)
    • settingsDir:获取的是当前项目的全路径,包括项目文件夹
    • settingsDir.parentFile:获取项目文件夹上一级路径

Flutter和Android UI交互

添加单个Flutter页面

使用FlutterActivity的形式,呈现Flutter界面十分方便缓存

  • 前置条件:FlutterActivity 必须在 AndroidManifest.xml 中注册bash

    <activity
      android:name="io.flutter.embedding.android.FlutterActivity"
      android:theme="@style/AppTheme"
      android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
      android:hardwareAccelerated="true"
      android:windowSoftInputMode="adjustResize"
      />
    复制代码
  • 启动Flutter Module默认路由(最简单形式)架构

    Intent intent = FlutterActivity.createDefaultIntent(this);
    startActivity(intent);
    复制代码

    这样能够直接跳转到Flutter界面,实际是启动FlutterActivity,在FlutterActivity里面加载Flutter的Ui界面。app

  • 选择性跳转某个路由界面ide

    Intent intent = FlutterActivity.withNewEngine()
                    .initialRoute("/you/route/") //设置你的路由地址
                    .build(this);
    startActivity(intent);
    复制代码

    实际上createDefaultIntent()方法里面就是封装了withNewEngine().build(launchContext)方法,有兴趣的,能够点进代码里面看看。布局

  • 使用缓存的 FlutterEnginegradle

    //使用缓存的FlutterEngine(最大程度地减小启动标准的延迟)
    FlutterEngine flutterEngine = new FlutterEngine(this);  //初始路由
    //flutterEngine.getNavigationChannel().setInitialRoute("your/route/here"); //自定义路由
    //开始执行Dart代码以预热FlutterEngine。
    flutterEngine.getDartExecutor().executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault());
    //缓存FlutterActivity要使用的FlutterEngine。
    FlutterEngineCache.getInstance().put("my_engine_id", flutterEngine);
    Intent intent = FlutterActivity
            .withCachedEngine("my_engine_id")
            .build(this);
    
    //某个按钮的点击事件
    findViewById(R.id.flutter_button).setOnClickListener(v -> {
            startActivity(intent);
        });
    
    复制代码

    说明下:实际上FlutterActivity.withNewEngine()...方式跳转,都是新生成一个FlutterEngine对象,每一个FlutterEngine都有一个很是重要的预热时间。这意味着启动一个标准的FlutterActivity 会在Flutter体验变得可见以前有一个短暂的延迟。为了最小化这个延迟,咱们能够在到达FlutterActivity以前预热FlutterEngine,而后可使用缓存的FlutterEngine。这种状况在debug安装的状况尤为显著,会有一段时间黑屏,提早缓存好FlutterEngine,能够避免这种状况,提高交互体验,推荐。

添加FlutterFragment

  • 说明:FlutterFragment可用的地方仍是蛮多的,可用于显示一个滑动的抽屉、标签式内容、 ViewPager 中的一个页面等等,固然,若是FlutterActivity能解决,建议使用FlutterActivity,由于FlutterActivity更容易使用。 官网原话:If an Activity is equally applicable for your application needs, consider using a FlutterActivity instead of a FlutterFragment, which is quicker and easier to use.

  • 使用FlutterFragment

    public class MyActivity extends FragmentActivity {
        private FlutterFragment flutterFragment;
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.my_activity_layout);
    
              //直接启动FlutterFragment
            FragmentManager fragmentManager = getSupportFragmentManager();
            //默认路由,至关于:initialRoute("/")
            //FlutterFragment flutterFragment = FlutterFragment.createDefault();
            FlutterFragment flutterFragment = FlutterFragment.withNewEngine()
                    .initialRoute("/")   //设置路由
                    .build();
            fragmentManager
                    .beginTransaction()
                    .add(R.id.flutter_ui, flutterFragment, "flutter_fragment")
                    .commit();
        }
    }
    复制代码
    • R.id.flutter_ui:是布局文件里面一个Fragment的id

    • 比较坑比的地方

      • FlutterFragment没法强转成Fragment类型,涉及到强转的部分会爆红,可是不影响运行。

      • 缘由:看了下FlutterFragment,继承Fragment的,子类强转父类是没问题的,可是很坑的是,FlutterFragment里面用的是Support包,我用的是Androidx,致使俩个对象无法强转。 就是:support.v4.app.Fragment的子类无法强转成androidx.fragment.app.Fragment ? 这个报错真是血坑。

      • 解决办法:在下只找到了可能的缘由,实在找不到解决之法。可能的解决办法

        • 报错就报错吧,留在那,不影响程序运行,佛系点
        • 等待Flutter团队,把这个FlutterFragment里面fuck的support包给删掉,换成androidx
        • 有其余办法解决,但愿评论告知下
    • 使用上面的代码,足以把你的Flutter页面展现出来了

  • 使FlutterFragment周期和Activity同步

    public class MyActivity extends FragmentActivity {
        @Override
        public void onPostResume() {
            super.onPostResume();
            flutterFragment.onPostResume();
        }
    
        @Override
        protected void onNewIntent(@NonNull Intent intent) {
            flutterFragment.onNewIntent(intent);
        }
    
        @Override
        public void onBackPressed() {
            flutterFragment.onBackPressed();
        }
    
        @Override
        public void onRequestPermissionsResult(
            int requestCode,
            @NonNull String[] permissions,
            @NonNull int[] grantResults
        ) {
            flutterFragment.onRequestPermissionsResult(
                requestCode,
                permissions,
                grantResults
            );
        }
    
        @Override
        public void onUserLeaveHint() {
            flutterFragment.onUserLeaveHint();
        }
    
        @Override
        public void onTrimMemory(int level) {
            super.onTrimMemory(level);
            flutterFragment.onTrimMemory(level);
        }
    }
    复制代码
  • 使用缓存的 FlutterEngine

    //使用缓存的FlutterEngine(最大程度地减小启动标准的延迟)
    FragmentManager fragmentManager = getSupportFragmentManager();
    FlutterEngine flutterEngine = new FlutterEngine(this);  //初始路由
    // flutterEngine.getNavigationChannel().setInitialRoute("/"); //自定义路由
    //开始执行Dart代码以预热FlutterEngine。
    flutterEngine.getDartExecutor().executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault());
    FlutterEngineCache.getInstance().put("my_engine_id", flutterEngine);//缓存要使用的FlutterEngine。
    
    //点击事件
    findViewById(R.id.flutter_button).setOnClickListener(v -> {
            FlutterFragment flutterFragment = FlutterFragment.withCachedEngine("my_engine_id").build();
            fragmentManager
                    .beginTransaction()
                    .add(R.id.flutter_ui, flutterFragment, "flutter_fragment")
                    .commit();
        });
    复制代码

    基本上和上面使用FlutterActivity缓存机制同样

    • Flutter官网上写完缓存代码,单独来了这么段代码:
      flutterFragment.withCachedEngine("my_engine_id").build();
      复制代码
      真是哔哔了狗,我还觉得withCachedEngine("my_engine_id").build()直接把缓存的FlutterEngine写入到flutterFragment对象里面,实际上,build() 返回的是FlutterFragment对象,上面的代码只是取设置好的FlutterFragment。
      • 猜想下官网上代码意图,难道是这样:
      fragmentManager
                .beginTransaction()
                .add(R.id.flutter_ui, flutterFragment.withCachedEngine("my_engine_id").build(), "flutter_fragment")
                .commit();
      复制代码
相关文章
相关标签/搜索