Flutter笔记--popUntilWithResult

在Flutter开发中,popUntilWithResult经常会用到,它是Flutter框架中用于简化页面导航和结果传递的API,它允许使用者从嵌套的页面栈中直接返回到目标页面,并携带结果数据。简单总结如下:

API:

popUntilWithResult:结合了popUntil和结果传递的能力。它会从当前页面开始,逐级检查页面栈,直到找到匹配的目标页面,然后直接返回到该页面,并携带结果数据。
场景:

1 表单提交后返回上级页面

2 筛选页面返回主列表并更新数据

3 多级设置页面返回并保存配置

栗子:

Dart 复制代码
import 'package:flutter/material.dart';

void main() => runApp(const MyPopTest());
class MyPopTest extends StatelessWidget {
  const MyPopTest({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const HomePage(),
      theme: ThemeData(primarySwatch: Colors.blue),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});
  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  String _result = "暂无数据";
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Home 首页")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              _result,
              style: const TextStyle(fontSize: 22, color: Colors.blue),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (_) => const Page1()),
                ).then((value) {
                  if (value != null) {
                    setState(() {
                      _result = "收到:$value";
                    });
                  }
                });
              },
              child: const Text("跳转到 Page1"),
            ),
          ],
        ),
      ),
    );
  }
}

// Page1
class Page1 extends StatelessWidget {
  const Page1({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Page1")),
      body: Center(
        child: ElevatedButton(
          onPressed: () => Navigator.push(
            context,
            MaterialPageRoute(builder: (_) => const Page2()),
          ),
          child: const Text("跳转到 Page2"),
        ),
      ),
    );
  }
}

// Page2
class Page2 extends StatelessWidget {
  const Page2({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Page2")),
      body: Center(
        child: ElevatedButton(
          onPressed: () => Navigator.push(
            context,
            MaterialPageRoute(builder: (_) => const Page3()),
          ),
          child: const Text("跳转到 Page3"),
        ),
      ),
    );
  }
}

class Page3 extends StatelessWidget {
  const Page3({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Page3")),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.popUntilWithResult(
              context,
                  (route) => route.isFirst, 
              "来自Page3的数据",
            );
          },
          child: const Text(
            "返回首页并传值",
            style: TextStyle(fontSize: 18),
          ),
        ),
      ),
    );
  }
}
Dart 复制代码
import 'package:flutter/material.dart';

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

class MyPopTest01 extends StatelessWidget {
  const MyPopTest01({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/',
      routes: {
        '/': (ctx) => const HomePage(),
        '/list': (ctx) => const ListPage(),
        '/edit': (ctx) => const EditPage(),
      },
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("首页")),
      body: Center(
        child: ElevatedButton(
          onPressed: () => Navigator.pushNamed(context, '/list'),
          child: const Text("进入列表页"),
        ),
      ),
    );
  }
}

class ListPage extends StatefulWidget {
  const ListPage({super.key});
  @override
  State<ListPage> createState() => _ListPageState();
}

class _ListPageState extends State<ListPage> {
  String info = "等待编辑结果...";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("列表页")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(info, style: const TextStyle(fontSize: 18, color: Colors.green)),
            ElevatedButton(
              onPressed: () {
                Navigator.pushNamed(context, '/edit').then((value) {
                  if (value != null) {
                    setState(() {
                      info = "更新:${value.toString()}";
                    });
                  }
                });
              },
              child: const Text("去编辑页"),
            ),
          ],
        ),
      ),
    );
  }
}

class EditPage extends StatelessWidget {
  const EditPage({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("编辑页")),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.popUntilWithResult(
              context,
                  (route) => route.settings.name == '/list',
              {
                "id": 1001,
                "title": "已完成编辑",
                "time": DateTime.now().toString(),
              },
            );
          },
          child: const Text("保存并返回列表页"),
        ),
      ),
    );
  }
}

注意:

1 版本兼容性:确保使用 Flutter 3.41 或更高版本。

2 目标页面唯一性:确保页面栈中只有一个匹配的目标页面,否则可能返回错误的页面。

3 结果类型安全:传递结果时建议使用 Map<String, dynamic> 或自定义模型类,并在目标页面进行类型检查。

4 异步处理:如果目标页面需要异步处理结果,尽量在initState或didChangeDependencies中处理。

源码:

Dart 复制代码
@optionalTypeArgs
  void popUntilWithResult<T extends Object?>(RoutePredicate predicate, T? result) {
    _RouteEntry? candidate = _lastRouteEntryWhereOrNull(_RouteEntry.isPresentPredicate);

    while (candidate != null) {
      if (predicate(candidate.route)) {
        return;
      }
      // Check what would be next if we pop this route.
      final _RouteEntry? next = _lastRouteEntryWhereOrNull(
        (_RouteEntry e) => _RouteEntry.isPresentPredicate(e) && e != candidate,
      );

      if (next != null && !next.route.willHandlePopInternally && predicate(next.route)) {
        pop<T>(result);
      } else {
        pop();
      }

      candidate = _lastRouteEntryWhereOrNull(_RouteEntry.isPresentPredicate);
    }
  }

分析:

Dart 复制代码
<T extends Object?>:泛型类型 T,表示返回结果的类型;
RoutePredicate predicate:一个回调函数,用于判断当前路由是否为目标路由
T? result:需要传递到目标路由的结果数据

流程:

1 初始化候选路由;

2 循环检查路由栈;

3 查找下一个候选路由;

4 决定是否带结果返回;

5 更新候选路由;

相关推荐
夜瞬21 小时前
NLP学习笔记01:文本预处理详解——从清洗、分词到词性标注
笔记·学习·自然语言处理
中屹指纹浏览器1 天前
指纹浏览器内核级渲染伪造技术:Canvas/WebGL/AudioContext深度伪造与检测绕过实战
经验分享·笔记
懂懂tty1 天前
React状态更新流程
前端·react.js
Utopia^1 天前
鸿蒙flutter第三方库适配 - 联系人备份工具
flutter·华为·harmonyos
-Springer-1 天前
STM32 学习 —— 个人学习笔记11-1(SPI 通信协议及 W25Q64 简介 & 软件 SPI 读写 W25Q64)
笔记·stm32·学习
LN花开富贵1 天前
【ROS】鱼香ROS2学习笔记一
linux·笔记·python·学习·嵌入式·ros·agv
小码哥_常1 天前
告别繁琐!手把手教你封装超实用Android原生Adapter基类
前端
skywalk81631 天前
pytest测试的时候这是什么意思?Migrating <class ‘kotti.resources.File‘>
前端·python
一只蝉nahc1 天前
vue使用iframe内嵌unity模型,并且向模型传递信息,接受信息
前端·vue.js·unity
子兮曰1 天前
Bun v1.3.12 深度解析:新特性、性能优化与实战指南
前端·typescript·bun