Dart Lesson 29 – Generics | Dataplexa

Generics in Dart

In this lesson, you will learn about Generics in Dart. Generics allow you to write flexible, reusable, and type-safe code.

They are heavily used in collections, APIs, frameworks, and real-world applications.


Why Generics Are Needed

Without generics, Dart code can become unsafe and error-prone. Let’s understand this with a simple example.

List values = [10, "Dart", true];
values.add(3.14);

This list can hold any data type, which makes it dangerous. You may accidentally insert incorrect values.


Type Safety Problem

If you expect only numbers but accidentally store strings, your program may crash at runtime.

Generics solve this problem by enforcing types at compile time.


Using Generics with Collections

Dart collections like List, Set, and Map support generics.

List scores = [85, 90, 78, 92];
scores.add(88);

Now this list can store only integers.


Real-World Example: Student Marks

Let’s store marks of students using generics.

List marks = [72.5, 88.0, 91.25];
double average = (marks[0] + marks[1] + marks[2]) / marks.length;
print(average);

This ensures that only numerical values are processed.


Generics with Map

Maps also support generics for key-value pairs.

Map productStock = {
  "Laptop": 25,
  "Mobile": 40,
  "Tablet": 15
};

Here, keys must be strings and values must be integers.


Creating Generic Classes

You can create your own classes using generics. This makes them reusable for multiple data types.

class Box {
  T value;

  Box(this.value);

  T getValue() {
    return value;
  }
}

Using a Generic Class

void main() {
  Box numberBox = Box(100);
  Box textBox = Box("Dataplexa");

  print(numberBox.getValue());
  print(textBox.getValue());
}

The same class works with different data types safely.


Real-World Use Case: API Response Wrapper

Generics are commonly used when handling API responses.

class ApiResponse {
  T data;
  bool success;

  ApiResponse(this.data, this.success);
}

Using API Response Class

ApiResponse response =
    ApiResponse("User created successfully", true);

print(response.data);
print(response.success);

Generic Functions

Functions can also use generics.

T getFirstElement(List items) {
  return items[0];
}

Using Generic Function

void main() {
  print(getFirstElement([1, 2, 3]));
  print(getFirstElement(["Dart", "Flutter", "Web"]));
}

Restricting Generic Types

You can restrict generic types using constraints.

class NumberBox {
  T value;
  NumberBox(this.value);

  T doubleValue() {
    return (value + value) as T;
  }
}

Now only numeric types are allowed.


Benefits of Generics

  • Compile-time type safety
  • Reusable and clean code
  • Better readability
  • Fewer runtime errors

📝 Practice Exercises


Exercise 1

Create a generic class Storage<T> that stores a value.

Exercise 2

Use it with both int and String.

Exercise 3

Create a generic function that returns the last element of a list.


✅ Practice Answers


class Storage {
  T value;
  Storage(this.value);

  T getValue() => value;
}

T getLast(List items) {
  return items[items.length - 1];
}

void main() {
  Storage s1 = Storage(500);
  Storage s2 = Storage("Dart Generics");

  print(s1.getValue());
  print(s2.getValue());

  print(getLast([10, 20, 30]));
}

What’s Next?

In the next lesson, you will learn about Asynchronous Programming in Dart, including how Dart handles tasks that take time to complete.