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 更新候选路由;

相关推荐
陆枫Larry3 小时前
小程序包体积优化:用 SVG 图片替换 iconfont,并保留 CSS 控色能力
前端
心中有国也有家3 小时前
AtomGit Flutter 鸿蒙客户端:呼吸练习的完整生命周期
学习·flutter·华为·harmonyos
环境栈笔记4 小时前
多账号浏览器选型检查清单:Profile、权限、Session 和任务日志怎么评估
前端·人工智能·后端·自动化
前端炒粉4 小时前
手写promise
java·前端·javascript
LaughingZhu4 小时前
智能体经典范式构建:ReAct、Plan-and-Solve 与 Reflection
前端·react.js·前端框架
Piko6144 小时前
锐捷交换机 DHCP Server部署
运维·网络·笔记
jsonbro4 小时前
手把手带你实现发布订阅模式
前端·vue.js·设计模式
一只猫的梦4 小时前
自动化模块 (Automation Module)
前端·chrome
熊猫钓鱼>_>5 小时前
Electron:当 Web 技术统治桌面
大数据·前端·javascript·人工智能·架构·electron·agent
德福危险5 小时前
从盲打xss到到模板注入:unix靶机渗透之Tempus_fugit4
前端·unix·xss