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

Flutter系统返回键失效:如何实现返回上一页而非退出应用?

问题:系统返回键无效,导航栏返回正常,BackButtonInterceptor与PopScope均失效

我代码中的BackButtonInterceptor无法正常工作,尝试过PopScope也没解决问题。我不想退出应用,只想返回上一页,请问需要做什么?比如是否需要配置父路由?导航栏的返回图标可以正常工作,但系统返回键完全没效果。

相关代码

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

  @override
  _ChecklistState createState() => _ChecklistState();
}

class _ChecklistState extends State<Checklist> {
  @override
  void initState() {
    super.initState();
    
    Provider.of<checkListProvider>(context, listen: false).loadData();
    
    BackButtonInterceptor.add(_onBackPressed);
  }

  @override
  void dispose() {
    
    BackButtonInterceptor.remove(_onBackPressed);
    super.dispose();
  }

  bool _onBackPressed(bool stopDefaultButtonEvent, RouteInfo info) {
    
    _showExitDialog();
    return true; 
  }

  Future<void> _showExitDialog() async {
    final shouldPop = await showDialog<bool>(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Confirm Exit'),
        content: const Text('Do you want to go back to the previous screen?'),
        actions: [
          TextButton(
            onPressed: () => Navigator.of(context).pop(false),
            child: const Text('Cancel'),
          ),
          TextButton(
            onPressed: () {
              Navigator.of(context).pop(true);
              GoRouter.of(context).pop(); // Go back to the previous route
            },
            child: const Text('Yes'),
          ),
        ],
      ),
    );

    if (shouldPop ?? false) {
      GoRouter.of(context).pop();
    }
  }

  @override
  Widget build(BuildContext context) {
    final pageState = Provider.of<PageState>(context);
    final titleProvider = Provider.of<PageTitle>(context);
    final title =
        titleProvider.title.isNotEmpty ? titleProvider.title : 'Default Title';

    final checkProvider = Provider.of<checkListProvider>(context);

    return Scaffold(
      backgroundColor: Colors.teal[50],
      appBar: AppBar(
        backgroundColor: Colors.teal[50],
        title: Center(child: Text(title)),
        leading: IconButton(
          icon: const Icon(Icons.chevron_left_outlined),
          onPressed: () {
            pageState.setRoute('/help-center');
            GoRouter.of(context).pop('/help-center');
          },
        ),
      ),
      body: checkProvider.isLoading
          ? const Center(child: CircularProgressIndicator())
          : checkProvider.errorMessage.isNotEmpty
              ? Center(child: Text(checkProvider.errorMessage))
              : ListView(
                  padding: const EdgeInsets.all(10),
                  children: [
                    ExpansionTile(
                      title: const Text('Completed'),
                      children: checkProvider.completed
                          .map((item) => ListTile(
                                title: Text(item['title']),
                                subtitle: Text(item['description']),
                              ))
                          .toList(),
                    ),
                    ExpansionTile(
                      title: const Text('Next'),
                      children: checkProvider.next
                          .map(
                            (item) => CheckboxListTile(
                              checkColor: Colors.teal[500],
                              checkboxShape: const CircleBorder(),
                              title: Text(item['title']),
                              subtitle: Text(item['description']),
                              value: item['completed'],
                              onChanged: (val) {
                                if (val == true) {
                                  checkProvider.toogleComplete(item, 'next');
                                }
                              },
                            ),
                          )
                          .toList(),
                    ),
                    ExpansionTile(
                      title: const Text('Upcoming'),
                      children: checkProvider.upcoming
                          .map(
                            (item) => CheckboxListTile(
                              checkColor: Colors.teal[500],
                              checkboxShape: const CircleBorder(),
                              title: Text(item['title']),
                              subtitle: Text(item['description']),
                              value: item['completed'],
                              onChanged: (val) {
                                if (val == true) {
                                  checkProvider.toogleComplete(
                                      item, 'upcoming');
                                }
                              },
                            ),
                          )
                          .toList(),
                    ),
                  ],
                ),
    );
  }
}

解决方案

核心原因

你使用了GoRouter管理路由,而BackButtonInterceptor是针对原生Navigator的拦截逻辑,GoRouter会接管系统返回键的处理,导致原生拦截器失效。PopScope需要正确配合GoRouter的路由栈逻辑才能生效。

具体修改步骤

  1. 移除BackButtonInterceptor相关代码:删除initState和dispose中拦截器的注册、移除代码。
  2. 用PopScope包裹Scaffold:在build方法中,将Scaffold嵌套进PopScope组件,自定义返回行为。
  3. 修复对话框中的重复pop问题:当前代码中Yes按钮和后续判断会执行两次GoRouter.of(context).pop(),导致路由栈异常。

修改后的代码片段

@override
Widget build(BuildContext context) {
  final pageState = Provider.of<PageState>(context);
  final titleProvider = Provider.of<PageTitle>(context);
  final title =
      titleProvider.title.isNotEmpty ? titleProvider.title : 'Default Title';

  final checkProvider = Provider.of<checkListProvider>(context);

  return PopScope(
    canPop: false, // 阻止默认返回行为
    onPopInvoked: (didPop) async {
      if (!didPop) {
        await _showExitDialog();
      }
    },
    child: Scaffold(
      // 原Scaffold内容保持不变
      backgroundColor: Colors.teal[50],
      appBar: AppBar(
        backgroundColor: Colors.teal[50],
        title: Center(child: Text(title)),
        leading: IconButton(
          icon: const Icon(Icons.chevron_left_outlined),
          onPressed: () {
            pageState.setRoute('/help-center');
            GoRouter.of(context).pop();
          },
        ),
      ),
      body: checkProvider.isLoading
          ? const Center(child: CircularProgressIndicator())
          : checkProvider.errorMessage.isNotEmpty
              ? Center(child: Text(checkProvider.errorMessage))
              : ListView(
                  padding: const EdgeInsets.all(10),
                  children: [
                    ExpansionTile(
                      title: const Text('Completed'),
                      children: checkProvider.completed
                          .map((item) => ListTile(
                                title: Text(item['title']),
                                subtitle: Text(item['description']),
                              ))
                          .toList(),
                    ),
                    ExpansionTile(
                      title: const Text('Next'),
                      children: checkProvider.next
                          .map(
                            (item) => CheckboxListTile(
                              checkColor: Colors.teal[500],
                              checkboxShape: const CircleBorder(),
                              title: Text(item['title']),
                              subtitle: Text(item['description']),
                              value: item['completed'],
                              onChanged: (val) {
                                if (val == true) {
                                  checkProvider.toogleComplete(item, 'next');
                                }
                              },
                            ),
                          )
                          .toList(),
                    ),
                    ExpansionTile(
                      title: const Text('Upcoming'),
                      children: checkProvider.upcoming
                          .map(
                            (item) => CheckboxListTile(
                              checkColor: Colors.teal[500],
                              checkboxShape: const CircleBorder(),
                              title: Text(item['title']),
                              subtitle: Text(item['description']),
                              value: item['completed'],
                              onChanged: (val) {
                                if (val == true) {
                                  checkProvider.toogleComplete(
                                      item, 'upcoming');
                                }
                              },
                            ),
                          )
                          .toList(),
                    ),
                  ],
                ),
    ),
  );
}

// 修复后的_showExitDialog方法
Future<void> _showExitDialog() async {
  final shouldPop = await showDialog<bool>(
    context: context,
    builder: (context) => AlertDialog(
      title: const Text('Confirm Exit'),
      content: const Text('Do you want to go back to the previous screen?'),
      actions: [
        TextButton(
          onPressed: () => Navigator.of(context).pop(false),
          child: const Text('Cancel'),
        ),
        TextButton(
          onPressed: () {
            Navigator.of(context).pop(true);
          },
          child: const Text('Yes'),
        ),
      ],
    ),
  );

  if (shouldPop ?? false) {
    pageState.setRoute('/help-center');
    GoRouter.of(context).pop();
  }
}

额外检查项

  • 确认当前页面是通过GoRouter.push()进入的,而非GoRouter.replace()或作为初始路由,确保路由栈中存在可返回的上一页。
  • 检查GoRouter的路由配置,确保/help-center是当前页面的父路由,或路由栈结构正确。

内容的提问来源于stack exchange,提问作者Ikram Ico

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 19:37:16