前言
虽然大部分场景下,Flutter可以通过Channel等与原生进行数据交互,但也存在一些情况需要自己写个原生的UI以使用部分原生的控件。
1. 在原有Index.ets包裹Navigation
官方目前主推的是Navigation,其好处就是会自带一些鸿蒙的UI,如AppBar等。因此我们需要Flutter的宿主UI下外面嵌套一个Navigation
。实现代码如下:
ts
build() {
Navigation(pageInfos) {
Column() {
FlutterPage({ viewId: this.viewId })
}
}.hideToolBar(true) \\取消底部导航栏,否则会有一大片的空白
}
这个pageInfo
则是通过一个ets
文件导出的,文件的内容如下:
ts
const pageInfos: NavPathStack = new NavPathStack();
export default pageInfos
2. 写一个原生UI
新开一个ets
文件写UI
ts
@Builder
export function PageOneBuilder() {
PageOne()
}
@Component
struct PageOne {
@StorageProp('bottomRectHeight') bottomRectHeight: number = 0;
@StorageProp('topRectHeight') topRectHeight: number = 0;
pathStack: NavPathStack = new NavPathStack();
build() {
NavDestination() {
// 你的内容
}
.title('原生UI')
.padding({ top: this.topRectHeight, left: 8, right: 8 })
.onReady((context: NavDestinationContext) => {
this.pathStack = context.pathStack;
})
}
}
但写完UI还不行,你还得告诉鸿蒙UI在哪里,因此你需要在resource/base/profile
下弄一个配置文件route_map.json
。配置如下内容:
json
{
"routerMap": [
{
"name": "PageOne",
"pageSourceFile": "src/main/ets/pages/PageOne.ets",
"buildFunction": "PageOneBuilder",
"data": {
"description": "this is pageOne"
}
}
]
}
最后在module.json5
中的module
加入一个key-value:
json
"routerMap": "$profile:route_map"
3. Flutter 跳转到原生
ts
openPreviewPage(){
pageInfos.pushPathByName('PageOne', null); // 这里就写struct的名字
}
async onMethodCall(call: MethodCall, result: MethodResult): Promise<void> {
switch (call.method) {
case "jumpToArkTs":
this.openPreviewPage()
result.success(null);
break;
default:
result.notImplemented();
}
}
完成以上代码后,就在Flutter端使用Channel就可以实现原生界面的跳转啦。
4. 坑
然而,你会发现这个原生界面AppBar与状态栏重合了。我们的上面的代码设置了Padding来解决这个问题。但怎么获取这个准确的状态栏高度呢?
在EntryAbility.ets
下增加下面这个函数,就可以获取到准确的状态栏高度啦。
ts
onWindowStageCreate(windowStage: window.WindowStage): void {
super.onWindowStageCreate(windowStage)
let windowClass: window.Window = windowStage.getMainWindowSync(); // 获取应用主窗口
let type = window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR; // 此处以导航条避让为例
let avoidArea = windowClass.getWindowAvoidArea(type);
let bottomRectHeight = avoidArea.bottomRect.height; // 获取到导航区域的高度
AppStorage.setOrCreate('bottomRectHeight', bottomRectHeight);
type = window.AvoidAreaType.TYPE_SYSTEM; // 以状态栏避让为例
avoidArea = windowClass.getWindowAvoidArea(type);
let topRectHeight = avoidArea.topRect.height; // 获取状态栏区域高度
console.debug("top" + topRectHeight);
display.getDefaultDisplaySync().getCutoutInfo().then((data) => {
data.boundingRects.forEach((rect) => {
AppStorage.setOrCreate('topRectHeight', rect.top);
console.debug("rect = " + JSON.stringify(rect));
})
})
}
以上便是所有关于Flutter 跳转鸿蒙原生界面的知识,若有更好的方案,欢迎大家在评论区与我讨论,一起进度!