You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter PopScope组件异常:返回键触发后出现白屏而非确认弹窗

Flutter PopScope触发系统返回手势时白屏问题排查与解决

问题场景

使用PopScope实现Android返回键/滑动返回手势触发离开确认弹窗,但实际操作时:

  • 点击页面内的"Page Two"按钮,能正常弹出确认对话框
  • 触发系统返回键或滑动返回手势时,页面出现白屏,无法正常显示弹窗

完整复现代码

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/home',
      onGenerateRoute: (RouteSettings settings) {
        return switch (settings.name) {
          '/two' => MaterialPageRoute(
              builder: (BuildContext context) => const PageTwo(),
            ),
          _ => MaterialPageRoute(
              builder: (BuildContext context) => const PageOne(),
            ),
        };
      },
    );
  }
}

class PageOne extends StatelessWidget {
  const PageOne({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: TextButton(
          child: const Text('Page One'),
          onPressed: () {
            Navigator.of(context).pushNamed('/two');
          },
        ),
      ),
    );
  }
}

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

  @override
  State<PageTwo> createState() => _PageTwoState();
}

class _PageTwoState extends State<PageTwo> {
  Future<bool?> _showBackDialog(BuildContext context) {
    return showDialog<bool>(
      context: context,
      builder: (BuildContext context) => AlertDialog(
        title: const Text('Are you sure?'),
        content: const Text(
          'Are you sure you want to leave this page?',
        ),
        actions: <Widget>[
          TextButton(
            child: const Text('Never Mind'),
            onPressed: () {
              Navigator.pop(context, false);
            },
          ),
          TextButton(
            child: const Text('leave'),
            onPressed: () {
              Navigator.pop(context, true);
            },
          )
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: PopScope(
          canPop: false,
          onPopInvoked: (bool didPop) async {
            if (didPop) {
              return;
            }
            final bool shouldPop = await _showBackDialog(context) ?? false;
            if (context.mounted && shouldPop) {
              Navigator.pop(context);
            }
          },
          child: TextButton(
            child: const Text('Page Two'),
            onPressed: () async {
              final bool shouldPop = await _showBackDialog(context) ?? false;
              if (context.mounted && shouldPop) {
                Navigator.pop(context);
              }
            },
          ),
        ),
      ),
    );
  }
}

问题原因

  1. 上下文层级冲突:原代码中PopScope被嵌套在Scaffold的body内部,它的上下文并非当前路由的根上下文。当系统返回手势触发时,onPopInvoked回调使用当前页面的上下文弹出对话框,此时导航栈正处于处理返回操作的状态,上下文层级冲突导致对话框无法正确挂载,最终出现白屏。
  2. 导航状态混乱:canPop: false阻止了系统默认的路由弹出,但在回调中使用非根上下文处理弹窗和路由弹出,会导致导航栈状态异常,进而引发页面渲染问题。

解决方案

方案一:调整PopScope的层级

将PopScope移到Scaffold的外层,使其成为当前路由的直接子Widget,确保它使用路由的根上下文处理返回事件:

@override
Widget build(BuildContext context) {
  return PopScope(
    canPop: false,
    onPopInvoked: (bool didPop) async {
      if (didPop) {
        return;
      }
      final bool shouldPop = await _showBackDialog(context) ?? false;
      if (context.mounted && shouldPop) {
        Navigator.pop(context);
      }
    },
    child: Scaffold(
      body: Center(
        child: TextButton(
          child: const Text('Page Two'),
          onPressed: () async {
            final bool shouldPop = await _showBackDialog(context) ?? false;
            if (context.mounted && shouldPop) {
              Navigator.pop(context);
            }
          },
        ),
      ),
    ),
  );
}

方案二:使用根导航器弹出对话框

在showDialog中设置useRootNavigator: true,确保对话框挂载在根导航器上,避免上下文冲突:

Future<bool?> _showBackDialog(BuildContext context) {
  return showDialog<bool>(
    context: context,
    useRootNavigator: true, // 新增此行
    builder: (BuildContext context) => AlertDialog(
      title: const Text('Are you sure?'),
      content: const Text(
        'Are you sure you want to leave this page?',
      ),
      actions: <Widget>[
        TextButton(
          child: const Text('Never Mind'),
          onPressed: () {
            Navigator.pop(context, false);
          },
        ),
        TextButton(
          child: const Text('leave'),
          onPressed: () {
            Navigator.pop(context, true);
          },
        )
      ],
    ),
  );
}

两种方案都能解决白屏问题,推荐方案一,因为它更符合PopScope的使用规范,让返回事件的处理层级更清晰。

内容的提问来源于stack exchange,提问作者DData Science

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 17:53:21