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

相关推荐
子兮曰4 小时前
Bun v1.3.14 深度解析:Image API、HTTP/3、全局虚拟存储与五十项变革
前端·后端·bun
薛定e的猫咪4 小时前
因果推理研究方向综述笔记
人工智能·笔记·深度学习·算法
AOwhisky4 小时前
虚拟化技术学习笔记
linux·运维·笔记·学习·虚拟化技术
一只机电自动化菜鸟5 小时前
一建机电备考笔记(33) 机电专业技术(起重技术-吊装方案)(含考频+题型)
经验分享·笔记·学习·职场和发展·课程设计
kyriewen5 小时前
今天,百年巨头一次砍了9200人,而一个离职科学家的实话让全网睡不着觉
前端·openai·ai编程
问心无愧05135 小时前
ctf show web 入门42
android·前端·android studio
kyriewen5 小时前
老板逼我上AI,我偷偷在浏览器里跑LLaMA,省下20万API费
前端·react.js·llm
Beginner x_u6 小时前
前端八股整理(手写 02)|数组转树、数组扁平化、随机打乱一个数组
前端·数组·数组转树·数组扁平化
KaMeidebaby6 小时前
卡梅德生物技术快报|禽类成纤维细胞 FISH 实验:鸟类性别染色体基因定位技术实现与数据验证
前端·数据库·其他·百度·新浪微博
天若有情6736 小时前
前端高阶性能优化:跳出传统懒加载与预加载,基于用户行为做轻量预判加载
前端·性能优化