Flutter 工程化搭建

数智工坊主
• 阅读 1676

为了积极拥抱新技术并优化RN的性能问题,所以决定在新业务需求中引入Flutter技术栈

Flutter混合栈开发大致可以分为一下两种模式

native工程直接依赖开发

具体接入方式为,先在setting.gradle 中加入如下代码:

setBinding(new Binding([gradle: this]))
evaluate(new File(
        settingsDir,
        '../../Flutter Module工程根目录/.android/include_flutter.groovy'
))

其次在App的build.gradle 中加入如下代码:

 implementation project(':flutter')

最后在主工程的build.gradle 中加入如下代码即可:

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

native工程接入aar

新建Flutter module工程

flutter create -t module xx_module

目录结构如下

xx_modlue 
          - .android // Android测试工程
          - .ios  // iOS测试工程
          - lib  // Flutter主工程
                - main.dart // Flutter入口文件
          - pubspec.yaml  // Flutter三方包配置文件

Flutter中提供了将module打包成aar的命令,生成的aar文件路径为 xx_modlue/build/host/outputs/repo

flutter build aar

将生成的aar文件引入Android开发工程即可完成aar的引用

到目前为止整个aar的引入基本是可以正常开发的,但是存在问题,那就是在每次开发都需要手动的将生成的aar包复制到主工程中进行依赖,不仅操作麻烦而且会出错,所以讲Flutter打包及引入流程变成日常开发常用的模式是最佳实践

flutter 打包上传流程分析:

为符合日常开发流程,需要将Flutter打成的aar文件上传至maven,因此首要任务就是解决将aar上传至maven问题

查看生成的aar目录下面的pom文件会发现主工程依赖的第三方aar包也会被下载至xx_modlue/build/host/outputs/repo路径下,pom文件如下:

<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.xxx.flutter</groupId>
  <artifactId>xxx</artifactId> 
  <version>release-0.0.7</version>
  <packaging>aar</packaging>
  <dependencies>
    <dependency>
      <groupId>io.flutter</groupId>
      <artifactId>flutter_embedding_release</artifactId>
      <version>1.0.0-af51afceb8886cc11e25047523c4e0c7e1f5d408</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>io.flutter</groupId>
      <artifactId>armeabi_v7a_release</artifactId>
      <version>1.0.0-af51afceb8886cc11e25047523c4e0c7e1f5d408</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>io.flutter</groupId>
      <artifactId>arm64_v8a_release</artifactId>
      <version>1.0.0-af51afceb8886cc11e25047523c4e0c7e1f5d408</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>io.flutter</groupId>
      <artifactId>x86_64_release</artifactId>
      <version>1.0.0-af51afceb8886cc11e25047523c4e0c7e1f5d408</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>
</project>

分析pom文件可知在上传主工程生成的aar的时候我们还需要将下载下来的第三方aar上传至maven库,因此我们得知具体工程化脚本流程如下:

1、获取生成的aar路径
2、上传第三方依赖的aar文件
3、更新主工程aar的artifactId
4、上传主工程aar文件

具体脚本如下:

deploy_aar(){
    mvn deploy:deploy-file \
    -DpomFile="$FILE_PATH/$NAME.pom" \
    -DgeneratePom=false \
    -Dfile="$FILE_PATH/$NAME.aar" \
    -Durl="http://xxx.xxx.xxx:xxx/repository/public/" \
    -DrepositoryId="nexus" \
    -Dpackaging=aar \
    -s="mvn-settings.xml" \
    -Dversion="$VERSION"
}

projectDir=`pwd`

# 清除Flutter生成文件
flutter clean

# 获取pub包
flutter pub get

# 删除文件夹
rm -rf `pwd`/build/host/outputs/repo/


# 修改版本号
group="com.xxx.flutter"
type="release"
#type="debug"
#type="profile"
version="${type}-0.0.7"
artifactId="xxx"


echo "替换Flutter/build.gradle 中的group 为${group}"
path=`pwd`/.android/Flutter/build.gradle
sed -i '' '29s/^.*$/group "'${group}'"/'  ${path}
echo "替换Flutter/build.gradle 中的version 为${version}"
path=`pwd`/.android/Flutter/build.gradle
sed -i '' '30s/^.*$/version "'${version}'"/'  ${path}


# 打包AAR
flutter build aar --no-debug --no-profile

# 找到AAR并上传
path=`pwd`
# shellcheck disable=SC2006
p=`find ${path}/build/host/outputs/repo -type f  -name "*${type}*.aar"`
echo "${p}"


array=(${p//'\n'/})
currentName=""
currentPath=""
currentPom=""
currentDir=""

# shellcheck disable=SC2068
for item in ${array[@]}
do
    resFile=`basename ${item}`
    echo "${item}"
    result=$(echo ${resFile} | grep "flutter_release")
    if [[ "$result" == "" ]]
    then
      lenght=${#item}
      sub=${item:0:${lenght}-3}
      pom="${sub}pom"
      resFileLenght=${#resFile}
      subDir=${item:0:${lenght}-${resFileLenght}}
      curName=`echo ${resFile} | cut -d "-" -f 2`
      curNameLenght=${#curName}
      subVersion=${curName:0:${curNameLenght}-4}
      nameLenght="${#resFile}"
      subName=${resFile:0:${nameLenght}-4}
      export FILE_PATH="${subDir}"
      export NAME="${subName}"
      export VERSION=${subVersion}
      deploy_aar
    else
      nameLenght="${#resFile}"
      subName=${resFile:0:${nameLenght}-4}
      currentName="${subName}"
      currentPath=${item}
      currentPath=${item}
      lenght=${#item}
      sub=${item:0:${lenght}-3}
      currentPom="${sub}pom"
      resFileLenght=${#resFile}
      subDir=${item:0:${lenght}-${resFileLenght}}
      currentDir=${subDir}
    fi
done

cd "${currentDir}"
echo `pwd`
mv "${currentName}.aar" "${artifactId}-${version}.aar"
mv "${currentName}.pom" "${artifactId}-${version}.pom"
cd ${projectDir}
echo `pwd`

currentName="${artifactId}-${version}"
currentPath="${currentDir}${currentName}.aar"
currentPom="${currentDir}${currentName}.pom"
echo "current name is ${currentName}"
echo "current path is ${currentPath}"
echo "currentPom is ${currentPom}"
echo "替换pom artifactId为${artifactId}"
sed -i '' '6s/^.*$/  <artifactId>'${artifactId}'<\/artifactId> /'  ${currentPom}
echo "currentDir is ${currentDir}"
echo "currentVersion is ${version}"


export FILE_PATH="${currentDir}"
export NAME="${currentName}"
export VERSION=${version}
deploy_aar

上传maven成功后,主工程依赖Flutter代码就和添加第三方SDK流程一致了。

选型对比

名称优点缺点
native工程直接依赖开发接入快工程结构复杂,无法将Flutter开发从native开发流程中剥离
native工程接入aarFlutter开发与native开发流程解耦初期接入流程复杂

最终选择为通过maven方式接入aar方便后续拓展

Flutter 混合栈选型

在完成Flutter混合开发接入流程后,会有混合栈管理问题,在混合方案中解决的主要问题是如何去处理交替出现的Flutter和Native页面。综合目前的开源框架,选型为FlutterBoost

flutterBoost Flutter端接入:

FlutterBoost.singleton.registerPageBuilders(<String, PageBuilder>{
      testhome: (String pageName, Map<dynamic, dynamic> params, String _) =>
          MyHomePage(title: ''),
      shoppingcar: (String pageName, Map<dynamic, dynamic> params, String _) {
        String platformItemNo = '';
        if (params.containsKey("platformItemNo")) {
          platformItemNo = params['platformItemNo'];
          NativeChat.print(platformItemNo);
        }
        return ShoppingCar(platformItemNo: platformItemNo);
      },
      login: (String pageName, Map<dynamic, dynamic> params, String _) =>
          LoginPage(),
      overlay: (String pageName, Map<dynamic, dynamic> params, String _) =>
          OverlayPage(),
    });

android端接入:

application 初始化代码:

val router =
            INativeRouter { context, url, urlParams, requestCode, exts ->
                PageRouter.openPageByUrl(context, url, urlParams)
            }

        val boostLifecycleListener = object : FlutterBoost.BoostLifecycleListener {
            override fun onEngineCreated() {
            }

            override fun onPluginsRegistered() {
            }

            override fun beforeCreateEngine() {
            }

            override fun onEngineDestroy() {
            }

        }

        val platform = FlutterBoost.ConfigBuilder(application, router)
            .isDebug(BuildConfig.DEBUG)
            .whenEngineStart(FlutterBoost.ConfigBuilder.ANY_ACTIVITY_CREATED)
            .renderMode(FlutterView.RenderMode.texture)
            .lifecycleListener(boostLifecycleListener)
            .build()
        FlutterBoost.instance().init(platform)

路由配置代码

// PageRouter 路由跳转及配置页面
object PageRouter {
    /**
     * 路由映射
     */
    val pageName: HashMap<String?, String?> =
        object : HashMap<String?, String?>() {
            init {
                put("xxxx://shoppingCar", "shoppingCar")
                put("xxxx://login", "login")
                put("xxxx://home", "home")
                put("xxxx://overlay", "overlay")
            }
        }
    const val SHOPPING_CAR = "xxxx://shoppingCar"
    const val LOGIN_PAGE = "xxxx://login"
    const val OVERLAY = "xxxx://overlay"
    const val BUYER_PRODUCT_DETAIL = "xxxx://buyer/productdetail"
    const val TEST_SECOND = "xxxx://testSecond"

    @JvmOverloads
    fun openPageByUrl(
        context: Context,
        url: String,
        params: Map<*, *>?,
        requestCode: Int = 0
    ): Boolean {
        val path = url.split("\\?").toTypedArray()[0]
        Log.i("openPageByUrl", path)
        return try {
            when {
                pageName.containsKey(path) -> {
                    val intent =
                        BoostFlutterActivity.withNewEngine().url(pageName[path]!!)
                            .params(params!!)
                            .backgroundMode(BoostFlutterActivity.BackgroundMode.opaque)
                            .build(context)
                    if (context is Activity) {
                        context.startActivityForResult(intent, requestCode)
                    } else {
                        context.startActivity(intent)
                    }

                    return true
                }
                url.startsWith(TEST_SECOND) -> {
                    context.startActivity(
                        Intent(
                            context,
                            SecondActivity::class.java
                        )
                    )
                    return true
                }
                else -> false
            }
        } catch (t: Throwable) {
            false
        }
    }
}

native 跳转逻辑

// 初始化channel通知

 FlutterBoost.instance().channel().addMethodCallHandler { call, result ->
            when (call.method) {
                "baseUrl" -> {
                    result.success(ApiConstant.getApiUrl())
                }
            }
        }

// 跳转代码

 val params = hashMapOf<String, String>()
            params["param"] = param
            PageRouter.openPageByUrl(this, PageRouter.SHOPPING_CAR, params)

Flutter 测试环境搭建

在混合开发的工程中被吐槽最多的大概就是测试了吧,和native打包在一起调试费时费力,对前端开发要求高需要了解native的基本流程

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
3年前
Java日期时间API系列31
  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
浩浩 浩浩
4年前
【Flutter实战】初识Flutter
1.2初识Flutter1.2.1Flutter简介Flutter是Google推出并开源的移动应用开发框架,主打跨平台、高保真、高性能。开发者可以通过Dart语言开发App,一套代码同时运行在iOS和Android平台。Flutter提供了丰富的组件、接口,开发者可以很快地为Flutter添加native扩展。同时Flu
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Stella981 Stella981
3年前
JVM 字节码指令表
字节码助记符指令含义0x00nop什么都不做0x01aconst\_null将null推送至栈顶0x02iconst\_m1将int型1推送至栈顶0x03iconst\_0将int型0推送至栈顶0x04iconst\_1将int型1推送至栈顶0x05ic
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
少湖说 少湖说
8个月前
鸿蒙Flutter实战:07-混合开发
鸿蒙Flutter实战:混合开发鸿蒙Flutter混合开发主要有两种形式。1.基于har将fluttermodule打包成har包,在原生鸿蒙项目中,以har包的方式引入。其优点是主项目开发者可以不关注Flutter实现,不需要安装配置Flutter开发环
数智工坊主
数智工坊主
Lv1
浪女躲的过对酒当歌的夜躲不过四下无人的街
文章
4
粉丝
0
获赞
0