Passing Arguments in Flutter

With only one codebase, Flutter, Google’s UI toolkit, simplifies the process of developing stunning natively built desktop, web, and mobile applications. Moving between displays and transferring data across them is a regular activity in app development. We’ll look at passing arguments between screens in Flutter in this blog article.

1. Basic Navigation in Flutter

Before diving into passing arguments, let’s quickly review how to navigate between screens in Flutter. Navigation in Flutter is managed using a Navigator widget and Routes. Here’s a simple example of navigating from one screen to another:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Navigation Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: FirstScreen(),
    );
  }
}

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => SecondScreen()),
            );
          },
          child: Text('Go to Second Screen'),
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Second Screen'),
      ),
      body: Center(
        child: Text('Welcome to the second screen!'),
      ),
    );
  }
}

2. Passing Arguments Using Navigator.push

To pass arguments to a screen, you can use the Navigator.push method along with the MaterialPageRoute constructor. Here’s how you can modify the above example to pass data to the SecondScreen:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Navigation Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: FirstScreen(),
    );
  }
}

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => SecondScreen(data: 'Hello from the first screen!'),
              ),
            );
          },
          child: Text('Go to Second Screen'),
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  final String data;

  SecondScreen({required this.data});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Second Screen'),
      ),
      body: Center(
        child: Text(data),
      ),
    );
  }
}

In this example, we pass a string from FirstScreen to SecondScreen using the data parameter.

3. Using Named Routes for Passing Arguments

Flutter also supports named routes, which can make it easier to manage navigation in larger applications. To pass arguments using named routes, you’ll need to define the routes and use the onGenerateRoute property. Here’s an example:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Navigation Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      // Define routes
      initialRoute: '/',
      routes: {
        '/': (context) => FirstScreen(),
        '/second': (context) => SecondScreen(),
      },
      onGenerateRoute: (settings) {
        if (settings.name == '/second') {
          final args = settings.arguments as String;

          return MaterialPageRoute(
            builder: (context) {
              return SecondScreen(data: args);
            },
          );
        }
        return null;
      },
    );
  }
}

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pushNamed(
              context,
              '/second',
              arguments: 'Hello from the first screen!',
            );
          },
          child: Text('Go to Second Screen'),
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  final String data;

  SecondScreen({required this.data});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Second Screen'),
      ),
      body: Center(
        child: Text(data),
      ),
    );
  }
}

In this example, we use named routes and the arguments property to pass data between screens.

4. Passing Complex Objects Between Screens

In addition to simple data types like strings, you can pass complex objects between screens. Here’s an example:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Navigation Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      initialRoute: '/',
      routes: {
        '/': (context) => FirstScreen(),
        '/second': (context) => SecondScreen(),
      },
      onGenerateRoute: (settings) {
        if (settings.name == '/second') {
          final args = settings.arguments as ScreenArguments;

          return MaterialPageRoute(
            builder: (context) {
              return SecondScreen(
                title: args.title,
                message: args.message,
              );
            },
          );
        }
        return null;
      },
    );
  }
}

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pushNamed(
              context,
              '/second',
              arguments: ScreenArguments(
                'Hello from the first screen!',
                'This is a message.',
              ),
            );
          },
          child: Text('Go to Second Screen'),
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  final String title;
  final String message;

  SecondScreen({required this.title, required this.message});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Text(message),
      ),
    );
  }
}

class ScreenArguments {
  final String title;
  final String message;

  ScreenArguments(this.title, this.message);
}

In this example, we define a ScreenArguments class to hold the data we want to pass between screens.

Conclusion

Passing arguments between screens in Flutter is straightforward and flexible. Whether you’re passing simple data types or complex objects, Flutter’s navigation system provides the tools you need. By using Navigator.push, Navigator.pushNamed, and named routes, you can efficiently manage navigation and data transfer in your Flutter applications.