Popular Searches
Popular Course Categories
Popular Courses

Managing collections of data

Managing collections of data

Flutter Lists & Collections

Managing Collections of Data in Dart

Managing collections of data is an essential skill in Dart and Flutter development. Applications frequently work with groups of related data such as users, products, orders, messages, categories, students, transactions, and API responses. Dart provides built-in collection types such as List, Set, and Map, along with common collection operations such as filtering, searching, sorting, mapping, and iteration.

Understanding collections helps developers write clean, organized, reusable, and efficient Dart and Flutter applications. Dart's core collection APIs include List, Set, Map, and Iterable-related functionality. :contentReference[oaicite:0]{index=0}

1. What is a Collection?

A collection is an object that contains multiple values or objects. Instead of creating separate variables for every piece of related data, a collection allows multiple values to be stored and processed together.

For example, instead of writing:

String student1 = 'Amit';
String student2 = 'Rahul';
String student3 = 'Neha';

you can manage the students using a collection:

List students = [
  'Amit',
  'Rahul',
  'Neha',
];

This makes it easier to loop through, search, filter, update, and display the data.

2. Main Collection Types in Dart

Dart provides three commonly used collection types through its core collection support: List, Set, and Map. :contentReference[oaicite:1]{index=1}

CollectionPurposeDuplicatesAccess Method
ListStores ordered dataAllowedIndex
SetStores unique valuesNot allowedValue / iteration
MapStores key-value pairsKeys must be uniqueKey

3. Working with Lists

A List is an ordered collection. List elements are accessed using indexes, and indexing starts from zero. :contentReference[oaicite:2]{index=2}

Creating a List

List fruits = [
  'Apple',
  'Banana',
  'Mango',
];

print(fruits);

Output:

[Apple, Banana, Mango]

Accessing List Elements

print(fruits[0]);
print(fruits[1]);
print(fruits[2]);

Output:

Apple
Banana
Mango

Adding Data

fruits.add('Orange');

fruits.addAll([
  'Grapes',
  'Pineapple',
]);

print(fruits);

Updating Data

fruits[0] = 'Green Apple';

print(fruits);

Removing Data

fruits.remove('Banana');
fruits.removeAt(0);

print(fruits);

4. Managing List Data with Loops

Loops are commonly used to process every item in a collection.

Using for-in

List students = [
  'Amit',
  'Rahul',
  'Neha',
];

for (var student in students) {
  print(student);
}

Using forEach()

students.forEach((student) {
  print(student);
});

Using an Indexed for Loop

for (int i = 0; i < students.length; i++) {
  print('$i: ${students[i]}');
}

5. Searching Collection Data

Searching is important when applications need to determine whether a particular value exists.

contains()

List names = [
  'Amit',
  'Rahul',
  'Neha',
];

if (names.contains('Rahul')) {
  print('Rahul found');
}

indexOf()

int index = names.indexOf('Neha');

print(index);

any()

The any() method checks whether at least one element satisfies a condition.

List marks = [45, 67, 89, 32];

bool hasHighMarks = marks.any(
  (mark) => mark > 80,
);

print(hasHighMarks);

every()

The every() method checks whether all elements satisfy a condition.

bool allPassed = marks.every(
  (mark) => mark >= 40,
);

print(allPassed);

6. Filtering Collection Data

Filtering means selecting only the elements that satisfy a particular condition.

Dart's where() method is useful for this purpose.

List numbers = [
  10,
  15,
  20,
  25,
  30,
];

List evenNumbers = numbers
    .where((number) => number % 2 == 0)
    .toList();

print(evenNumbers);

Output:

[10, 20, 30]

Practical Student Example

class Student {
  String name;
  int marks;

  Student(this.name, this.marks);
}

void main() {
  List students = [
    Student('Amit', 85),
    Student('Rahul', 35),
    Student('Neha', 92),
    Student('Priya', 42),
  ];

  List passedStudents = students
      .where((student) => student.marks >= 40)
      .toList();

  for (var student in passedStudents) {
    print(student.name);
  }
}

7. Transforming Collection Data

Transformation means converting each item into another form. Dart's map() method is commonly used for this.

List prices = [
  100,
  200,
  300,
];

List updatedPrices = prices
    .map((price) => price + 50)
    .toList();

print(updatedPrices);

Output:

[150, 250, 350]

Transforming Strings

List names = [
  'amit',
  'rahul',
  'neha',
];

List upperCaseNames = names
    .map((name) => name.toUpperCase())
    .toList();

print(upperCaseNames);

8. Sorting Collection Data

Sorting organizes collection elements according to a particular order.

Ascending Order

List numbers = [
  50,
  10,
  40,
  20,
  30,
];

numbers.sort();

print(numbers);

Output:

[10, 20, 30, 40, 50]

Descending Order

numbers.sort(
  (a, b) => b.compareTo(a),
);

print(numbers);

Sorting Objects

students.sort(
  (a, b) => b.marks.compareTo(a.marks),
);

This sorts students from highest marks to lowest marks.

9. Using Sets for Unique Data

A Set stores unique elements. Adding an element that is already present does not create another copy. :contentReference[oaicite:3]{index=3}

Set skills = {
  'Dart',
  'Flutter',
  'Dart',
  'Firebase',
};

print(skills);

The duplicate Dart value is stored only once.

Adding Elements to a Set

skills.add('Git');

skills.addAll([
  'REST API',
  'SQLite',
]);

Checking Set Membership

print(skills.contains('Flutter'));
print(skills.contains('Java')); 

Removing Set Elements

skills.remove('Git');

10. Removing Duplicates from a List

A common collection-management task is removing duplicate values from a list.

List numbers = [
  10,
  20,
  10,
  30,
  20,
  40,
];

List uniqueNumbers = numbers.toSet().toList();

print(uniqueNumbers);

Output:

[10, 20, 30, 40]

11. Set Operations

Sets are useful when comparing groups of unique data.

Union

Set androidSkills = {
  'Dart',
  'Flutter',
  'Firebase',
};

Set webSkills = {
  'HTML',
  'CSS',
  'Dart',
};

Set allSkills = androidSkills.union(webSkills);

print(allSkills);

Intersection

Set commonSkills =
    androidSkills.intersection(webSkills);

print(commonSkills);

The intersection contains values present in both sets.

Difference

Set onlyAndroid =
    androidSkills.difference(webSkills);

print(onlyAndroid);

12. Working with Maps

A Map stores data as key-value pairs. Each key can occur only once, and a key is used to retrieve its associated value. :contentReference[oaicite:4]{index=4}

Creating a Map

Map student = {
  'name': 'Amit',
  'age': 21,
  'marks': 85,
};

Accessing Map Values

print(student['name']);
print(student['marks']);

Adding Data

student['course'] = 'Flutter';

Updating Data

student['marks'] = 90;

Removing Data

student.remove('age');

13. Checking Map Data

containsKey()

if (student.containsKey('name')) {
  print('Name exists');
}

containsValue()

if (student.containsValue('Flutter')) {
  print('Flutter course found');
}

Getting Keys

print(student.keys);

Getting Values

print(student.values);

14. Iterating Through a Map

Map data can be processed using forEach().

student.forEach((key, value) {
  print('$key: $value');
});

You can also iterate over keys and use each key to retrieve its value:

for (var key in student.keys) {
  print('$key: ${student[key]}');
}

15. List of Maps

A list of maps is useful for representing structured data such as products, users, orders, or API responses.

List> products = [
  {
    'name': 'Laptop',
    'price': 55000,
    'category': 'Electronics',
  },
  {
    'name': 'Phone',
    'price': 25000,
    'category': 'Electronics',
  },
  {
    'name': 'Chair',
    'price': 5000,
    'category': 'Furniture',
  },
];

Accessing Product Data

print(products[0]['name']);
print(products[1]['price']);

Filtering Products

var electronics = products
    .where((product) => product['category'] == 'Electronics')
    .toList();

print(electronics);

16. Map of Lists

A map can contain lists as values.

Map> courses = {
  'Flutter': [
    'Dart',
    'Widgets',
    'Layouts',
  ],
  'Web': [
    'HTML',
    'CSS',
    'JavaScript',
  ],
};

Accessing Nested Data

print(courses['Flutter']![0]);

Output:

Dart

17. Nested Collections

Collections can contain other collections. This is useful when representing hierarchical or grouped data.

List> matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9],
];

print(matrix[0][1]);
print(matrix[2][2]);

18. Using Collection Literals

Dart provides concise literal syntax for creating Lists, Sets, and Maps. Generic type arguments can also be specified when needed. :contentReference[oaicite:5]{index=5}

List names = [
  'Amit',
  'Rahul',
];

Set skills = {
  'Dart',
  'Flutter',
};

Map marks = {
  'Amit': 85,
  'Rahul': 75,
};

19. Collection-if

Collection-if allows elements to be conditionally included while creating a collection.

bool isAdmin = true;

List menu = [
  'Home',
  'Profile',
  if (isAdmin) 'Admin',
];

print(menu);

20. Collection-for

Collection-for allows a loop to generate collection elements.

List numbers = [
  for (int i = 1; i <= 5; i++)
    i * 10,
];

print(numbers);

Output:

[10, 20, 30, 40, 50]

21. Spread Operator

The spread operator ... inserts the elements of one collection into another collection. Dart also provides the null-aware spread operator ...?. :contentReference[oaicite:6]{index=6}

List basicSkills = [
  'Dart',
  'Flutter',
];

List advancedSkills = [
  'Firebase',
  'REST API',
];

List allSkills = [
  ...basicSkills,
  ...advancedSkills,
];

print(allSkills);

Null-Aware Spread

List? optionalSkills;

List skills = [
  'Dart',
  ...?optionalSkills,
  'Flutter',
];

print(skills);

22. Combining Multiple Collection Operations

Collection methods can be chained together to create a data-processing pipeline.

List prices = [
  100,
  250,
  50,
  400,
  150,
];

List result = prices
    .where((price) => price >= 100)
    .map((price) => price + 20)
    .toList();

result.sort();

print(result);

The code performs three operations:

  1. Filters prices greater than or equal to 100.
  2. Adds 20 to every remaining price.
  3. Sorts the resulting values.

23. Working with Iterable

Many collection operations return an Iterable. Methods such as where() and map() are commonly used to process collections lazily, and toList() can be used when an actual List is needed.

List numbers = [1, 2, 3, 4, 5];

Iterable doubled = numbers.map(
  (number) => number * 2,
);

List result = doubled.toList();

print(result);

24. Checking Empty Collections

Use isEmpty and isNotEmpty to safely check whether a collection contains data. These properties are available across commonly used collection types. :contentReference[oaicite:7]{index=7}

List users = [];

if (users.isEmpty) {
  print('No users found');
}

if (users.isNotEmpty) {
  print('Users are available');
}

25. Working with Null-Safe Collections

Dart's null safety requires developers to distinguish between a collection that exists but is empty and a collection that itself may be null.

Nullable Collection

List? names;

print(names);

Non-Nullable Collection

List names = [];

print(names);

Safe Access

print(names?.length);

26. Managing API-Style Data

Flutter applications commonly receive collections from APIs. A typical JSON-like response may contain a list of objects.

List> users = [
  {
    'id': 1,
    'name': 'Amit',
    'email': '[email protected]',
  },
  {
    'id': 2,
    'name': 'Neha',
    'email': '[email protected]',
  },
];

Extracting User Names

List userNames = users
    .map((user) => user['name'] as String)
    .toList();

print(userNames);

27. Using Model Classes Instead of Dynamic Maps

For larger applications, creating model classes can make collection data easier to maintain and safer to work with.

class User {
  final int id;
  final String name;
  final String email;

  User({
    required this.id,
    required this.name,
    required this.email,
  });
}

List users = [
  User(
    id: 1,
    name: 'Amit',
    email: '[email protected]',
  ),
  User(
    id: 2,
    name: 'Neha',
    email: '[email protected]',
  ),
];

Now the application can access strongly typed properties:

for (var user in users) {
  print(user.name);
  print(user.email);
}

28. Managing a Shopping Cart

A shopping cart is a practical example of collection management.

class Product {
  final String name;
  final double price;

  Product(this.name, this.price);
}

void main() {
  List cart = [
    Product('Laptop', 55000),
    Product('Mouse', 1200),
    Product('Keyboard', 2500),
  ];

  double total = cart.fold(
    0,
    (sum, product) => sum + product.price,
  );

  print('Cart Total: ₹$total');
}

29. Managing a Student Collection

class Student {
  final String name;
  final int marks;

  Student(this.name, this.marks);
}

void main() {
  List students = [
    Student('Amit', 85),
    Student('Rahul', 38),
    Student('Neha', 92),
    Student('Priya', 70),
  ];

  var passedStudents = students
      .where((student) => student.marks >= 40)
      .toList();

  passedStudents.sort(
    (a, b) => b.marks.compareTo(a.marks),
  );

  for (var student in passedStudents) {
    print('${student.name}: ${student.marks}');
  }
}

30. Managing Collections in Flutter UI

Collections are extremely useful when creating dynamic Flutter interfaces. A list of data can be converted into widgets instead of manually writing every widget.

import 'package:flutter/material.dart';

class ProductPage extends StatelessWidget {
  ProductPage({super.key});

  final List products = [
    'Laptop',
    'Phone',
    'Tablet',
    'Headphones',
  ];

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

31. Dynamic Collection Management with setState()

In a StatefulWidget, collection data can be updated and the UI can be rebuilt using setState().

class TodoPage extends StatefulWidget {
  const TodoPage({super.key});

  @override
  State createState() => _TodoPageState();
}

class _TodoPageState extends State {
  final List todos = [];

  void addTodo() {
    setState(() {
      todos.add('New Task');
    });
  }

  void removeTodo(int index) {
    setState(() {
      todos.removeAt(index);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Todo List'),
      ),
      body: ListView.builder(
        itemCount: todos.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(todos[index]),
            trailing: IconButton(
              icon: const Icon(Icons.delete),
              onPressed: () {
                removeTodo(index);
              },
            ),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: addTodo,
        child: const Icon(Icons.add),
      ),
    );
  }
}

32. Choosing the Right Collection

RequirementRecommended CollectionExample
Ordered dataListProducts, messages, students
Unique dataSetTags, categories, unique IDs
Key-value dataMapUser details, settings
Indexed accessListItems displayed by position
Fast membership checksSetChecking selected IDs
Lookup by identifierMapUser ID to User object

33. Common Collection Operations

OperationPurposeExample
add()Add one itemlist.add(item)
addAll()Add multiple itemslist.addAll(items)
remove()Remove an itemlist.remove(item)
contains()Check for an itemlist.contains(item)
where()Filter datalist.where(condition)
map()Transform datalist.map(transform)
sort()Sort datalist.sort()
forEach()Process each itemlist.forEach(...)
join()Combine valueslist.join(', ')
fold()Calculate one resultlist.fold(...)

34. Collection Immutability

In many application architectures, it is useful to avoid changing an existing collection directly. Instead, a new collection can be created from the existing data.

final original = [
  'Dart',
  'Flutter',
];

final updated = [
  ...original,
  'Firebase',
];

print(original);
print(updated);

This approach is especially useful when working with predictable state-management patterns.

35. Using final with Collections

final prevents the variable from being assigned to a different collection, but a mutable collection can still have its contents changed.

final List skills = [
  'Dart',
  'Flutter',
];

skills.add('Firebase');

The variable still refers to the same list, while the list's contents have changed.

36. Using const with Collections

A const collection is a compile-time constant and cannot be modified.

const List skills = [
  'Dart',
  'Flutter',
];

// skills.add('Firebase'); // Error

37. Efficient Collection Processing

When managing large collections, consider how much data must actually be processed and when operations are evaluated.

  • Use filtering to remove unnecessary data early.
  • Use map() when transforming elements.
  • Use Set when uniqueness is the main requirement.
  • Use Map when data needs to be retrieved by a unique key.
  • Use lazy Iterable operations where appropriate.
  • In Flutter, use builder widgets for large dynamic UI collections.
  • Avoid repeatedly performing expensive transformations inside frequently rebuilt widgets.

38. Common Mistakes When Managing Collections

  • Using a List when only unique values are required.
  • Using a Map when ordered indexed access is the main requirement.
  • Using dynamic everywhere instead of strongly typed collections.
  • Accessing a list index that does not exist.
  • Accessing first or last without checking whether the collection is empty.
  • Trying to modify a fixed-length or constant collection.
  • Forgetting to convert an Iterable to a List when a List is specifically required.
  • Creating very large collections unnecessarily.
  • Mixing unrelated types in collections when strong typing would make the code clearer.

39. Best Practices

  • Prefer collection literals when they clearly express the intended collection.
  • Use explicit generic types when they improve readability or type safety.
  • Choose List, Set, or Map according to the data access requirement.
  • Use model classes for complex application data.
  • Use where() for filtering and map() for transformation.
  • Use collection-if, collection-for, and spread operators to construct collections cleanly.
  • Check for empty collections before accessing elements such as first and last.
  • Keep collection-processing logic separate from UI code when the logic becomes complex.
  • Use appropriate Flutter builder widgets for large dynamic collections.

Dart's Effective Dart guidance recommends collection literals when possible and highlights collection features such as spreads, collection-if, and collection-for for concise collection construction. :contentReference[oaicite:8]{index=8}

40. Complete Practical Example

class Product {
  final int id;
  final String name;
  final String category;
  final double price;

  Product({
    required this.id,
    required this.name,
    required this.category,
    required this.price,
  });
}

void main() {
  List products = [
    Product(
      id: 1,
      name: 'Laptop',
      category: 'Electronics',
      price: 55000,
    ),
    Product(
      id: 2,
      name: 'Phone',
      category: 'Electronics',
      price: 25000,
    ),
    Product(
      id: 3,
      name: 'Chair',
      category: 'Furniture',
      price: 5000,
    ),
    Product(
      id: 4,
      name: 'Desk',
      category: 'Furniture',
      price: 10000,
    ),
  ];

  print('All Products:');

  for (var product in products) {
    print('${product.name} - ₹${product.price}');
  }

  var electronics = products
      .where((product) => product.category == 'Electronics')
      .toList();

  print('\nElectronics:');

  for (var product in electronics) {
    print(product.name);
  }

  var expensiveProducts = products
      .where((product) => product.price > 20000)
      .toList();

  print('\nExpensive Products:');

  for (var product in expensiveProducts) {
    print(product.name);
  }

  products.sort(
    (a, b) => a.price.compareTo(b.price),
  );

  print('\nProducts Sorted by Price:');

  for (var product in products) {
    print('${product.name}: ₹${product.price}');
  }

  double total = products.fold(
    0,
    (sum, product) => sum + product.price,
  );

  print('\nTotal Value: ₹$total');
}

41. Practice Exercises

  1. Create a List containing 10 student names and display them using a loop.
  2. Create a Set of programming languages and add five unique languages.
  3. Create a Map containing a student's name, age, course, and marks.
  4. Filter all numbers greater than 50 from a List.
  5. Remove duplicate values from a List.
  6. Sort a list of numbers in ascending and descending order.
  7. Create a List of Product objects and filter products by category.
  8. Create a Map where the key is a student name and the value is the student's marks.
  9. Calculate the total and average marks of a list.
  10. Create a Flutter ListView that displays data from a collection.
  11. Create a shopping cart using a List of Product objects.
  12. Use collection-if and collection-for to dynamically construct a list.

42. Quick Revision

  • Collection: A structure used to manage multiple values.
  • List: Ordered and index-based collection.
  • Set: Collection of unique values.
  • Map: Collection of key-value pairs.
  • Iterable: Provides common ways to process sequences of values.
  • where(): Filters collection data.
  • map(): Transforms collection data.
  • sort(): Organizes List elements.
  • contains(): Checks whether a value exists.
  • any(): Checks whether at least one element matches.
  • every(): Checks whether all elements match.
  • fold(): Combines collection elements into one result.
  • ...: Spread operator.
  • ...?: Null-aware spread operator.
  • collection-if: Conditionally adds collection elements.
  • collection-for: Generates collection elements using a loop.

43. Conclusion

Managing collections of data is fundamental to Dart and Flutter development. Lists are useful for ordered data, Sets are useful for unique values, and Maps are useful for key-value relationships. By combining these collections with filtering, transformation, sorting, searching, iteration, collection-if, collection-for, and spread operators, developers can build clean and flexible data-processing logic.

In Flutter, these techniques become especially useful when collections are connected to dynamic widgets, API data, forms, shopping carts, dashboards, lists, and other application features.

Official Dart Resources

Learn Flutter with JustAcademy

whatsapp