Flutter混合项目搭建之Flutter_Boot的使用

一、概念

1、Flutterr_Boot

这是一个由咸鱼技术出品,帮助你在已有原生应用的情况下,搭建flutter混合开发环境的工具。 它提供了标准的混合工程结构,同时支持混合栈(一套原生和flutter之前页面通信和过渡的方案)的快速接入。

https://github.com/alibaba-flutter/flutter-boot

2、Flutter_Boost

官网介绍:新一代Flutter-Native混合解决方案。 FlutterBoost是一个Flutter插件,它可以轻松地为现有原生应用程序提供Flutter混合集成方案。FlutterBoost的理念是将Flutter像Webview那样来使用。在现有应用程序中同时管理Native页面和Flutter页面并非易事。 FlutterBoost帮你处理页面的映射和跳转,你只需关心页面的名字和参数即可(通常可以是URL)。

https://github.com/alibaba/flutter_boost

二、准备工作

  1. npm安装以及环境配置
  2. flutter 的v1.9.1-hotfixes分支安装以及环境配置,对应flutter_boost版本是0.1.60---0.1.64
  3. JDK,Android SDK 等环境配置正确

三、开始混合项目搭建

项目地址:https://github.com/jingzhanwu/FlutterHybrid-FlutterBoot

1、安装flutter_boot,命令行执行:npm install flutter_boot

npm install flutter_boot
image.gif

2、初始化flutter_boot项目,进入要存放工程的目录执行:

flutter-boot init
image.gif

接着会提示你输入工程名称

image
image.gif

输完工程名会一次提示你输入flutter 仓库地址、是否有android项目,是否有IOS项目、关联本地项目等,根据自己项目需求选择即可,如果你已经有了android原生项目,则最好在关联android项目哪一步输入本地native项目的路劲进行关联,如果选择不关联也可以,后面后说道如果关联,因为我是第二次创建,所以就默认执行了之前选择的。

上面都执行完毕后在本地就会生成一个项目,名称就是刚输入的flutter_lib

3、编译flutter_lib项目,下载配置gradle,如果是android studio 直接打开项目也可以

依次执行下面命令,第一个命令是进入到项目根目录

>>>cd flutter_lib

>>>flutter build apk
image.gif
image
image.gif

4、如果初始化的时候没有关联native项目,则在原生项目根目录下运行下面命令进行关联、如果初始化时已经关联则跳过此步骤。
进入native项目根目录执行:flutter-boot link "flutter项目的本地目录",link后面为flutter项目本地目录

image
image.gif

重新关联:flutter-boot link -f "flutter项目的本地目录"

这一步是将原生项目与flutter项目进行软关联,这一步执行完毕已经就可以进行正常的混合开发了

5、使用flutter_boost,添加混合栈

执行:flutter-boot use

flutter-boot use
image.gif

这一步执行完毕,会在原生项目的app下build.gradle、settings.gradle和gradle.properties文件中生成一些配置信息,具体如下

gradle.properties文件

image
image.gif

settings.gradle文件

image
image.gif

app下build.gradle文件

image
image.gif

除了以上增加的内容外,还会多出来两个gradle文件,分别是flutter的build.gradle和flutter-boost的build.gradle文件;他们分别对应flutter_lib(flutter侧)和flutter_boost库的gradle配置,一般情况下不要去修改这两个文件中的内容,否则会引起一些编译错误。

6、初始化flutter-boost

执行完flutter-boot use 命令后分别会在native侧和flutter侧的项目根目录下生成一些初始化使用的事例代码,可以参考;native侧的在 fb目录下,flutter的在main.dart和my_flutter_boost_app.dart文件中。

Native侧我把生成的事例代码进行了整理,首先是Application中的初始化部分:FlutterInitializer.init(this);

public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        FlutterInitializer.init(this);
    }
}
image.gif

FlutterInitializer.java的部分

public class FlutterInitializer {
    private final static String TAG = FlutterInitializer.class.getSimpleName();

    public static void init(Application app) {
        if (!(app instanceof FlutterApplication)) {
            FlutterMain.startInitialization(app);
        }

        //路由,Flutter 启动Native页面的时候回调这里
        INativeRouter router = (context, url, urlParams, requestCode, exts) -> {
            String assembleUrl = Utils.assembleUrl(url, urlParams);
            PageRouter.openPageByUrl(context, assembleUrl, urlParams);
        };
        //插件注册
        FlutterBoost.BoostPluginsRegister pluginsRegister = mRegistry -> {
            GeneratedPluginRegistrant.registerWith(mRegistry);
            //注册native的TextView插件,插件名称:TextPlatformViewPlugin
            TextPlatformViewPlugin.register(mRegistry.registrarFor("TextPlatformViewPlugin"));
        };
        //配置
        Platform platform = new FlutterBoost.ConfigBuilder(app, router)
                .isDebug(true)
                .whenEngineStart(FlutterBoost.ConfigBuilder.ANY_ACTIVITY_CREATED)
                .renderMode(FlutterView.RenderMode.texture)
                .pluginsRegister(pluginsRegister)
                .build();

        //初始化flutter_boost
        FlutterBoost.instance().init(platform);
    }
}
image.gif

PageRouter.java:是一个自定义的路由控制辅助类,方便管理页面跳转

public class PageRouter {

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

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

    public static final String NATIVE_PAGE_URL = "sample://nativePage";

    public static final String FLUTTER_DEMO_PAGE_URL = "sample://demoFlutterPage";
    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)) {
                //打开指定url的flutter页面
                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(FLUTTER_FRAGMENT_PAGE_URL)) {
                //打开flutter创建的fragment
                context.startActivity(new Intent(context, FlutterFragmentPageActivity.class));
                return true;
            } else if (url.startsWith(NATIVE_PAGE_URL)) {
                //打开原生Activity
                context.startActivity(new Intent(context, NativePageActivity.class));
                return true;
            }
            return false;
        } catch (Throwable t) {
            return false;
        }
    }
}
image.gif

flutter侧的代码我整理到lib下了,具体如下

main.dart文件,我自己在生成的事例代码上添加了自己的一些逻辑

void main() => runApp(MyFlutterBoostApp({
      'embeded': (pageName, params, String id) => EmbededFirstRouteWidget(),
      'first': (pageName, params, String id) => FirstRouteWidget(),
      'second': (pageName, params, String id) => SecondRouteWidget(),
      'tab': (pageName, params, String id) => TabRouteWidget(),
      'platformView': (pageName, params, String id) => PlatformRouteWidget(),
      'flutterDemoPage': (String url, Map params, String id) =>
          MyHomePage(title: '$url $id'),
      'flutterPage': (String pageName, Map params, String id) =>
          FlutterRouteWidget(params: params),
      'flutterFragment': (String url, Map params, String id) =>
          FragmentRouteWidget(params),
    }));
image.gif

my_flutter_boost_app.dart文件

class MyFlutterBoostApp extends StatefulWidget {
  final Map<String, PageBuilder> builders;

  MyFlutterBoostApp(this.builders);

  @override
  _MyFlutterBoostAppState createState() => _MyFlutterBoostAppState();
}

class _MyFlutterBoostAppState extends State<MyFlutterBoostApp> {
  @override
  void initState() {
    super.initState();
    FlutterBoost.singleton.registerPageBuilders(widget.builders);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      builder: FlutterBoost.init(postPush: _onRoutePushed),
      home: Container(),
    );
  }

  void _onRoutePushed(
    String pageName,
    String uniqueId,
    Map params,
    Route route,
    Future _,
  ) {
    print("flutter端路由:pageName:$pageName\n params:$params\n route:$route");
  }
}
image.gif

核心代码为: FlutterBoost.singleton.registerPageBuilders(widget.builders)这句,flutter-boost注册一个flutter 的widget供native侧通过url来调用。

7、manifest文件的配置

必须注册这个activity,否则无法打开flutter页面

 <!--        这个必须注册,不然无法打开flutter的页面-->
        <activity
            android:name="com.idlefish.flutterboost.containers.BoostFlutterActivity"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
            android:hardwareAccelerated="true"
            android:screenOrientation="portrait"
            android:theme="@style/Theme.AppCompat"
            android:windowSoftInputMode="adjustResize">
            <meta-data
                android:name="io.flutter.embedding.android.SplashScreenDrawable"
                android:resource="@drawable/page_loading" />

        </activity>
image.gif

在每个需要使用flutter页面的activity中都要加入android:hardwareAccelerated="true"这个属性的配置

8、如果native项目出现support库重复的情况,可以在项目的build.gradle文件中加入以下代码来排除重复包

def addRepos(RepositoryHandler handler) {
    handler.google()
    handler.jcenter()
    handler.maven { url "https://jitpack.io" }
}

allprojects {
    //循环所有依赖,指定support的版本为28.0.0,解决重复包问题
    //此方法在编译的时候会循环依赖,所以会增加编译时间。建议在
    //依赖的时候使用exclude 方法排除可能重复的包
    addRepos(repositories)
    subprojects {
        project.configurations.all {
            resolutionStrategy.eachDependency { details ->
                if (details.requested.group == 'com.android.support'
                        && !details.requested.name.contains('multidex')) {
                    details.useVersion "28.0.0"
                }
            }
        }
    }
}
image.gif

以上就是flutter-boot混合项目脚手架的使用以及flutter-boost的配置了,每一步都是本人自己通过动手实践验证过的,如过程中有错误,首页确认以下flutter版本和使用的flutter-boost的版本,版本对应关系以flutter-boost官网介绍为准https://github.com/alibaba/flutter_boost

博客:https://blog.csdn.net/qq_19979101

简易MVP,支持Jetpack:https://blog.csdn.net/qq_19979101/article/details/103691091

Flutter混合项目搭建之Flutter_Boot的使用:https://blog.csdn.net/qq_19979101/article/details/103777979

Flutter中Sqlite使用:https://blog.csdn.net/qq_19979101/article/details/93030803

Flutter的Stomp-websocket插件:https://blog.csdn.net/qq_19979101/article/details/93873731

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342