Flutter — Navigate and pass custom data to next screen

The Dev Guy
5 min readJul 7, 2023

--

This article will show how to navigate from one screen to another in Flutter and pass custom data along with it.

As discussed in previous navigation article where we want to navigate from one screen to another only without the need of sending anything to next screen but there are situations where we have to pass data while navigating to the next screen. Flutter provides couple of way of accomplishing this

  1. Pass custom data inside Widget constructor
  2. Pass the arguments using RouteSettings

Approach 1: Pass custom data inside Widget constructor 🚀🚀🚀

Our app contains two screens

  1. Users screen where we will show list of users.
  2. UserDetail screen where we are going to show the name and occupation of specific user.

On clicking any user at Users screen, we want to pass the user object that’s been tapped and display it on UserDetail screen.

There are 5 brief steps we have to take in order to accomplish this task.

  1. Define User class.
  2. Create list of users using User class created in step 1.
  3. Create Users screen displaying list of user created in step 2.
  4. Create a UserDetail screen that can display information about a user.
  5. Navigate and pass data to the UserDetail screen.

Let see the detail of each of these 5 steps.

1. Define User class

For simplicity, lets assume our User have only two attributes i.e. Name & Occupation. User class looks like this

class User{
final String name;
final String occupation;

const User(this.name, this.occupation);
}

2. Define list of Users

Suppose we are receiving this below list from some API or local data-source which we need to display at Users screen

const userList = [
User("User - 1", "Software Engineer"),
User("User - 2", "Android Engineer"),
User("User - 3", "IOS Engineer"),
User("User - 4", "Electrical Engineer"),
User("User - 5", "Mechanical Engineer")
];

3. Create Users Screen

Two things that we need to consider at this screen are:

  1. Users screen will received a list named as users in its constructor which will be displayed using ListView.builder and ListTile.
  2. Show each user data i.e. name & occupation will be shown in title and subtitle using ListTile
class UsersScreen extends StatelessWidget {
const UsersScreen({super.key, required this.users});

final List<User> users;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Users'),
),
body: ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(users[index].name),
subtitle: Text(users[index].occupation),
);
},
),
);
}
}

When any of the list items gets tapped, it will navigate to UserDetail screen passing the current user object along with it. (See step 5 below)

4. Create UserDetail Screen

We are going to display the name of user in Appbar title and we are going to display a concatenated string of name & occupation in body

class UserDetailScreen extends StatelessWidget {

const UserDetailScreen({super.key, required this.user});

final User user;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(user.name), // title of UserDetail screen
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Text("${user.name} is ${user.occupation}"),
),
);
}
}

5. Navigate and pass data to the UserDetail screen

So how do we pass user object to next screen. For this, we need to include the following 2 things inside Users screen ListTile code:

  1. onTap() method which will listen to tapped events.
  2. Navigator.push() and MaterialPageRoute() methods to navigate and pass data from Users screen to UserDetail screen.
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UserDetailScreen(user: users[index]),
),
);
},

Complete code for Approach 1

import 'package:flutter/material.dart';

class User{
final String name;
final String occupation;

const User(this.name, this.occupation);
}

void main() {
const userList = [
User("User - 1", "Software Engineer"),
User("User - 2", "Android Engineer"),
User("User - 3", "IOS Engineer"),
User("User - 4", "Electrical Engineer"),
User("User - 5", "Mechanical Engineer")
];
runApp(
const MaterialApp(
title: 'Demo',
home: UsersScreen(
users: userList,
),
),
);
}

class UsersScreen extends StatelessWidget {
const UsersScreen({super.key, required this.users});

final List<User> users;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Users'),
),
body: ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(users[index].name),
subtitle: Text(users[index].occupation),

onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UserDetailScreen(user: users[index]),
),
);
},
);
},
),
);
}
}

class UserDetailScreen extends StatelessWidget {

const UserDetailScreen({super.key, required this.user});

final User user;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(user.name),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Text("${user.name} is ${user.occupation}"),
),
);
}
}
Sending user object from Users screen to UserDetail screen

Approach 2: Pass the arguments using RouteSettings 🚀🚀🚀

The first two steps shown in Approach 1 above remains same. Lets talk about the difference in remaining steps.

Inside Users screen onTap() method, pass the arguments as part of the RouteSettings like shown below.

onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const UserDetailScreen(),
// Pass the arguments as part of the RouteSettings. The
// UserDetail screen reads the arguments from these settings.
settings: RouteSettings(
arguments: users[index],
),
),
);
},

This will pass the user object as an argument to the next screen we are navigating. We need to use ModelRoute.of() method (more details can be found here) inside the next screen in order to extract the arguments like shown below.

class UserDetailScreen extends StatelessWidget {
const UserDetailScreen({super.key});

@override
Widget build(BuildContext context) {
final user = ModalRoute.of(context)!.settings.arguments as User;
return Scaffold(
appBar: AppBar(
title: Text(user.name),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Text("${user.name} is ${user.occupation}"),
),
);
}
}

Notice how we removed the user field from constructor and retreiving and assigning it inside build() method using ModelRoute.of() method.

Complete code for Approach 2

import 'package:flutter/material.dart';

class User{
final String name;
final String occupation;

const User(this.name, this.occupation);
}

void main() {
const userList = [
User("User - 1", "Software Engineer"),
User("User - 2", "Android Engineer"),
User("User - 3", "IOS Engineer"),
User("User - 4", "Electrical Engineer"),
User("User - 5", "Mechanical Engineer")
];
runApp(
const MaterialApp(
title: 'Demo',
home: UsersScreen(
users: userList,
),
),
);
}

class UsersScreen extends StatelessWidget {
const UsersScreen({super.key, required this.users});

final List<User> users;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Users'),
),
body: ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(users[index].name),
subtitle: Text(users[index].occupation),

onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const UserDetailScreen(),
// Pass the arguments as part of the RouteSettings. The
// UserDetail screen reads the arguments from these settings.
settings: RouteSettings(
arguments: users[index],
),
),
);
},
);
},
),
);
}
}

class UserDetailScreen extends StatelessWidget {
const UserDetailScreen({super.key});

@override
Widget build(BuildContext context) {
final user = ModalRoute.of(context)!.settings.arguments as User;
return Scaffold(
appBar: AppBar(
title: Text(user.name),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Text("${user.name} is ${user.occupation}"),
),
);
}
}

It will produce same results as shown for Approach 1 in previous section.

Use https://dartpad.dartlang.org/ to test these above code samples.

Conclusion

In this article, we learn how easily we can use Flutter Navigation using Widget Constructor approach and RouteSettings approach to send and display our custom data from one screen to another screen.

Feel free to comment if you find anything we can discuss and improve upon. New ideas are always welcome.

Loved this article and want to support me as a writer? Consider throwing a clap 👏 and follow me for more amazing articles. This will keep me motivated to write more and share it with community.

--

--

The Dev Guy

Mobile App Developer | Tech Enthusiast | Blogger 📚 Constant learner, continuously improving skills and staying up-to-date with new technologies and frameworks.