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