Yes, Flutter makes this pattern easy using Navigator.push and Navigator.pop.
Here’s a full working example:
Screen A (caller):
import 'package:flutter/material.dart';
import 'screen_b.dart'; // assume you created this separately
class ScreenA extends StatefulWidget {
@override
_ScreenAState createState() => _ScreenAState();
}
class _ScreenAState extends State<ScreenA> {
String returnedData = 'No data yet';
void _navigateAndGetData() async {
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => ScreenB()),
);
if (result != null) {
setState(() {
returnedData = result;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Screen A")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Returned data: $returnedData'),
ElevatedButton(
onPressed: _navigateAndGetData,
child: Text('Go to Screen B'),
),
],
),
),
);
}
}
Screen B (Returns Data) :
import 'package:flutter/material.dart';
class ScreenB extends StatelessWidget {
final TextEditingController _controller = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Screen B")),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(controller: _controller),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.pop(context, _controller.text); // return data
},
child: Text('Send back data'),
),
],
),
),
);
}
}
Navigator.push returns a Future that completes when the pushed route is popped.
In ScreenB, Navigator.pop(context, data) returns the data to the previous screen.
You can await the result and use setState to update the UI.
This is the Flutter-recommended way to pass data back when popping a route.
Navigator.push returns a Future?In Flutter, Navigator.push() creates a new route (i.e., a screen or a page) and adds it to the navigation stack. This is an asynchronous operation — the new screen stays on top until it's popped.
Because of this, Navigator.push() returns a Future<T>, where T is the data type you expect when the screen is popped. The await keyword lets you wait for this result without blocking the UI.
final result = await Navigator.push(...); // result gets assigned when the screen pops
Navigator.pop(context, data) work?When you call:
Navigator.pop(context, 'some data');
You're removing the current screen from the navigation stack and sending data back to the screen below. That data becomes the result that was awaited by Navigator.push.
Think of it like a dialog that returns a value when closed — except you're navigating entire screens.
This navigation-and-return-data pattern is especially useful in cases like:
Picking a value from a list (e.g., selecting a city or a contact).
Filling out a form and submitting it.
Performing any interaction in a secondary screen that should inform the calling screen of the result.