List of Maps

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(),
    );
  }
}

Updated on