Flutter(六):Flutter_Boost接入现有原生工程(iOS+Android)

本篇博客会介绍如何通过第三方插件Flutter_Boost实现接入原有工程。

如果不希望引入第三方插件,可以参考博客Flutter混合开发--接入现有原生工程(iOS+Android)

一、新建原生工程和Flutter Module

1、新建Android工程

搭建一个空的Android工程FlutterDemo_Android模拟已经存在的原有工程
Android项目配置:

2、新建iOS工程

搭建一个空的iOS工程FlutterDemo_iOS模拟已经存在的原有工程
Xcode项目配置

搭建Pod:
在工程目录下执行

pod init
pod install

3、新建Flutter Module

这里使用AndroidStudio做Flutter开发环境

3.1 AndroidStudio添加Flutter插件

3.2 新建Flutter Module

第一步:File -> New Flutter Project

第二步:选择Flutter

第三步:配置Flutter Module

flutter_demo_module

完成目录结构

二、Flutter Boost 配置

1、Flutter_Module工程

1.修改pubspec.yaml文件:

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2

  # add
  flutter_boost:  
    git:
      url: 'https://github.com/alibaba/flutter_boost.git'
      ref: '4.1.0'

然后执行flutter pub get下载flutter_boost到module中

2.新增willpop.dart页面做为跳转目标页

import 'package:flutter/material.dart';

class WillPopRoute extends StatefulWidget {
  const WillPopRoute({Key? key, this.title}) : super(key: key);

  final String? title;

  @override
  State<StatefulWidget> createState() => _WillPopRouteState();
}

class _WillPopRouteState extends State<WillPopRoute> {
  bool shouldPop = true;
  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        return shouldPop;
      },
      child: Scaffold(
        appBar: AppBar(
          title: const Text('Flutter Demo'),
        ),
      ),
    );
  }
}

3.修改main.dart文件,配置页面路由

import 'package:flutter/material.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'package:flutter_demo_module/willpop.dart';

void main() {
  CustomFlutterBinding();
  runApp(const MyApp());
}

class CustomFlutterBinding extends WidgetsFlutterBinding
    with BoostFlutterBinding {}


class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
    // 配置路由
  static Map<String, FlutterBoostRouteFactory> routerMap = {
    'flutterPage': (settings, uniqueId) {
      return PageRouteBuilder<dynamic>(
          settings: settings, pageBuilder: (_, __, ___) => const WillPopRoute());
    }
  };

  Route<dynamic>? routeFactory(RouteSettings settings, String? uniqueId) {
    FlutterBoostRouteFactory? func = routerMap[settings.name!];
    if (func == null) {
      return null;
    }
    return func(settings, uniqueId);
  }

  @override
  void initState() {
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return FlutterBoostApp(routeFactory);
  }

}

2、Android跳转Flutter:

1.setting.gralde添加:

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

注意:new Binding([gradle: this])会有红色提示,不用管

2.app的AndroidManifest添加:

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

</activity>
<meta-data android:name="flutterEmbedding"
    android:value="2">
</meta-data>

注意这一步之后执行gradle sync,如果遇到问题,可以参考文章最后的问题集锦

3.FlutterBoost初始化

自定义Application

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        FlutterBoost.instance().setup(this, object : FlutterBoostDelegate {
            override fun pushNativeRoute(options: FlutterBoostRouteOptions) {
                // 原生跳转
            }

            override fun pushFlutterRoute(options: FlutterBoostRouteOptions) {
                // Flutter跳转
            }
        }, FlutterBoost.Callback { engine: FlutterEngine? -> })
    }
}

配置app的AndroidManifest

<application
        android:name=".MyApp"
        ...
</application>

4.Android跳转代码

val params: Map<String, Any> = HashMap()
val intent: Intent = FlutterBoostActivity.CachedEngineIntentBuilder(
    FlutterBoostActivity::class.java
)
    .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.opaque)
    .destroyEngineWithActivity(false)
    .url("flutterPage")
    .urlParams(params)
    .build(context)
startActivity(intent)

3、iOS跳转Flutter:

1.修改Pod文件

step1. load file

flutter_application_path = '../flutter_demo_module'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')

step2. install function

install_all_flutter_pods(flutter_application_path)

step3 post_install

post_install do |installer|
  flutter_post_install(installer) if defined?(flutter_post_install)
end

完整pod文件

# Flutter Module Path
# step1 load file
flutter_application_path = '../flutter_demo_module'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')

target 'FlutterDemo_iOS' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!
  
  # step2 install function
  install_all_flutter_pods(flutter_application_path)
end

# step3 post_install
post_install do |installer|
  flutter_post_install(installer) if defined?(flutter_post_install)
end

执行pod install,podhelper.rb 脚本会把你的 plugins, Flutter.framework,和 App.framework 集成到你的项目中。

备注:
Flutter.framework 是 Flutter engine 的框架, App.framework 是你的 Dart 代码的编译产物。

2.添加Delegate

定义FlutterDelegate

import UIKit
import Flutter
import flutter_boost
import FlutterPluginRegistrant


class MyBoostAppDelegate: NSObject,FlutterBoostDelegate {
    
    static let shared = MyBoostAppDelegate()

    ///您用来push的导航栏
    var navigationController:UINavigationController?

    ///用来存返回flutter侧返回结果的表
    var resultTable:Dictionary<String,([AnyHashable:Any]?)->Void> = [:];

    func pushNativeRoute(_ pageName: String!, arguments: [AnyHashable : Any]!) {

        //可以用参数来控制是push还是pop
        let isPresent = arguments["isPresent"] as? Bool ?? false
        let isAnimated = arguments["isAnimated"] as? Bool ?? true
        //这里根据pageName来判断生成哪个vc,这里给个默认的了
        let targetViewController = UIViewController()

        if(isPresent){
            self.navigationController?.present(targetViewController, animated: isAnimated, completion: nil)
        }else{
            self.navigationController?.pushViewController(targetViewController, animated: isAnimated)
        }
    }

    func pushFlutterRoute(_ options: FlutterBoostRouteOptions!) {
        let vc:FBFlutterViewContainer = FBFlutterViewContainer()
        vc.setName(options.pageName, uniqueId: options.uniqueId, params: options.arguments,opaque: options.opaque)

        //用参数来控制是push还是pop
        let isPresent = (options.arguments?["isPresent"] as? Bool)  ?? false
        let isAnimated = (options.arguments?["isAnimated"] as? Bool) ?? true

        //对这个页面设置结果
        resultTable[options.pageName] = options.onPageFinished;

        //如果是present模式 ,或者要不透明模式,那么就需要以present模式打开页面
        if(isPresent || !options.opaque){
            self.navigationController?.present(vc, animated: isAnimated, completion: nil)
        }else{
            self.navigationController?.pushViewController(vc, animated: isAnimated)
        }
    }

    func popRoute(_ options: FlutterBoostRouteOptions!) {
        //如果当前被present的vc是container,那么就执行dismiss逻辑
        if let vc = self.navigationController?.presentedViewController as? FBFlutterViewContainer,vc.uniqueIDString() == options.uniqueId{

            //这里分为两种情况,由于UIModalPresentationOverFullScreen下,生命周期显示会有问题
            //所以需要手动调用的场景,从而使下面底部的vc调用viewAppear相关逻辑
            if vc.modalPresentationStyle == .overFullScreen {

                //这里手动beginAppearanceTransition触发页面生命周期
                self.navigationController?.topViewController?.beginAppearanceTransition(true, animated: false)

                vc.dismiss(animated: true) {
                    self.navigationController?.topViewController?.endAppearanceTransition()
                }
            }else{
                //正常场景,直接dismiss
                vc.dismiss(animated: true, completion: nil)
            }
        }else{
            self.navigationController?.popViewController(animated: true)
        }
        //否则直接执行pop逻辑
        //这里在pop的时候将参数带出,并且从结果表中移除
        if let onPageFinshed = resultTable[options.pageName] {
            onPageFinshed(options.arguments)
            resultTable.removeValue(forKey: options.pageName)
        }
    }
}

修改AppDelegate

import UIKit
import flutter_boost

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
       
        //创建代理,做初始化操作
        let delegate = MyBoostAppDelegate.shared
        FlutterBoost.instance().setup(application, delegate: delegate) { engine in

        }
        return true
    }
}

跳转Flutter页面代码

    @IBAction func clickButton(_ sender: Any) {
        MyBoostAppDelegate.shared.navigationController = self.navigationController
        let options = FlutterBoostRouteOptions()
        options.pageName = "flutterPage"
        FlutterBoost.instance().open(options)
    }

问题集锦

1.Android导入Flutter Module报错Failed to apply plugin class ‘FlutterPlugin’

setting.gradle修改Model值为RepositoriesMode.PREFER_PROJECT

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
    repositories {
        google()
        mavenCentral()
    }
}
posted @ 2022-10-10 17:05  柳云居士  阅读(2533)  评论(0编辑  收藏  举报