Updated File Structure
features/
└── data_structure/
└── list_of_maps/
├── components/
│ └── list_crud_component.dart
└── screen/
└── list_of_map_crud_screen.dart
core/
└── models/
└── student_model.dart
1. Create the Student Model
The model class will define the structure of a student object and include a factory method for converting maps to objects and objects to maps.
File: student_model.dart
class StudentModel {
final int id;
String name;
int age;
String grade;
StudentModel({
required this.id,
required this.name,
required this.age,
required this.grade,
});
// Factory method to create an instance from a Map
factory StudentModel.fromMap(Map<String, dynamic> map) {
return StudentModel(
id: map['id'],
name: map['name'],
age: map['age'],
grade: map['grade'],
);
}
// Convert the object to a Map
Map<String, dynamic> toMap() {
return {
'id': id,
'name': name,
'age': age,
'grade': grade,
};
}
}
2. Update the List CRUD Component
File: list_crud_component.dart
Here, we will use the StudentModel
instead of plain Map
objects. This improves type safety and readability.
import 'package:flutter/material.dart';
import '../../../core/models/student_model.dart';
class ListCrudComponent extends StatefulWidget {
const ListCrudComponent({Key? key}) : super(key: key);
@override
State<ListCrudComponent> createState() => _ListCrudComponentState();
}
class _ListCrudComponentState extends State<ListCrudComponent> {
// List of StudentModel objects
List<StudentModel> students = [
StudentModel(id: 1, name: "John", age: 20, grade: "90"),
StudentModel(id: 2, name: "Jane", age: 22, grade: "92"),
StudentModel(id: 3, name: "Mark", age: 21, grade: "92"),
];
final TextEditingController nameController = TextEditingController();
final TextEditingController ageController = TextEditingController();
final TextEditingController gradeController = TextEditingController();
bool isEditing = false;
int? editingId;
// Add a new student
void addStudent() {
setState(() {
students.add(StudentModel(
id: students.length + 1,
name: nameController.text,
age: int.tryParse(ageController.text) ?? 0,
grade: gradeController.text,
));
clearInputs();
});
}
// Update an existing student
void updateStudent() {
setState(() {
int index = students.indexWhere((student) => student.id == editingId);
if (index != -1) {
students[index].name = nameController.text;
students[index].age = int.tryParse(ageController.text) ?? students[index].age;
students[index].grade = gradeController.text;
}
clearInputs();
isEditing = false;
});
}
// Edit handler
void editStudent(int id) {
final student = students.firstWhere((s) => s.id == id);
nameController.text = student.name;
ageController.text = student.age.toString();
gradeController.text = student.grade;
setState(() {
isEditing = true;
editingId = id;
});
}
// Delete handler
void deleteStudent(int id) {
setState(() {
students.removeWhere((student) => student.id == id);
});
}
void clearInputs() {
nameController.clear();
ageController.clear();
gradeController.clear();
}
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
int columnCount = screenWidth > 600 ? 3 : 2;
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Input Fields
const Text("Add / Update Student", style: TextStyle(fontSize: 18)),
Row(
children: [
Expanded(
child: TextField(
controller: nameController,
decoration: const InputDecoration(labelText: "Name"),
),
),
const SizedBox(width: 8),
Expanded(
child: TextField(
controller: ageController,
decoration: const InputDecoration(labelText: "Age"),
keyboardType: TextInputType.number,
),
),
const SizedBox(width: 8),
Expanded(
child: TextField(
controller: gradeController,
decoration: const InputDecoration(labelText: "Grade"),
),
),
],
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: isEditing ? updateStudent : addStudent,
child: Text(isEditing ? "Update" : "Add"),
),
const SizedBox(height: 20),
// Student Cards
Wrap(
spacing: 10,
runSpacing: 10,
children: students.map((student) {
return SizedBox(
width: (screenWidth / columnCount) - 20,
child: Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Name: ${student.name}",
style: const TextStyle(fontWeight: FontWeight.bold)),
Text("Age: ${student.age}"),
Text("Grade: ${student.grade}"),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButton(
icon: const Icon(Icons.edit, color: Colors.blue),
onPressed: () => editStudent(student.id),
),
IconButton(
icon: const Icon(Icons.delete, color: Colors.red),
onPressed: () => deleteStudent(student.id),
),
],
),
],
),
),
),
);
}).toList(),
),
],
),
);
}
}
3. Main Screen
File: list_of_map_crud_screen.dart
import 'package:flutter/material.dart';
import '../components/list_crud_component.dart';
class ListOfMapCrudScreen extends StatelessWidget {
const ListOfMapCrudScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("List of Students CRUD"),
),
body: const ListCrudComponent(),
);
}
}