Labs ICT
Pro Login

Passing Data Between Screens

Sharing information across routes.

Passing Data Through Constructor

The most straightforward way to pass data between screens is through constructor parameters. You define final fields in your destination widget, accept them in the constructor, and provide values when you push the route. It's explicit and easy to follow.

class MessageScreen extends StatelessWidget {
  final String title;
  final String body;

  MessageScreen({required this.title, required this.body});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(title)),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Text(body, style: TextStyle(fontSize: 18)),
      ),
    );
  }
}

// Push with data:
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => MessageScreen(
      title: 'Welcome',
      body: 'This screen received data from the previous screen.',
    ),
  ),
);
Try it Yourself ->

Using Navigator.push Arguments

Instead of relying on constructors, you can pass data using the arguments parameter of MaterialPageRoute. The destination screen then retrieves it using ModalRoute.of(context).settings.arguments. This is handy when using named routes where you can't easily modify constructors.

// Sender screen
Navigator.pushNamed(
  context,
  '/details',
  arguments: {'id': 42, 'name': 'Flutter Course'},
);

// Receiver screen
class DetailScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final args = ModalRoute.of(context)!.settings.arguments as Map;

    return Scaffold(
      appBar: AppBar(title: Text(args['name'])),
      body: Center(
        child: Text('Course ID: ${args['id']}'),
      ),
    );
  }
}
Try it Yourself ->

Returning Data Back

You can pass data back to the previous screen by providing a value to Navigator.pop. The calling screen awaits the result using Navigator.push or Navigator.pushNamed, giving you a clean way to handle things like form submissions or confirmation dialogs.

// Screen A — awaiting a result
class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: () async {
            final result = await Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => InputScreen()),
            );
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text('You entered: $result')),
            );
          },
          child: Text('Enter Something'),
        ),
      ),
    );
  }
}

// Screen B — returning a result
class InputScreen extends StatelessWidget {
  final TextEditingController _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Input')),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(controller: _controller),
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: () {
                Navigator.pop(context, _controller.text);
              },
              child: Text('Submit'),
            ),
          ],
        ),
      ),
    );
  }
}
Try it Yourself ->