Flutter混合开发Android篇

  • 在Windows上搭建Flutter开发环境
  • Android Flutter混合开发
  • 创建module
  • 1.在Android项目的同级目录新建Flutter Module
  • 2.Flutter module项目集成FlutterBoost
  • 3.运行代码
  • 安卓原生项目中集成FlutterBoost
  • 1.将文件夹的名改为app
  • 2.修改 settings.gradle 文件
  • 3.修改最外层目录下的build.gradle 文件
  • 4.修改app目录下的build.gradle 文件
  • 5.修改app目录下的AndroidManifest.xml文件
  • 6. 增加Flutter文件
  • 7. Application 初始化
  • 8. 打开Flutter界面
  • 9.混淆
  • 学习资料


在Windows上搭建Flutter开发环境

1.下载Flutter SDK (Channel stable, v1.17.1, on Microsoft Windows [Version 10.0.18363.1256], locale zh-CN),不要下载最新的SDK,兼容混合开发框架 flutter_boost: ^1.17.1。

2.下载四个插件,分别为 Flutter、Dart、FishReduxTemplate、Flutter Intl。流程图如下:
File | Settings | Plugins

3.检查三个SDK的配置,分别为Androd SDK、Flutter SDK、
Dart SDK. 流程图如下:
File | Settings | Appearance & Behavior | System Settings | Android SDK
File | Settings | Languages & Frameworks | Flutter
File | Settings | Languages & Frameworks | Dart

Windows上搭建Flutter开发环境

Android Flutter混合开发

创建module

1.在Android项目的同级目录新建Flutter Module

File | New | New Flutter Project… | Flutter Module

2.Flutter module项目集成FlutterBoost

在flutter_module项目的pubspec.yaml文件中添加依赖插件配置

dependencies:
  flutter_boost: ^1.17.1

配置完成后执行flutter packages get命令下载依赖插件到本地

需要了解 pub get 和 pub upgrade 命令 , 打开pubspec.yaml这两个命令就能显示出来

pub get
在项目中配置了pubspec文件后,就可以在项目根目录中执行pub get命令

pub upgrade
第一次获取依赖时,Pub 会下载依赖及其兼容的最新版本。然后通过创建lockfile 锁定依赖,以始终使用这个版本。 Pub会在pubspec旁创建并存储一个名为pubspec.lock文件。它列出了使用的每个依赖包的指定版本(当前包或传递包的版本)。

3.运行代码

1.直接点击绿色的箭头Run就能独立运行到手机里面。
2.Build | Flutter | Build AAR ,就能编译出AAR文件,并生成Android集成日志

安卓原生项目中集成FlutterBoost

1.将文件夹的名改为app

将文件夹的名改为app, 否则会报错。

2.修改 settings.gradle 文件

include ':app', ':MiHomeSDK'
setBinding(new Binding([gradle: this]))
evaluate(new File(
        settingsDir.parentFile,
        'flutter_module/.android/include_flutter.groovy'
))
include ':flutter_module'
project(':flutter_module').projectDir = new File('../flutter_module')

3.修改最外层目录下的build.gradle 文件

1.增加 maven {url ‘http://download.flutter.io’}

buildscript {
   ...
    repositories {
        ...
        maven {url 'http://download.flutter.io'}
    }
    dependencies {
        ...
    }
}

allprojects {
    repositories {
        ...
        maven {url 'http://download.flutter.io'}
    }
}

4.修改app目录下的build.gradle 文件

repositories {
    maven {url '../../flutter_module/build/host/outputs/repo'}
}
debugImplementation 'com.xiaoyi.flutter_module:flutter_debug:1.0'
    profileImplementation 'com.xiaoyi.flutter_module:flutter_profile:1.0'
    releaseImplementation 'com.xiaoyi.flutter_module:flutter_release:1.0'

5.修改app目录下的AndroidManifest.xml文件

<activity
            android:name="com.idlefish.flutterboost.containers.BoostFlutterActivity"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize" >
       </activity>

6. 增加Flutter文件

里面包含三个Java 文件, 是自己封装,主要是初始化、通道、路由等功能
FlutterUtil.java

package com.ants360.yicamera.flutter;

import android.app.Application;
import android.os.Build;

import com.ants360.yicamera.base.UserManager;
import com.ants360.yicamera.bean.User;
import com.google.gson.Gson;
import com.idlefish.flutterboost.FlutterBoost;
import com.idlefish.flutterboost.Platform;
import com.idlefish.flutterboost.Utils;
import com.idlefish.flutterboost.interfaces.INativeRouter;

import java.util.HashMap;
import java.util.Map;

import io.flutter.embedding.android.FlutterView;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.StandardMessageCodec;

public class FlutterUtil {
    public static void init(Application app) {
        INativeRouter router = (context, url, urlParams, requestCode, exts) -> {
            String assembleUrl = Utils.assembleUrl(url, urlParams);
            PageRouter.openPageByUrl(context, assembleUrl, urlParams);
        };

        FlutterBoost.BoostLifecycleListener boostLifecycleListener = new FlutterBoost.BoostLifecycleListener() {

            @Override
            public void beforeCreateEngine() {

            }

            @Override
            public void onEngineCreated() {

                // 注册MethodChannel,监听flutter侧的getPlatformVersion调用
                MethodChannel methodChannel = new MethodChannel(FlutterBoost.instance().engineProvider().getDartExecutor(), "flutter_native_channel");
                methodChannel.setMethodCallHandler((call, result) -> {
                    if (call.method.equals("getPlatformVersion")) {
                        result.success(Build.VERSION.RELEASE);
                    } else if (call.method.equals("getUser")) {
                        User user  = UserManager.getInstance().getUser();
                        Map<String, String> map = new HashMap<>();
                        Gson gson = new Gson();
                        map.put("user", gson.toJson(user));
                        result.success(map);
                    }else {
                        result.notImplemented();
                    }

                });


                EventChannel methodChannel2 = new EventChannel(FlutterBoost.instance().engineProvider().getDartExecutor(), "native_flutter_channel");
                methodChannel2.setStreamHandler(new EventChannel.StreamHandler() {
                    @Override
                    public void onListen(Object arguments, EventChannel.EventSink events) {
                        events.success("msg");
                    }

                    @Override
                    public void onCancel(Object arguments) {
                    }
                });

                // 注册PlatformView viewTypeId要和flutter中的viewType对应
                FlutterBoost
                        .instance()
                        .engineProvider()
                        .getPlatformViewsController()
                        .getRegistry()
                        .registerViewFactory("plugins.test/view", new TextPlatformViewFactory(StandardMessageCodec.INSTANCE));

            }

            @Override
            public void onPluginsRegistered() {

            }

            @Override
            public void onEngineDestroy() {

            }

        };

        Platform platform = new FlutterBoost
                .ConfigBuilder(app, router)
                .isDebug(true)
                .whenEngineStart(FlutterBoost.ConfigBuilder.ANY_ACTIVITY_CREATED)
                .renderMode(FlutterView.RenderMode.texture)
                .lifecycleListener(boostLifecycleListener)
                .build();
        FlutterBoost.instance().init(platform);
    }
}

PageRouter.java

package com.ants360.yicamera.flutter;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.ants360.yicamera.activity.camera.share.DeviceShareResultActivity;
import com.ants360.yicamera.constants.KeyConst;
import com.idlefish.flutterboost.containers.BoostFlutterActivity;

import java.util.HashMap;
import java.util.Map;

public class PageRouter {

    public final static Map<String, String> pageName = new HashMap<String, String>() {{

        put("notification", "notification");
        put("device_login_message", "device_login_message");
        put("device_Share_message", "device_Share_message");


        put("first", "first");
        put("second", "second");
        put("tab", "tab");
        put("sample://flutterPage", "flutterPage");
    }};

    public static final String NATIVE_PAGE_URL_DEVICE_SHARE_RESULT_ACTIVITY = "yicamera://DeviceShareResultActivity";









    public static final String NATIVE_PAGE_URL = "sample://nativePage";
    public static final String FLUTTER_PAGE_URL = "sample://flutterPage";
    public static final String FLUTTER_FRAGMENT_PAGE_URL = "sample://flutterFragmentPage";

    public static boolean openPageByUrl(Context context, String url, Map params) {
        return openPageByUrl(context, url, params, 0);
    }

    public static boolean openPageByUrl(Context context, String url, Map params, int requestCode) {

        String path = url.split("\\?")[0];

        Log.i("openPageByUrl",path);

        try {
            if (pageName.containsKey(path)) {
                Intent intent = BoostFlutterActivity.withNewEngine().url(pageName.get(path)).params(params)
                        .backgroundMode(BoostFlutterActivity.BackgroundMode.opaque).build(context);
                if(context instanceof Activity){
                    Activity activity = (Activity) context;
                    activity.startActivityForResult(intent,requestCode);
                }else{
                    context.startActivity(intent);
                }
                return true;
            } else if (url.startsWith(NATIVE_PAGE_URL_DEVICE_SHARE_RESULT_ACTIVITY)) {
                Intent intent = new Intent(context, DeviceShareResultActivity.class);
                if( null != params) {
                    intent.putExtra(KeyConst.DEVICE_SHARE_WAY, params.get(KeyConst.DEVICE_SHARE_WAY).toString());
                    intent.putExtra(KeyConst.DEVICE_SHARE_MESSAGE_ID, params.get(KeyConst.DEVICE_SHARE_MESSAGE_ID).toString());
                    intent.putExtra(KeyConst.DEVICE_SHARE_TOKEN, params.get(KeyConst.DEVICE_SHARE_TOKEN).toString());
                    intent.putExtra(KeyConst.DEVICE_SHARE_OWNER_NAME, params.get(KeyConst.DEVICE_SHARE_OWNER_NAME).toString());
                }
                if(context instanceof Activity){
                    Activity activity = (Activity) context;
                    activity.startActivityForResult(intent, requestCode);
                }else{
                    context.startActivity(intent);
                }
                return true;
            } else if (url.startsWith(FLUTTER_FRAGMENT_PAGE_URL)) {
//                context.startActivity(new Intent(context, FlutterFragmentPageActivity.class));
                // TODO
                return true;
            } else if (url.startsWith(NATIVE_PAGE_URL)) {
//                context.startActivity(new Intent(context, NativePageActivity.class));
                // TODO
                return true;
            }

            return false;

        } catch (Throwable t) {
            return false;
        }
    }
}

TextPlatformViewFactory.java

package com.ants360.yicamera.flutter;

import android.content.Context;
import android.view.View;
import android.widget.TextView;

import io.flutter.plugin.common.MessageCodec;
import io.flutter.plugin.platform.PlatformView;
import io.flutter.plugin.platform.PlatformViewFactory;

public class TextPlatformViewFactory extends PlatformViewFactory {

    public TextPlatformViewFactory(MessageCodec<Object> createArgsCodec) {
        super(createArgsCodec);
    }

    @Override
    public PlatformView create(Context context, int i, Object o) {
        return new TextPlatformView(context);
    }

    private static class TextPlatformView implements PlatformView {

        private TextView platformTv;

        public TextPlatformView(Context context) {
            platformTv = new TextView(context);
            platformTv.setText("PlatformView Demo");
        }

        @Override
        public View getView() {
            return platformTv;
        }

        @Override
        public void dispose() {

        }
    }
}

7. Application 初始化

@Override
    public void onCreate() {
        super.onCreate();
        FlutterUtil.init(this);
   }

8. 打开Flutter界面

PageRouter.openPageByUrl(getActivity(), PageRouter.pageName.get("notification") , null);

9.混淆

@Keep
public class User implements IUser {
}

学习资料

Flutter官网 Dart官网 依赖包 OpenFlutter社区 flutter_boost 混合开发框架 fish_redux架构开发