preface

The advantages of Flutter in cross-platform development are clear.

  1. Near-native performance
  2. Thermal overload
  3. Rich components

Given that the project has a large number of native businesses, it is not possible to refactor all businesses based on Flutter. Therefore, Flutter can only be used on an existing basis to develop new business or restructure old business. Refer to Idle Fish, Harrow, etc., who also provide the corresponding solutions for mixed development: Flutter_boost and Flutter_thrio. At present, we adopt Flutter_Boost as the solution of our TW591 project.

Flutter remix existing projects

Official Access:

  1. Create a Flutter project, Flutter create -t module flutter_module, create a Flutter project, Flutter create -t module flutter_module Otherwise, you will encounter the flutter_export_environment problem described in clause 4

  2. Add the created Flutter_module project to your target repository

  3. Add the following script to your Podfile

Flutter_application_path = '.. 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) endCopy the code
  1. Notice The flutter_export_environment.sh file path is incorrect

To handle this, delete the Flutter_module /ios file

Run this with AndoirdStudio or VSCode to generate the new flutter_export_environment.sh file

  1. Integrated idle fish Flutter_boost hybrid development program
  • Note: The Flutter_boost version needs to correspond to our flutter version, for example, Flutter_boost :v1.17.1-hotfixes corresponding to the FLUTTER SDK: 1.17.1
  • Use the Flutter –version command to view the current version
  • Open pubspec.yaml in the flutter_module folder and add dependencies
Flutter_boost: git: url: 'https://github.com/alibaba/flutter_boost.git' ref: 'v1.17.1 - hotfixes'Copy the code
  • Execute the commandflutter packages get
  • Finally, it is implemented in the projectpod install --repo-update

  • The native project Flutter directory after install is executed will package our Flutter project into a framework

Native part

  • The TWFlutterUtil singleton class is used to register the FLUTTER engine and encapsulate a layer of calls to avoid direct calls from the FlutterBoostPlugin
  • Based on Flutter_boost DEMO, TWFlutterPlatformRouter was adjusted according to our project business, mainly the Router that FLUTTER interacts with Native platform
  • TWFlutterJumpUtil mainly deals with the business logic between Flutter and Native hop routes
  • TWFlutterNativePageName Defines the page route name
  • TWFlutterNativeEventUtil mainly deals with the business logic of Flutter and Native events

Sample TWFlutterUtil section

TWFlutterUtil.swift

import UIKit
import flutter_boost
@objcMembers class TWFlutterUtil: NSObject {
    
    static let shareInstance = TWFlutterUtil(a)var router: TWFlutterPlatformRouter?
    var engine: FlutterEngine?
    
    // register the FLUTTER engine
    func registerFlutter(a) {
        router = TWFlutterPlatformRouter(a)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 layer sends events to Dart layer, eventName specified by eventName ** @param eventName eventName * @param arguments */
    open class func sendEvent(_ eventName: String.arguments: [AnyHashable: Any]) {
        FlutterBoostPlugin.sharedInstance().sendEvent(eventName, arguments: arguments)
    }
    
    /** * Add event listener for Dart layer to call Native layer ** @param name event name * @param listner event listener */
    open class func addEventListener(_ listener: @escaping FLBEventListener.name: String) - >FLBVoidCallback {
        return FlutterBoostPlugin.sharedInstance().addEventListener(listener, forName: name)
    }
    
    // MARK: - open/close Page
    
    ** @param uniqueId the uniqueId of the closed page * @param resultData the result to return from the page (for the previous page), * @param Completion * @Param Completion Note that an immediate callback is required to close the page, which will be called back once the page is closed */
    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)
    }
    
    /** * Open a new page (default is push), mixing the recommended interface for operating the page; UrlParams can be set to present the page: urlParams:@{@"present":@(YES)} * * @param url url to open the page resource locator * @param urlParams passed in the page parameter; @param resultCallback The callback that is executed when the page returns at the end of the page. This callback is used to retrieve the data returned from the page. For example, in the resultData * @param completion passed in by the close function, note that the immediate callback to open the page must be passed, which will be called */ once the page is opened
    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)
    }
    
    ** @param url = @param urlParams = @param urlParams; @param resultCallback The callback that is executed when the page returns at the end of the page. This callback is used to retrieve the data returned from the page. For example, in the resultData * @param completion passed in by the close function, note that the immediate callback to open the page must be passed, which will be called */ once the page is opened
    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)
    }
    
}
Copy the code

Native opens the Flutter page

The page uses the file page class name as the registration ID

// TWFlutterUtil opens more pages in iOS
TWFlutterUtil.open("TWMorePage",
                           urlParams: ["isDebug":API_DEBUG = = 1 ? "1" : "0"],
                           exts: ["isHideNavigationBar":"1"],
                           onPageFinished: { (result) in
        }) { (finish) in
        }
    
Copy the code

Flutter part

  • TWFlutterBoostPage is mainly the entry page for the Flutter project
  • TWRouterBoost is used to register routes to native calls to flutter
  • TWFlutterNativeEvent is used for flutter and native event processing
  • TWRouterFlutterNative Service logic processing of route redirection

Example TWFlutterBoostPage section

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();
    // Initialize registration...
    TWRouterBoost routerBoost  = TWRouterBoost();
    routerBoost.registerRouter();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Boost example'.// (2) Initialize the route
        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> _,) {}}Copy the code

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";

  /// Route hop logic processes map
  static Map<String, PageBuilder> routerPageBuilder = <String, PageBuilder>{
    tw_flutterMorePage: (String pageName, Map<String.dynamic> params, String _) => TWMorePage(params: params,),
  };
}
Copy the code

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{

  // Initialize the registered route...
  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"); }}Copy the code

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 sends the Flutter event name *** * * ** * * ** * * ** * * ** /
  static final String tw_flutterNativeEventConfigureInfo = "EventConfigureInfo";

  ///* * * ** * * ** * * *** The event name ** is sent to Native by *** * * ** * * ** * * ** * * **/

  ///* * * ** * * ** * * *** Native sends uniform processing to Flutter events *** * * ** * * ** * * ** * * ** /

  /// Listen for startup configuration information
  static void addConfigureInfo() {
    TWLog("Start configuring startup information...");
    FlutterBoost.singleton.channel.addEventListener(TWFlutterNativeEvent.tw_flutterNativeEventConfigureInfo,
            (name, arguments) {
          TWConfigure.singleton.configure(arguments);
          return; }); }}Copy the code

Flutter opens the Native page

Open in project routing mode note: TWFlutterOpenNative ID is to mark the opening of the Native page from the Flutter page and the routing part of the exTS field on the Native page. It is suggested that the original project should unify the routing rules and standards of Android and iOS. We use MGJRouter for iOS and ARouter for Android. Define a set of routing URL standards for projects

  ///* * * ** * * ** * * *** Private Method *** * * ** * * ** * * ** * * ** /
  void clickAction(int index) {
    TWLog("Index =$index");
    /// Open the evaluation,
    String open_page_url = "app:///xxxx/more_page? entrance=more_evalue&pushAnimation=1";
	/// Free fish library open native 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'));
 }
Copy the code

Refactor more pages with Flutter, and the experience feels similar to native

Excellent third party library

In order to minimize native bridging and make Android and iOS as common as possible, some infrastructure needs to be built. Including network library, state management, image cache, data cache and so on…

classification address star
The network library Dio 8.2 k.
State management bloc 5.3 k.
State management provider 2.8 k.
The database sqflite 1.8 k.
Image cache library flutter_cached_network_image 1.4 k.
Refresh the controls flutter_easyrefresh 2.2 k.
Refresh the controls flutter_pulltorefresh 1.6 k.
shuffling flutter_swiper 2.6 k.
Local notifications flutter_local_notifications 1.1 k.
Widgets flukit 2.6 k.
Toast flutter_oktoast 288
Toast FlutterToast 818
The menu flutter_slidable 1.4 k.
The map flutter_amap 137
The map flutter_amap_location 249
A debugging tool flutter_slidable 308

Current project Structure

Flutter_module ├ ─ ─ images # image resources ├ ─ ─ asset # local resources ├ ─ ─ lib # project code ├ ─ ─ the features # business module | ├ ─ ─ home # page | ├ ─ ─ # search search | ├ ─ ─ # # news news | └ ─ ─ mime my | └ ─ ─ more # more (example: More pages) | ├ ─ ─ the model # model | ├ ─ ─ the view # view | ├ ─ ─ tool # tools | └ ─ ─ page # page | ├ ─ ─ page # portal home page ├ ─ ─ common # general components, header files, defining constants (example: │ ├─ Utility class │ HTTP tools │ public Methods │ ├─ All kinds of Widgets │ ├─ Store │ ├ s │ State Management │ ├─ Config │ Center ├ ─ ├ ─ router # ├ ─ imp (1), imp (2), imp (2), imp (3Copy the code