Skip to content

Latest commit

 

History

History
executable file
·
613 lines (481 loc) · 18.6 KB

install.md

File metadata and controls

executable file
·
613 lines (481 loc) · 18.6 KB

各平台安装

1. 目录结构

我们新建一个文件夹FlutterBoostExample,这个文件夹下面放置另外三个文件夹。 另外三个分别是您的Android工程,iOS工程,以及需要接入的flutter module, 这个地方注意,flutter一定是module,而不是工程项目,判断是不是module的方法就是看其是否有android和ios文件夹, 如果没有,那就是module,才是正确的

在这里我们命名为BoostTestAndroidBoostTestIOS,以及flutter_module 注意:这三个工程在同级目录下

现在可以开始搞事情了

Dart部分

  1. 首先,需要添加FlutterBoost依赖到yaml文件
flutter_boost:
  git:
    url: 'https://github.com/alibaba/flutter_boost.git'
    ref: '4.6.5'

之后在flutter工程下运行flutter pub get dart端就集成完毕了,然后可以在dart端放上一些代码,以下代码基于example3.0

//这里要特别注意,如果你的工程里已经有一个继承自WidgetsFlutterBinding的自定义Binding,则只需要将其with上BoostFlutterBinding //如果你的工程没有自定义的Binding,则可以参考这个CustomFlutterBinding的做法 //BoostFlutterBinding用于接管Flutter App的生命周期,必须得接入的

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_boost/flutter_boost.dart';

void main() {
  ///这里的CustomFlutterBinding调用务必不可缺少,用于控制Boost状态的resume和pause
  CustomFlutterBinding();
  runApp(MyApp());
}


///创建一个自定义的Binding,继承和with的关系如下,里面什么都不用写
class CustomFlutterBinding extends WidgetsFlutterBinding with BoostFlutterBinding {}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  /// 由于很多同学说没有跳转动画,这里是因为之前exmaple里面用的是 [PageRouteBuilder]
  /// 其实这里是可以自定义的,和Boost没太多关系,比如我想用类似iOS平台的动画,
  /// 那么只需要像下面这样写成 [CupertinoPageRoute] 即可
  /// (这里全写成[MaterialPageRoute]也行,这里只不过用[CupertinoPageRoute]举例子)
  ///
  /// 注意,如果需要push的时候,两个页面都需要动的话,
  /// (就是像iOS native那样,在push的时候,前面一个页面也会向左推一段距离)
  /// 那么前后两个页面都必须是遵循CupertinoRouteTransitionMixin的路由
  /// 简单来说,就两个页面都是CupertinoPageRoute就好
  /// 如果用MaterialPageRoute的话同理

  Map<String, FlutterBoostRouteFactory> routerMap = {
    'mainPage': (RouteSettings settings, String uniqueId) {
      return CupertinoPageRoute(
          settings: settings,
          builder: (_) {
            Map<String, Object> map = settings.arguments as Map<String, Object> ;
            String data = map['data'] as String;
            return MainPage(
              data: data,
            );
          });
    },
    'simplePage': (settings, uniqueId) {
      return CupertinoPageRoute(
          settings: settings,
          builder: (_) {
            Map<String, Object> map = settings.arguments as Map<String, Object>;
            String data = map['data'] as String;
            return SimplePage(
              data: data,
            );
          });
    },
  };

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

  Widget appBuilder(Widget home) {
    return MaterialApp(
      home: home,
      debugShowCheckedModeBanner: true,

      ///必须加上builder参数,否则showDialog等会出问题
      builder: (_, __) {
        return home;
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return FlutterBoostApp(
      routeFactory,
      appBuilder: appBuilder,
    );
  }
}

class MainPage extends StatelessWidget {
  const MainPage({Object data});
  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(child: Text('Main Page')),
    );
  }
}

class SimplePage extends StatelessWidget {
  const SimplePage({Object data});
  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body:  Center(child: Text('SimplePage')),
    );
  }
}

到此dart端就集成完毕了

Android部分

  1. 在setting.gradle文件中添加如下的代码,这一步是引用flutter工程 添加之后Binding会报红,这个地方不管他,直接往下看
setBinding(new Binding([gradle: this]))
evaluate(new File(
        settingsDir.parentFile,
        'flutter_module/.android/include_flutter.groovy'
))
include ':flutter_module'
project(':flutter_module').projectDir = new File('../flutter_module')
  1. 然后在app的build.gradle下添加如下代码
implementation project(':flutter')
implementation project(':flutter_boost')
  1. 还需要在清单文件中添加以下内容直接粘贴到<application>标签包裹的内部即可,也就是和其他<activity>标签同级
<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>

然后点击右上角的sync同步一下,就会开始一些下载和同步的进程,等待完成

  1. Application中添加FlutterBoost的启动流程,并设置代理
public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        FlutterBoost.instance().setup(this, new FlutterBoostDelegate() {
            @Override
            public void pushNativeRoute(FlutterBoostRouteOptions options) {
                //这里根据options.pageName来判断你想跳转哪个页面,这里简单给一个
                Intent intent = new Intent(FlutterBoost.instance().currentActivity(), YourTargetAcitvity.class);
                FlutterBoost.instance().currentActivity().startActivityForResult(intent, options.requestCode());
            }

            @Override
            public void pushFlutterRoute(FlutterBoostRouteOptions options) {
                Intent intent = new FlutterBoostActivity.CachedEngineIntentBuilder(FlutterBoostActivity.class)
                        .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
                        .destroyEngineWithActivity(false)
                        .uniqueId(options.uniqueId())
                        .url(options.pageName())
                        .urlParams(options.arguments())
                        .build(FlutterBoost.instance().currentActivity());
                FlutterBoost.instance().currentActivity().startActivity(intent);
            }
        }, engine -> {
        });
    }
}

到此为止Android的集成流程就全部完成

iOS部分

  1. 首先到自己的iOS目录下,执行pod init,之后执行一次pod install

  2. 打开创建的Podfile文件,添加以下代码

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

添加之后,您的Podfile应该类似下面这样

# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

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

target 'BoostTestIOS' do
  use_frameworks!

  install_all_flutter_pods(flutter_application_path)

end

然后再执行pod install,安装完成

3.进行准备工作创建FlutterBoostDelegate。 这里面的内容是完全可以自定义的,在您了解各个API的含义时,你可以完全自定义这里面每个方法的代码,下面只是给出大多数场景的默认解法

class BoostDelegate: NSObject,FlutterBoostDelegate {

    ///您用来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,这里给个默认的了
        var 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)
        }
    }
}

4.在AppDelegatedidFinishLaunchingWithOptions方法中进行初始化

//创建代理,做初始化操作
let delegate = BoostDelegate()
FlutterBoost.instance().setup(application, delegate: delegate) { engine in

}

OHOS部分

单UIAbility

整个应用只有唯一UIAbility,Flutter界面由FlutterPage进行承载

  1. Flutter与UIAbility进行绑定

在UIAbility的onCreateonDestroyonWindowStageCreateonWindowStageDestroy生命周期函数将Flutter进行绑定。

async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
  FlutterManager.getInstance().pushUIAbility(this)
}

onDestroy(): void {
  FlutterManager.getInstance().popUIAbility(this);
}

onWindowStageCreate(windowStage: window.WindowStage): void {
  FlutterManager.getInstance().pushWindowStage(this, windowStage)
}

onWindowStageDestroy(): void {
  FlutterManager.getInstance().popWindowStage(this);
}
  1. 初始化FlutterBoost

因为FlutterBoost调用usetup后会初始化引擎,建议的初始化optionsBuilder时带上GeneratedPluginRegistrant.getPlugins()

  • 当前UIAbility继承FlutterBoostDelegate
  • 并在适当的时机调用FlutterBoost.getInstance().setup (建议onWindowStageCreate)
export default class EntryAbility extends UIAbility implements FlutterBoostDelegate {
  // FlutterBoostDelegate override
  pushNativeRoute(options: FlutterBoostRouteOptions) {

  }
  // FlutterBoostDelegate override
  pushFlutterRoute(options: FlutterBoostRouteOptions) {

    router.pushUrl({
      url: 'pages/MyFlutterPage', params: {
        uri: options.getPageName(),
        params: options.getArguments(),
      }
    }).then(() => {
      console.info('Succeeded in jumping to the second page.')
    })
  }
  onWindowStageCreate(windowStage: window.WindowStage): void {

    // 初始化启动参数(建议带上GeneratedPluginRegistrant.getPlugins())
    const optionsBuilder: FlutterBoostSetupOptionsBuilder = new FlutterBoostSetupOptionsBuilder()
      .setPlugins(GeneratedPluginRegistrant.getPlugins());

    FlutterBoost.getInstance().setup(this, this.context, () => {
      //引擎初始化成功
    }, optionsBuilder.build())

  }
}

如果想要带Promise的setup函数,请使用FlutterBoost.getInstance().setupSync

  1. 自定义FlutterPage

创建一个Page,加上相关相关FlutterBoostEntry以及FlutterPage代码

@Entry
@Component
struct MyFlutterPage {
  private flutterEntry: FlutterBoostEntry | null = null;
  private flutterView?: FlutterView

  aboutToAppear() {
    this.flutterEntry = new FlutterBoostEntry(getContext(this), router.getParams());
    this.flutterEntry.aboutToAppear();
    this.flutterView = this.flutterEntry.getFlutterView();
    hilog.info(0x0000, "Flutter", "Index aboutToAppear===");
  }
  //2
  aboutToDisappear() {
    hilog.info(0x0000, "Flutter", "Index aboutToDisappear===");
    this.flutterEntry?.aboutToDisappear()
  }

  onPageShow() {
    hilog.info(0x0000, "Flutter", "Index onPageShow===");
    this.flutterEntry?.onPageShow()
  }
  // 1
  onPageHide() {
    hilog.info(0x0000, "Flutter", "Index onPageHide===");
    this.flutterEntry?.onPageHide()
  }

  build() {
    Stack() {
      FlutterPage({ viewId: this.flutterView?.getId() })
    }
  }

  // 拦截返回键
  onBackPress(): boolean | void {
    FlutterBoost.getInstance().getPlugin()?.onBackPressed();
    return true;
  }
}
  1. 跳转Flutter界面 后面直接使用router跳转到3步骤中的page即可
router.pushUrl({
  url: 'pages/MyFlutterPage', params: {
    uri: '这里写dart侧注册好的路由名',
    params: '传递给界面的参数',
  }
}).then(() => {
  console.info('Succeeded in jumping to the second page.')
})

多tab共存

如何在多tab上有多个flutter界面共存。

@Entry
@Component
struct EntryPage {
  @State currentIndex: number = 0;
  private flutterEntries: Array<FlutterBoostEntry> = [];

  build() {
    Column() {
      Tabs({
        index: this.currentIndex,
        barPosition: BarPosition.End
      }) {
        ForEach(bottomTabItems.getTabItems(), (item: TabItem) => {
          TabContent() {
            Column() {
              this.Content()
            }
          }
          .tabBar(this.CardTab(item))
        }, (item: TabItem, index?: number) => index + JSON.stringify(item))
      }
      .vertical(false)
      .barWidth("100%")
      .barHeight("56vp")
      .layoutWeight(1)
      .barMode(BarMode.Fixed)
      .align(Alignment.Center)
      .onChange((index: number) => {
        this.currentIndex = index;

        if (this.currentIndex == 1) {
          this.flutterEntries[0]?.aboutToAppear();
          this.flutterEntries[0]?.onPageShow();
          this.flutterEntries[1]?.onPageHide();
        } else if (this.currentIndex == 2) {
          this.flutterEntries[1]?.aboutToAppear();
          this.flutterEntries[1]?.onPageShow();
          this.flutterEntries[0]?.onPageHide();
        }
      })
    }
    .backgroundColor("#F1F3F5")
  }

  // 组件生命周期
  aboutToAppear() {
    console.info('MyComponent aboutToAppear');

    const firstFlutterEntry = new FlutterBoostEntry(getContext(this), {
      uri: 'firstFirst'
    });

    const secondFlutterEntry = new FlutterBoostEntry(getContext(this), {
      uri: 'flutterPage'
    });

    this.flutterEntries.push(firstFlutterEntry);
    this.flutterEntries.push(secondFlutterEntry);
  }

  // 组件生命周期
  aboutToDisappear() {
    console.info('MyComponent aboutToDisappear');
  }

  // 只有被@Entry装饰的组件才可以调用页面的生命周期
  onPageShow() {
    console.info('Index onPageShow');
  }

  // 只有被@Entry装饰的组件才可以调用页面的生命周期
  onPageHide() {
    console.info('Index onPageHide');
  }

  @Builder
  CardTab(item: TabItem) {
    Column() {
      Image(this.currentIndex === item.index ? item.imageActivated : item.imageOriginal)
        .width("21vp")
        .height("21vp")
        .objectFit(ImageFit.Contain)
        .margin({
          top: "4vp",
          bottom: "5.5vp"
        })
      Text(item.title)
        .fontSize("10fp")
        .fontColor(this.currentIndex === item.index ?
          "#007DFF" : "#66000000")
    }
    .justifyContent(FlexAlign.Center)
    .width("100%")
    .height("100%")
  }

  @Builder
  Content() {
    if (this.currentIndex === 0) {
      Column() {
        Button('打开FlutterEntry')
          .onClick(() => {
            router.pushUrl({ url: 'pages/MyFlutterPage', params: {
              uri: 'flutterPage',
              params: {}
            } }).then(() => {
              console.info('Succeeded in jumping to the second page.')
            })
          })
      }
    } else if (this.currentIndex === 1) {
      FlutterPage({ viewId: this.flutterEntries[0]?.getFlutterView()?.getId() })
    } else if (this.currentIndex === 2) {
      FlutterPage({ viewId: this.flutterEntries[1]?.getFlutterView()?.getId() })
    } else {
      Text("内容" + this.currentIndex)
        .fontSize("20fp")
        .fontColor(Color.Black)
        .fontWeight(500)
    }``
  }

  // 拦截返回键
  onBackPress(): boolean | void {
    FlutterBoost.getInstance().getPlugin()?.onBackPressed();
    return true;
  }
}

到此为止,所有的前置内容均已完成