如何解决页面跳转后弹窗不关闭
==============

#### 问题现象

在页面中的自定义弹窗中点击按钮进行页面跳转，跳转完成后，自定义弹窗不关闭，如下图所示：

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/10/v3/4TwjAuCcR-eC8giHrXgZnw/zh-cn_image_0000002658972589.png?HW-CC-KV=V1&HW-CC-Date=20260709T134044Z&HW-CC-Expire=86400&HW-CC-Sign=874EEF88132DB313840C418C7B971893EBB5D4FF6E6F094CB1F6E293E4189D1D "点击放大")

主页代码如下所示：

import { BusinessError } from '@kit.BasicServicesKit';

@CustomDialog
struct CustomDialogExample {
  controller?: CustomDialogController;

  build() {
    Column({ space: 5 }) {
      Text('这是自定义弹窗')
        .fontSize(30)
        .height(100);
      Button('跳转到其他页面').onClick(() => {
        this.getUIContext().getRouter()
          .pushUrl({
            url: 'pages/Page',
          })
          .then(() => {
            console.info('succeeded');
          })
          .catch((error: BusinessError) => {
            console.error(\`pushUrl failed, code is ${error.code}, message is ${error.message}\`);
          });
      });
    };
  }
}

@Entry
@Component
struct Index {
  dialogController: CustomDialogController | null = new CustomDialogController({
    builder: CustomDialogExample(),
    autoCancel: true,
    alignment: DialogAlignment.Center,
    customStyle: false,
    width: 300,
    height: 200,
    backgroundColor: Color.White,
  });

  aboutToDisappear() {
    this.dialogController = null;
  }

  build() {
    Column() {
      Button('click me')
        .onClick(() => {
          this.dialogController?.open();
        }).backgroundColor('#0A59F7');
    }
    .width('100%')
    .margin({ top: 5 });
  }
}

#### 背景知识

*   [router](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-router)提供通过不同的url访问不同的页面的功能，包括跳转到应用内的指定页面、同应用内的某个页面替换当前页面、返回上一页面或指定的页面等。
*   [Navigation](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-navigation-navigation)主要用于实现页面间以及组件内部的页面跳转，支持在不同组件间传递跳转参数，提供灵活的跳转栈操作，从而更便捷地实现对不同页面的访问和复用。
*   [CustomDialog](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-methods-custom-dialog-box)是自定义弹出框，可用于广告、中奖、警告、软件更新等与用户交互响应操作。开发者可以通过CustomDialogController类显示自定义弹出框。
*   [页面级弹出框](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-embedded-dialog)能够在路由跳转后，弹出框随着前一个路由页面的切换而消失，并且路由返回后弹出框还能够继续正常显示。

#### 问题定位

ArkUI的[弹出框](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-embedded-dialog)默认设置为全局级别，弹窗节点作为页面根节点的子节点，显示层级高于应用中的所有路由/导航页面。当页面内进行路由跳转时，如果应用未主动调用close方法关闭弹出框，弹出框不会自动关闭，并且会在下一个跳转页面上继续显示

#### 分析结论

在页面跳转时未给跳转按钮添加关闭弹窗的方法，也未尝试使用页面级弹出框。

#### 修改建议

*   **方案一**：在弹窗跳转之前调用[close](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-methods-custom-dialog-box#close)方法关闭弹窗。
    *   使用router跳转页面：创建CustomDialogController自定义弹窗控制器。在[pushUrl](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-uicontext-router#pushurl)方法之前使用controller的close方法，关闭页面弹窗。修改代码如下：
        
        import { BusinessError } from '@kit.BasicServicesKit';
        
        @CustomDialog
        struct CustomDialogExample {
          controller?: CustomDialogController;
        
          build() {
            Column({ space: 5 }) {
              Text('这是自定义弹窗')
                .fontSize(30)
                .height(100);
              Button('跳转到其他页面').onClick(() => {
                this.controller?.close();
                this.getUIContext()
                  .getRouter()
                  .pushUrl({
                    url: 'pages/Page',
                  })
                  .then(() => {
                    console.info('succeeded');
                  })
                  .catch((error: BusinessError) => {
                    console.error(\`pushUrl failed, code is ${error.code}, message is ${error.message}\`);
                  });
              });
            };
          }
        }
        
        @Entry
        @Component
        struct PageNavigationOne {
          dialogController: CustomDialogController | null = new CustomDialogController({
            builder: CustomDialogExample(),
            autoCancel: true,
            alignment: DialogAlignment.Center,
            customStyle: false,
            width: 300,
            height: 200,
            backgroundColor: Color.White,
          });
        
          aboutToDisappear() {
            this.dialogController = null;
          }
        
          build() {
            Column() {
              Button('click me')
                .onClick(() => {
                  this.dialogController?.open();
                }).backgroundColor('#0A59F7');
            }
            .width('100%')
            .margin({ top: 5 });
          }
        }
        
        修改后的效果如下：
        
        ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/74/v3/K6Ic7sIURHypit5NDZ_Zrw/zh-cn_image_0000002658852637.png?HW-CC-KV=V1&HW-CC-Date=20260709T134044Z&HW-CC-Expire=86400&HW-CC-Sign=2081D119982C64CC957893581708DABF36539A49B4C5CB3B97A431A283F53E02 "点击放大")
        
    *   在页面的生命周期回调函数[onPageHide](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-custom-component-lifecycle#onpagehide)中添加关闭弹框逻辑可以实现弹窗自动关闭。
        
        import { BusinessError } from '@kit.BasicServicesKit';
        
        @CustomDialog
        struct CustomDialogExample {
          controller: CustomDialogController = new CustomDialogController({
            builder: CustomDialogExample({}),
          });
        
          build() {
            Column() {
              Text('这是自定义弹窗')
                .fontSize(30)
                .height(100);
              Button('跳转到其他页面').onClick(() => {
                this.getUIContext()
                  .getRouter()
                  .pushUrl({
                    url: 'pages/Page',
                  })
                  .then(() => {
                    console.info('succeeded');
                  })
                  .catch((error: BusinessError) => {
                    console.error(\`pushUrl failed, code is ${error.code}, message is ${error.message}\`);
                  });
              });
            };
          }
        }
        
        @Entry
        @Component
        struct PageNavigationTwo {
          dialogController: CustomDialogController = new CustomDialogController({
            builder: CustomDialogExample(),
            autoCancel: true,
            alignment: DialogAlignment.Center,
            customStyle: false,
            width: 300,
            height: 200,
            backgroundColor: Color.White,
          });
        
          build() {
            Column() {
              Button('click me')
                .onClick(() => {
                  this.dialogController.open();
                })
                .backgroundColor('#0A59F7');
            }.width('100%').margin({ top: 5 });
          }
        
         _ // 页面隐藏时关闭弹窗_
          onPageHide(): void {
            this.dialogController?.close();
          }
        }
        
    *   使用Navigation跳转页面：创建CustomDialogController自定义弹窗控制器。在[pushPathByName](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-navigation#pushpathbyname10)之前使用controller的close方法，关闭页面弹窗。使用Navigation时需注意，将主页面而不是弹窗界面压栈。代码示例如下：
        
        主页面：
        
        @Builder
        export function MainPageBuilder() {
          PageNavigationThree();
        }
        
        @CustomDialog
        struct CustomDialogExample {
          @Consume pageInfos: NavPathStack;
          controller?: CustomDialogController;
        
          build() {
            Column({ space: 5 }) {
              Text('这是自定义弹窗')
                .fontSize(30)
                .height(100);
              Button('跳转到其他页面').onClick(() => {
                this.controller?.close();
                this.pageInfos.pushPathByName('Page', null);
              });
            };
          }
        }
        
        @Entry
        @Component
        struct PageNavigationThree {
          @Provide('pageInfos') pageInfos: NavPathStack = new NavPathStack();
          dialogController: CustomDialogController | null = new CustomDialogController({
            builder: CustomDialogExample(),
            autoCancel: true,
            alignment: DialogAlignment.Center,
            customStyle: false,
            width: 300,
            height: 200,
            backgroundColor: Color.White,
          });
        
          aboutToDisappear() {
            this.dialogController = null;
          }
        
          build() {
            Navigation(this.pageInfos) {
              Column() {
                Button('click me')
                  .onClick(() => {
                    this.dialogController?.open();
                  }).backgroundColor('#0A59F7');
              }
              .width('100%')
              .margin({ top: 5 });
            };
          }
        }
        
        目标跳转页面：
        
        @Builder
        export function MainPageBuilder() {
          Page();
        }
        
        @Entry
        @Component
        struct Page {
          @State message: string = 'Hello World';
          pageInfos: NavPathStack = new NavPathStack();
        
          build() {
            NavDestination() {
              RelativeContainer() {
                Text(this.message)
                  .id('PageHelloWorld')
                  .fontSize($r('app.float.page\_text\_font\_size'))
                  .fontWeight(FontWeight.Bold)
                  .alignRules({
                    center: { anchor: '\_\_container\_\_', align: VerticalAlign.Center },
                    middle: { anchor: '\_\_container\_\_', align: HorizontalAlign.Center }
                  })
                  .onClick(() => {
                    this.message = 'Welcome';
                  });
              }
              .height('100%')
              .width('100%');
            };
          }
        }
        
        在src/main目录下的module.json5配置文件中的module字段里配置"routerMap": "$profile:router\_map"，并在src/main/resources/base/profile目录下新增router\_map.json。router\_map.json示例如下：
        
        {
          "routerMap": \[
            {
              "name": "Page",
              "pageSourceFile": "src/main/ets/pages/Page.ets",
              "buildFunction": "MainPageBuilder",
              "data": {
                "description": "This is secondPage"
              }
            }
          \]
        }
        
        效果如下：
        
        ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/c5/v3/5PzbPgFySu-QXtZfnQyKRg/zh-cn_image_0000002628773274.png?HW-CC-KV=V1&HW-CC-Date=20260709T134044Z&HW-CC-Expire=86400&HW-CC-Sign=453FD832B355D9D3564A9A147CA50E0A5452742ADC197E9A6C0062B57E5C7AF6 "点击放大")
        
*   **方****案二**：使用页面级弹出框。在弹出框的options入参中设置levelMode属性，值为LevelMode.EMBEDDED表示开启页面级弹出框能力，该方法仅支持API15及以上版本。目标跳转页面同方案一，主页面示例代码为：
    
    import { BusinessError } from '@kit.BasicServicesKit';
    import { ImmersiveMode, LevelMode } from '@kit.ArkUI';
    
    @CustomDialog
    struct CustomDialogExample {
      controller?: CustomDialogController;
    
      build() {
        Column({ space: 5 }) {
          Text('这是自定义弹窗')
            .fontSize(30)
            .height(100);
          Button('跳转到其他页面').onClick(() => {
            this.getUIContext()
              .getRouter()
              .pushUrl({
                url: 'pages/Page',
              })
              .then(() => {
                console.info('succeeded');
              })
              .catch((error: BusinessError) => {
                console.error(\`pushUrl failed, code is ${error.code}, message is ${error.message}\`);
              });
          });
        };
      }
    }
    
    @Entry
    @Component
    struct PageNavigationFour {
      dialogController: CustomDialogController | null = new CustomDialogController({
        builder: CustomDialogExample(),
        autoCancel: true,
        alignment: DialogAlignment.Center,
        customStyle: false,
        width: 300,
        height: 200,
        backgroundColor: Color.White,
        levelMode: LevelMode.EMBEDDED, _// 启用页面级弹出框_
        immersiveMode: ImmersiveMode.EXTEND, _// 设置页面级弹出框蒙层的显示模式_
      });
    
      aboutToDisappear() {
        this.dialogController = null;
      }
    
      build() {
        Column() {
          Button('click me')
            .onClick(() => {
              this.dialogController?.open();
            }).backgroundColor('#0A59F7');
        }
        .width('100%')
        .margin({ top: 5 });
      }
    }
    
    效果如下：
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/99/v3/Awlu4y71THmULTkBjQ2hNQ/zh-cn_image_0000002628613376.png?HW-CC-KV=V1&HW-CC-Date=20260709T134044Z&HW-CC-Expire=86400&HW-CC-Sign=4C0ADECD1E32BC60E9768A143C4E3469F485DCD9627FB1CCC01DA44E765359EC "点击放大")
    

#### 常见FAQ

Q：设置levelMode属性后，页面级弹出框未生效，可能是什么原因导致的？

A：当且仅当弹出框为非子窗模式时，页面级能力才会生效，即showInSubWindow参数不设置或设置为false。