How do you pass data with Navigator.push and return data with Navigator.pop?
Tests imperative navigation and async result patterns. Strong answer: push via MaterialPageRoute with constructor args; await the Future from Navigator.push; pop with Navigator.pop(context, result). Red flag: using global state instead of the Future result.
WHAT THIS TESTS: This question evaluates whether you understand Flutter's imperative navigation API beyond copy-paste snippets. At senior level, interviewers care that you know Navigator.push returns a Future, that you type that Future explicitly, and that you understand this pattern becomes unwieldy at scale compared to declarative routing. They also want to see you mention BuildContext safety, null awareness, and the widget lifecycle around route disposal.
A GOOD ANSWER COVERS: First, forward data. You create the destination widget with constructor arguments inside the builder of MaterialPageRoute or PageRouteBuilder, keeping the payload immutable if possible. Second, receive backward data. You declare a typed variable like final String result = await Navigator.push(context, MaterialPageRoute(builder: (_) => DetailScreen(id: 42))) because push returns a Future that completes when the route is popped. Awaiting that Future blocks the async flow cleanly without manual callbacks. Third, send data back. In the new screen you call Navigator.pop(context, selectedValue) where selectedValue is the payload, and you ensure the payload type matches the awaited type. Fourth, edge cases. Mention that the Future resolves to null if the user dismisses the route with the system back button without calling pop, so you should handle nullability with a default or early return. Optionally note that in production apps you might prefer GoRouter or declarative routing, but the push-pop Future contract remains the same conceptually.
COMMON WRONG ANSWERS: Using a global variable or singleton to ferry data back instead of the Future. Passing data through static fields, shared state, or event buses for a simple two-screen handshake. Forgetting to await Navigator.push and then trying to read a result synchronously. Calling Navigator.pop without the second argument and then being surprised by a null result. Pushing without a MaterialPageRoute or CupertinoPageRoute and losing platform transitions. Using BuildContext after an async gap without checking mounted. Suggesting setState on the popped route after pop has already fired.
LIKELY FOLLOW-UPS: How would you handle this with GoRouter or declarative routing? What happens if the route is popped by the OS back gesture instead of your button? How do you pass complex objects and maintain type safety across routes? How would you refactor this if ten screens need the same result pattern? Would you use a callback pattern instead, and when is that appropriate?
ONE CONCRETE EXAMPLE: A settings screen pushes a language picker screen. The picker screen builds a ListView of locales. When the user taps an item, it calls Navigator.pop(context, Locale('es')). The settings screen awaited the push, so it receives Locale('es'), validates it is non-null, and rebuilds with setState to update the UI language immediately.
Read the original → docs.flutter.dev
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.