Flutter(六):Flutter_Boost接入现有原生工程(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()
}
}