flutter
/

Flutter Navigation Arguments – Passing Data Between Screens

Last Sync: Today

On this page

9
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

flutter

Flutter Navigation Arguments – Passing Data Between Screens

Why Pass Arguments?

In any non‑trivial app, you'll need to send data from one screen to another when the user navigates. For example, tapping a product in a list should take you to a detail screen that knows which product to display. Flutter's Navigator allows you to pass arguments in several ways, from simple primitive values to complex objects. This guide covers the most common patterns for passing data between screens.

Passing Arguments with MaterialPageRoute

When pushing a new route using MaterialPageRoute, you can include an arguments parameter in the route's settings. The settings object holds metadata about the route, including arguments.

DARTRead-only
1
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => DetailScreen(),
    settings: RouteSettings(
      arguments: 'Hello from first screen', // can be any object
    ),
  ),
);

Extracting Arguments in the Destination Screen

In the destination widget, you can retrieve the arguments using ModalRoute.of(context). This gives you the current route, from which you can access the settings.arguments.

DARTRead-only
1
class DetailScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Extract arguments
    final args = ModalRoute.of(context)!.settings.arguments as String;

    return Scaffold(
      appBar: AppBar(title: Text('Detail')),
      body: Center(child: Text('Received: $args')),
    );
  }
}

You should cast the argument to the expected type. Always handle cases where the argument might be missing or of the wrong type.

Passing Complex Objects

You can pass any object as an argument – a Map, a custom class, or even a function. For custom classes, it's a good practice to make them immutable and possibly implement toString for debugging. For example:

DARTRead-only
1
class Product {
  final int id;
  final String name;
  final double price;

  const Product({required this.id, required this.name, required this.price});
}

// Pushing
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => ProductDetailScreen(),
    settings: RouteSettings(arguments: product),
  ),
);

// Extracting
final product = ModalRoute.of(context)!.settings.arguments as Product;

Named Routes with Arguments

If you use named routes (defined in MaterialApp's routes), you can pass arguments by using Navigator.pushNamed with a separate arguments parameter.

DARTRead-only
1
MaterialApp(
  initialRoute: '/',
  routes: {
    '/': (context) => HomeScreen(),
    '/detail': (context) => DetailScreen(),
  },
  onGenerateRoute: (settings) {
    // Alternatively, use onGenerateRoute for dynamic routes
    if (settings.name == '/detail') {
      final args = settings.arguments;
      return MaterialPageRoute(
        builder: (context) => DetailScreen(),
        settings: settings,
      );
    }
    return null;
  },
)

// Pushing with arguments
Navigator.pushNamed(
  context,
  '/detail',
  arguments: 'My argument',
);

With onGenerateRoute, you can handle dynamic route names (e.g., /product/123) as well. For that, you'd parse the route path to extract the ID. However, for simplicity, using arguments is often sufficient.

Returning Data Back to the Previous Screen

When you pop a route, you can also return data to the previous screen using Navigator.pop(context, result). The previous screen can wait for the result with await Navigator.push(...).

DARTRead-only
1
// On the first screen
final result = await Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => SecondScreen()),
);
print('Result: $result');

// On the second screen
Navigator.pop(context, 'Data to return');

Common Mistakes

    • Forgetting to cast: Always cast the arguments to the expected type. Use as and handle potential type errors.
    • Not checking for null: The arguments might be null if the route was created without arguments. Use null checks or provide default values.
    • Passing mutable objects: If you pass a mutable object, changes in the destination screen might affect the original (if you later modify it). Consider making objects immutable or passing copies.
    • Using settings.arguments without casting: This returns Object?, so you must cast it to the correct type.
    • Not handling edge cases: If the argument type is wrong, your app will crash. Validate or use if (args is String) before casting.

Best Practices

    • Use a dedicated model class to pass complex data.
    • For large or sensitive data, consider using a state management solution (like Provider, Riverpod) instead of passing through routes.
    • When using named routes with arguments, consider using onGenerateRoute to centralize argument parsing.
    • Always document what arguments a screen expects (e.g., using comments).
    • Use const for route arguments that are constant values.

Key Takeaways

    • Arguments are passed via RouteSettings.arguments in MaterialPageRoute.
    • Retrieve arguments in the destination using ModalRoute.of(context)!.settings.arguments.
    • You can pass any object, including custom classes, as arguments.
    • Named routes also support arguments through Navigator.pushNamed and onGenerateRoute.
    • Use await Navigator.push and Navigator.pop to return data back to the previous screen.
    • Always cast and handle potential null or type mismatches to avoid runtime errors.

Try it yourself

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Nav Arguments Demo',
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home')),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => DetailScreen(),
                settings: RouteSettings(
                  arguments: 'Hello from Home!',
                ),
              ),
            );
          },
          child: Text('Go to Detail'),
        ),
      ),
    );
  }
}

class DetailScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final args = ModalRoute.of(context)!.settings.arguments as String;
    return Scaffold(
      appBar: AppBar(title: Text('Detail')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Received: $args'),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () => Navigator.pop(context),
              child: Text('Go Back'),
            ),
          ],
        ),
      ),
    );
  }
}

Test Your Knowledge

Q1
of 4

How do you pass arguments when pushing a route with `MaterialPageRoute`?

A
In the `builder` function as a constructor parameter
B
In the `settings` property with `RouteSettings.arguments`
C
In the `onTap` callback
D
You can't pass arguments with `MaterialPageRoute`
Q2
of 4

How do you retrieve arguments in the destination screen?

A
Navigator.of(context).arguments
B
Route.of(context).arguments
C
ModalRoute.of(context)!.settings.arguments
D
context.arguments
Q3
of 4

What should you do after retrieving an argument to ensure type safety?

A
Use `toString()`
B
Use `as` to cast to the expected type
C
Print the argument
D
Nothing, it's already typed
Q4
of 4

How do you return data to the previous screen?

A
Navigator.pop(context, data)
B
Navigator.pushReplacement(context, data)
C
Navigator.popAndPush(context, data)
D
context.pop(data)

Frequently Asked Questions

Can I pass a function as an argument?

Yes, you can pass any object, including functions. However, be careful because passing functions can lead to unintended side effects and may make it harder to test. For callbacks, it's often better to define a callback in the destination widget's constructor.

How do I pass arguments with `Navigator.pushReplacement`?

The same way – you create a MaterialPageRoute with settings.arguments. The replacement route receives the arguments like any other route.

How can I pass arguments to the initial route?

Use MaterialApp's onGenerateRoute to create the initial route with arguments. Alternatively, you can set the initial route using a custom RouteInformationParser (more advanced). For simple cases, you can store arguments in a global or use a state management solution.

What is the difference between `settings.arguments` and passing parameters via constructor?

When you use MaterialPageRoute(builder: (context) => MyWidget(data: data)), you're passing data through the constructor. That's perfectly fine and simpler for direct navigation. The settings.arguments approach is useful when you need to extract arguments in a common way, especially with named routes or when you have a generic wrapper screen.

Can I pass a `Future` as an argument?

Yes, but you should be cautious about the lifecycle. If the Future hasn't completed when the screen is built, you'll need to handle the loading state inside the screen, or you can await it before pushing.

Previous

flutter named routes

Next

flutter bottom navigation bar

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.