flutter:PopScope返回拦截监听组件

  1. PopScope 是什么?
  2. 参数
  3. 入门案例
    1. 代码
    2. 解释
      1. 情况一:canPop = false
      2. 情况二:canPop = true

PopScope 是什么?

PopScope 是 Flutter 提供的一个 路由返回(pop)拦截和监听组件。当用户尝试返回页面(比如按下返回键、手势返回、调用 Navigator.pop()),PopScope 可以:

  • 控制:是否允许返回
  • 监听:返回事件是否真正发生

参数

const PopScope({
  super.key,
  required this.child,
  this.canPop = true,
  this.onPopInvokedWithResult,
  @Deprecated(
    'Use onPopInvokedWithResult instead. '
    'This feature was deprecated after v3.22.0-12.0.pre.',
  )
  this.onPopInvoked,
}) : assert(
       onPopInvokedWithResult == null || onPopInvoked == null,
       'onPopInvoked is deprecated, use onPopInvokedWithResult',
     );
  • child

    • 类型:Widget

    • 说明:这是 PopScope 包裹的子组件,也就是你真正要展示的页面内容。

  • canPop

    • 类型:bool

    • 说明:是否允许 自动返回

    • canPop = true:用户按返回键 / 手势返回 / 调用 Navigator.pop() → 页面会 直接返回。这时 onPopInvokedWithResult(didPop: true, result: xxx) 会被触发,告诉你页面已经返回了。

    • canPop = false:用户按返回键 / 手势返回 / 调用 Navigator.pop()不会自动退出。而是先触发 onPopInvokedWithResult(didPop: false, result: null)。在这个回调里可以:

      • 弹出确认框(是否真的要退出)
      • 保存数据后再退出
      • 或者干脆什么都不做,阻止退出

      如果你决定真的退出 → 需要手动调用

      Navigator.of(context).pop("自定义的返回值");
      

      手动退出后,onPopInvokedWithResult(didPop: true, result: "自定义的返回值") 会再次触发。

  • onPopInvokedWithResult

    • 类型:void Function(bool didPop, Object? result)
    • 说明:当页面尝试返回时调用(推荐使用这个)。
      • didPop → 表示是否真的返回了
        • true:已经返回成功
        • false:返回被拦截了
      • result → 如果返回时 Navigator.pop(result) 带了值,这里可以接收到。
  • onPopInvoked(已过时)

  • 类型:void Function(bool didPop)

  • 说明:和 onPopInvokedWithResult 类似,但 没有 result 参数,因此无法知道 Navigator.pop(result) 传出的值。

入门案例

代码

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: FirstPage());
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("First Page")),
      body: Center(
        child: ElevatedButton(
          onPressed: () async {
            final result = await Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const SecondPage()),
            );
            debugPrint("从 SecondPage 返回的结果: $result");
          },
          child: const Text("跳转到 SecondPage"),
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return PopScope(
      canPop: false, // 禁止自动返回
      onPopInvokedWithResult: (didPop, result) {
        if (didPop) {
          debugPrint("页面已返回,返回值: $result");
        } else {
          debugPrint("返回被拦截,可以做处理逻辑");
          // 手动退出并带数据
          Navigator.of(context).pop("从 SecondPage 自定义的返回值");
        }
      },
      child: Scaffold(
        appBar: AppBar(title: const Text("Second Page")),
        body: const Center(child: Text("按返回键或手势,触发 PopScope")),
      ),
    );
  }
}

日志如下:

I/flutter ( 8164): 返回被拦截,可以做处理逻辑
I/flutter ( 8164): 页面已返回,返回值: 从 SecondPage 自定义的返回值
I/flutter ( 8164): 从 SecondPage 返回的结果: 从 SecondPage 自定义的返回值

解释

情况一:canPop = false

  1. 用户点击返回键 / 手势返回
  2. 系统发现 canPop = false → 不会自动退出
  3. 触发 onPopInvokedWithResult(didPop: false, result: null)
    • 这里的 didPop = false 表示 页面没有退出
  4. 如果你在这里调用了 Navigator.of(context).pop("返回值")
    • 页面真正退出
    • 再次触发 onPopInvokedWithResult(didPop: true, result: "返回值")

👉 所以 会触发两次回调:

  • 第一次:拦截(didPop = false
  • 第二次:真正退出(didPop = true

情况二:canPop = true

  1. 用户点击返回键 / 手势返回
  2. 系统允许返回,页面立即退出
  3. 触发 onPopInvokedWithResult(didPop: true, result: null)
    • 这里 didPop = true,因为页面已经退出
    • 如果 Navigator.pop("值"),那么 result 就是这个值

👉 所以 只会触发一次回调(直接返回成功)。

×

喜欢就点赞,疼爱就打赏