初嘗-Flutter-與-Native-混合開發-FlutterBoost-應用

前言

在跨平臺開發中 Flutter 優點很明瞭,簡單總結 :
ios

  1. 接近原生的性能
  2. 熱重載
  3. 豐富的組件

考慮項目有大量原生業務,咱們也不可能基於 Flutter 重構全部的業務。
所以只能在原有的基礎上混合使用 Flutter 來開發新業務或重構舊業務。
參考閒魚,哈羅等,他們也提供相應的解決混合開發方案 flutter_boostflutter_thrio
目前咱們採用 flutter_boost 做爲咱們 tw591 項目的解決方案。git

Flutter 混編現有項目

正式接入:github

  1. 建立 Flutter 項目flutter create -t module flutter_module, 建議建立完能夠執行用 AndoirdStudio 編譯執行,看是否正常,不過記得刪除生成的ios 文件,不然會遇第4條說的flutter_export_environment 問題數據庫

  2. 將建立的 flutter_module 項目到本身目標倉庫中swift

  3. 在 Podfile 文件添加以下腳本api

// 注意:這個指定路徑便是你的 flutter_module 項目的目標路徑
flutter_application_path = '../../TWFlutter591/flutter_module'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')

target 'twhouse' do
install_all_flutter_pods(flutter_application_path)
end
複製代碼
  1. 注意項目報 flutter_export_environment.sh 文件路徑錯誤

處理方式是刪除flutter_module/ios 文件緩存

用AndoirdStudio 或者 VSCode 運行一遍,產生新的 flutter_export_environment.sh 文件markdown

  1. 集成閒魚 flutter_boost 混合開發方案
  • 注意:flutter_boost 版本須要和咱們 flutter 版本對應,例如:flutter_boost:v1.17.1-hotfixes 對應的flutter sdk:1.17.1
  • 使用 Flutter --version 命令查看當前版本
  • 打開 flutter_module 文件夾 下pubspec.yaml 文件,添加依賴
flutter_boost:
    git:
        url: 'https://github.com/alibaba/flutter_boost.git'
        ref: 'v1.17.1-hotfixes'
複製代碼
  • 執行命令 flutter packages get
  • 最後在項目中執行 pod install --repo-update

  • 執行 install 後原生項目 Flutter 目錄,會將咱們 Flutter 項目打包成 framework

Native 部分

  • TWFlutterUtil 單例類用來註冊 flutter 引擎,同時封裝一層調用,避免 FlutterBoostPlugin 直接調用
  • TWFlutterPlatformRouter 基於 Flutter_boost DEMO 提供的基礎上,根據咱們項目業務作了調整,主要是 flutter 與 Native 平臺交互的 Router
  • TWFlutterJumpUtil 主要是處理 Flutter 與 Native 相互路由跳轉間的業務邏輯
  • TWFlutterNativePageName 定義頁面路由名稱
  • TWFlutterNativeEventUtil 主要是處理 Flutter 與 Native 事件的業務邏輯

示例 TWFlutterUtil 部分

TWFlutterUtil.swift網絡

import UIKit
import flutter_boost
@objcMembers class TWFlutterUtil: NSObject {
    
    static let shareInstance = TWFlutterUtil()
    var router: TWFlutterPlatformRouter?
    var engine: FlutterEngine?
    
    /// 註冊 flutter 引擎
    func registerFlutter() {
        router = TWFlutterPlatformRouter()
        guard let router = router else {return}
        FlutterBoostPlugin.sharedInstance().startFlutter(with: router) {[weak self](engine) in
            self?.engine = engine
            HouseTool.dispatch(afterTime: 2.0) {
                TWFlutterNativeEventUtil.sendConfigureInfo()
            }
        }
    }
    
    // MARK: - sendEvent
    
    /** * Native層往Dart層發送事件,事件名稱經過eventName指定 * * @param eventName 事件名稱 * @param arguments 參數 */
    open class func sendEvent(_ eventName: String, arguments: [AnyHashable: Any]) {
        FlutterBoostPlugin.sharedInstance().sendEvent(eventName, arguments: arguments)
    }
    
    /** * 添加監聽Dart層調用Native層的事件 * * @param name 事件名稱 * @param listner 事件監聽器 */
    open class func addEventListener(_ listener: @escaping FLBEventListener, name: String) -> FLBVoidCallback {
        return FlutterBoostPlugin.sharedInstance().addEventListener(listener, forName: name)
    }
    
    // MARK: - open/close Page
    
    /** * 關閉頁面,混合棧推薦使用的用於操做頁面的接口 * * @param uniqueId 關閉的頁面惟一ID符 * @param resultData 頁面要返回的結果(給上一個頁面),會做爲頁面返回函數的回調參數 * @param exts 額外參數 * @param completion 注意必傳 關閉頁面的即時回調,頁面一旦關閉即回調 */
    open class func close(_ uniqueId: String, result resultData: [AnyHashable: Any], exts: [AnyHashable: Any], completion: @escaping (Bool) -> Void) {
        FlutterBoostPlugin.close(uniqueId, result: resultData, exts: exts, completion: completion)
    }
    
    /** * 打開新頁面(默認以push方式),混合棧推薦使用的用於操做頁面的接口;經過urlParams能夠設置爲以present方式打開頁面:urlParams:@{@"present":@(YES)} * * @param url 打開的頁面資源定位符 * @param urlParams 傳入頁面的參數; 如有特殊邏輯,能夠經過這個參數設置回調的id * @param exts 額外參數 * @param resultCallback 當頁面結束返回時執行的回調,經過這個回調能夠取得頁面的返回數據,如close函數傳入的resultData * @param completion 注意必傳 打開頁面的即時回調,頁面一旦打開即回調 */
    open class func open(_ url: String, urlParams: [AnyHashable: Any], exts: [AnyHashable: Any], onPageFinished resultCallback: @escaping ([AnyHashable: Any]) -> Void, completion: @escaping (Bool) -> Void) {
        FlutterBoostPlugin.open(url, urlParams: urlParams, exts: exts, onPageFinished: resultCallback, completion: completion)
    }
    
    /** * Present方式打開新頁面,混合棧推薦使用的用於操做頁面的接口 * * @param url 打開的頁面資源定位符 * @param urlParams 傳入頁面的參數; 如有特殊邏輯,能夠經過這個參數設置回調的id * @param exts 額外參數 * @param resultCallback 當頁面結束返回時執行的回調,經過這個回調能夠取得頁面的返回數據,如close函數傳入的resultData * @param completion 注意必傳 打開頁面的即時回調,頁面一旦打開即回調 */
    open class func present(_ url: String, urlParams: [AnyHashable: Any], exts: [AnyHashable: Any], onPageFinished resultCallback: @escaping ([AnyHashable: Any]) -> Void, completion: @escaping (Bool) -> Void) {
        FlutterBoostPlugin.present(url, urlParams: urlParams, exts: exts, onPageFinished: resultCallback, completion: completion)
    }
    
}
複製代碼

Native 打開 Flutter 頁面

頁面採用文件 page 頁面類名,做爲註冊 idapp

//在 iOS 中 TWFlutterUtil 打開更多頁面
TWFlutterUtil.open("TWMorePage",
                           urlParams: ["isDebug":API_DEBUG == 1 ? "1" : "0"],
                           exts: ["isHideNavigationBar":"1"],
                           onPageFinished: { (result) in
        }) { (finish) in
        }
    
複製代碼

Flutter 部分

  • TWFlutterBoostPage 主要是 flutter 項目的入口頁面
  • TWRouterBoost 用於註冊原生調用 flutter 的路由
  • TWFlutterNativeEvent 用於 flutter 與 native 的事件處理
  • TWRouterFlutterNative 路由跳轉業務邏輯處理

示例 TWFlutterBoostPage 部分

TWFlutterBoostPage

import 'package:flutter/material.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'package:flutter_module/common/tw_app_color.dart';
import 'package:flutter_module/router/tw_router_boost.dart';

class TWFlutterBoostPage extends StatefulWidget {
  @override
  _TWFlutterBoostAppState createState() => _TWFlutterBoostAppState();
}

class _TWFlutterBoostAppState extends State<TWFlutterBoostApp> {
  @override
  void initState() {
    super.initState();
    //初始化註冊...
    TWRouterBoost routerBoost  = TWRouterBoost();
    routerBoost.registerRouter();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Boost example',
        //(2)初始化路由
        debugShowCheckedModeBanner: true,
        theme: ThemeData(
          primaryColor: TWAppColor.tw_ff7f00,
          dividerColor: TWAppColor.tw_eeeeee,
        ),
        builder: FlutterBoost.init(postPush: _onRoutePushed),
        home: Container(
            color:Colors.white
        ));
  }

  void _onRoutePushed(
      String pageName,
      String uniqueId,
      Map<String, dynamic> params,
      Route<dynamic> route,
      Future<dynamic> _,
      ) {

  }
}
複製代碼

TWRouterFlutterNative

import 'package:flutter_boost/flutter_boost.dart';
import 'package:flutter_module/features/mine/more/tw_more_page.dart';

class TWRouterFlutterNative {

  ///************** Flutter to Naitive *******************/
  static const String tw_flutterOpenNative = 'TWFlutterOpenNative';

  ///************** Naitive to Flutter *******************/
  static const String tw_flutterMorePage = "TWMorePage";

  /// 路由跳轉邏輯處理 map
  static Map<String, PageBuilder> routerPageBuilder = <String, PageBuilder>{
    tw_flutterMorePage: (String pageName, Map<String, dynamic> params, String _) => TWMorePage(params: params,),
  };
}
複製代碼

TWRouterBoost

import 'package:flutter/material.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'package:flutter_module/router/tw_router_flutter_native.dart';
import 'package:flutter_module/util/tw_log.dart';

class TWRouterBoost extends NavigatorObserver{

  // 初始化註冊路由...
  void registerRouter() {
    FlutterBoost.singleton.registerPageBuilders(TWRouterFlutterNative.routerPageBuilder);
    FlutterBoost.singleton.addBoostNavigatorObserver(this);
  }

  ///************** NavigatorObserver Method *******************/
  void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
    TWLog("flutterboost#didPush");
  }

  void didPop(Route<dynamic> route, Route<dynamic> previousRoute) {
    TWLog("flutterboost#didPop");
  }

  void didRemove(Route<dynamic> route, Route<dynamic> previousRoute) {
    TWLog("flutterboost#didRemove");
  }

  void didReplace({Route<dynamic> newRoute, Route<dynamic> oldRoute}) {
    TWLog("flutterboost#didReplace");
  }
}
複製代碼

TWFlutterNativeEvent

import 'package:flutter_boost/flutter_boost.dart';
import 'package:flutter_module/config/tw_configure.dart';
import 'package:flutter_module/util/tw_log.dart';

class TWFlutterNativeEvent {

  ///************** Native 發送給 Flutter 事件名稱 *******************/
  static final String tw_flutterNativeEventConfigureInfo = "EventConfigureInfo";

  ///************** Flutter 發送給 Native 事件名稱 *******************/

  ///************** Native 發送給 Flutter 事件統一處理 *******************/

  /// 監聽啓動配置信息
  static void addConfigureInfo() {
    TWLog("開始配置啓動信息...");
    FlutterBoost.singleton.channel.addEventListener(TWFlutterNativeEvent.tw_flutterNativeEventConfigureInfo,
            (name, arguments) {
          TWConfigure.singleton.configure(arguments);
          return;
        });
  }
}
複製代碼

Flutter 打開 Native 頁面

採用項目路由方式打開 注意:TWFlutterOpenNative 標識 ID ,便是標記從 Flutter 頁面打開 Native 頁面 ,打開 Native 頁面路由 exts 字段 路由部分,建議是原來項目有的,統一Android 和 iOS 的路由規則標準,咱們 iOS 採用 MGJRouter , Android 採用 ARouter。定義一套項目的路由 URL 標準

///************** Private Method *******************/
  void clickAction(int index) {
    TWLog("點擊 index = $index");
    /// 打開評價,
    String open_page_url = "app:///xxxx/more_page?entrance=more_evalue&pushAnimation=1";
	/// 閒魚庫打開 原生api
    FlutterBoost.singleton.open(TWRouterFlutterNative.tw_flutterOpenNative, exts: {
      "app_open_url": app_open_url
    }).then((value) => print('call me when page is finished. did recieve native route result $value'));
 }
複製代碼

用 flutter 重構更多頁面, 體驗感受和原生差很少

優秀的第三方庫

爲了減小與原生橋接,儘可能讓 Android 和 iOS 能公用一套,那麼接下來要作的是一些基礎設施建設了。包括 網絡庫,狀態管理,圖片緩存,數據緩存等...

分類 地址 star
網絡庫 Dio 8.2k
狀態管理 bloc 5.3k
狀態管理 provider 2.8k
數據庫 sqflite 1.8k
圖片緩存庫 flutter_cached_network_image 1.4k
刷新控件 flutter_easyrefresh 2.2k
刷新控件 flutter_pulltorefresh 1.6k
輪播 flutter_swiper 2.6k
本地通知 flutter_local_notifications 1.1k
Widgets flukit 2.6k
Toast flutter_oktoast 288
Toast FlutterToast 818
菜單 flutter_slidable 1.4k
地圖 flutter_amap 137
地圖 flutter_amap_location 249
調試工具 flutter_slidable 308

目前項目結構

flutter_module
├──images #圖片資源
├──asset  #本地資源
├──lib   #項目代碼
	├── features    # 業務模塊
	|   ├── home    # 首頁	
	|   ├── search  # 搜尋	
	|   ├── news 	# 新聞	
	|   └── mine 	# 個人
	|       └──more # 更多 (例:更多頁面)
	|   		├── model 	# 模型
	|   		├── view 	# 視圖
	|   		├── tool 	# 工具	
	|   		└── page 	# 頁面  	
	|   	
	├── page        # 入口主頁面
	├── common      # 通用組件,頭文件,定義常量 (例:顏色常量)
	├── util        # 工具類,http工具,公共方法   
	├── widgets     # 各類封裝的可複用組件  
	├── store       # 數據相關 緩存庫等
	├── blocs       # 狀態管理相關	
	├── config      # 配置中心 (例:主題顏色,debug 線上切換方法等)
	└── router      # 路由
	     └──  頁面映射配置、observe 方法導出
複製代碼
相關文章
相關標籤/搜索