I have a class named User
which basically has two variables. name
and age
. Also, I have another class named UserList
.The aim of this class is to add the user
objects to a list and return that list.
class User with ChangeNotifier{
late String? name;
late int? age;
//set user name;
setName(String name){
this.name=name;
}
//set user age
setAge(int age){
this.age=age;
}
}
class UserList with ChangeNotifier{
final List<User> _list=[];
//add a new user to the list.
void addUserToList(User user){
_list.add(user);
notifyListeners();
}
//return the private list of users.
List<User>getUsers() =>_list;
}
Here is the scenario. I want to insert a value from one page and view that inserted value on the second page. Look at the pictures below. On the first page,
age
and name
valuesUser
object instance (user.setName, user.setAge
)user
object instance in a UserList
listprovider
to provide that list.(Provider.of<UserList>(context).addUserToList(user);
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => UserList()),
ChangeNotifierProvider(create: (context) => User()),
],
child: const MyApp())
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
TextEditingController nameController = TextEditingController();
TextEditingController ageController = TextEditingController();
@override
Widget build(BuildContext context) {
User user =User();
return Scaffold(
backgroundColor: Colors.grey[300],
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: nameController,
decoration: const InputDecoration(
hintText: "Name",
),
),
TextField(
controller: ageController,
decoration: const InputDecoration(
hintText: "Age",
),
),
ElevatedButton(onPressed: (){
//providing userList
user.setName(nameController.text); //setting user name
user.setAge(int.parse(ageController.text)); // setting user age
Provider.of<UserList>(context,listen: false).addUserToList(user); //providing the list of users
nameController.text=""; //after insertion, clearing the text field
ageController.text="";//after insertion, clearing the text field
user=User(); // instantiating a new user object for the following insertion.
Navigator.push(context, MaterialPageRoute(builder: (context)=>const ViewUser()));
}, child: const Text("Submit"))
],
),
),
);
}
}
On the second page, display those inserted values in a ListView
. Let's head to the main problem. Each item of ListView
is wrapped with a GestureDetector
to be able to gain functionality.I hope everything is clear up to now.
The problem is that; When I click on each item I want to return to the first page
. But, this time the TextField
shouldn't be empty. It must be replaced with the value of that item. For exmple. If I click on John Nash
and 43
. The First page must pop up with field value of that list item. To be able to do that I use another Provider
to provide User
object. But, I couldn't do it because of null safety it gives an error about the user object is null. Is there any idea that could be helpful.
class ViewUser extends StatelessWidget {
const ViewUser({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
List<User> userList = Provider.of<UserList>(context).getUsers();
return Scaffold(
appBar: AppBar(
title: const Text("Users List"),
),
body: ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: userList.length,
itemBuilder: (BuildContext context, int index) {
return Column(
children: [
const SizedBox(height: 15,),
GestureDetector(
onTap: (){
Provider.of<User>(context,listen: false)
.setName(userList[index].name!);
Provider.of<User>(context,listen: false)
.setAge(userList[index].age!);
Navigator.push(context, MaterialPageRoute(builder: (context)=>MyHomePage()));
},
child: Container(
height: 50,
color: Colors.amber,
child: Center(
child: Row(
children: [
Text("Name: " + userList[index].name!),
const SizedBox(
width: 40,
),
Text("Age:" + userList[index].age.toString()),
],
),
),
),
),
],
);
}),
);
}
}
While you are using Navigator.push
you can pass user as arguments
.
GestureDetector(
onTap: () {
final user = userList[index];
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MyHomePage(),
settings: RouteSettings(
arguments: user,
)),
);
},
MyHomePage
build method will be
@override
Widget build(BuildContext context) {
User user = User();
User? args = ModalRoute.of(context)?.settings.arguments as User?;
if (args != null) {
nameController.text = args.name ?? "";
ageController.text = args.age.toString();
}
return Scaffold(....
You can also use Navigator.pop
in this case.
GestureDetector(
onTap: () {
final user = userList[index];
Navigator.pop(context, user);
},
And to receive data on MyHomePage
onPressed: () async {
//....
final data = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const ViewUser(),
),
);
if (data != null) {
nameController.text = data.name ?? "";
ageController.text = data.age.toString();
}
More about navigate-with-arguments.