Unlocking the Power of Generics in Dart: Using List, Set, Map, and Queue Collections
Tags: flutter, flutterdev, dart
Published: 2023-03-10 00:07:33

Generics are a powerful feature of the Dart language that allow us to create collections that can only hold a single type of data. This ensures type safety and prevents us from accidentally adding the wrong type of data to a collection. Generics can be used with List, Set, Map, and Queue collections.

For example, a List<String> can only hold strings, a Set<int> can only hold integers, a Map<String, int> can only hold key-value pairs where the key is a string and the value is an integer, and a Queue<String> can only hold strings.

Using generics also allows us to easily access the data stored in a collection. For example, if we have a List<String> we know that all of the elements in the list will be strings and we can easily access them without having to worry about type errors. Similarly, if we have a Map<String, int> we know that all of the keys will be strings and all of the values will be integers and we can easily access them without having to worry about type errors.

## 1. 𝗚𝗲𝗻𝗲𝗿𝗶𝗰 𝗟𝗶𝘀𝘁
In Dart, a List is simply an ordered group of objects. A list is simple an implementation of an array.
Example:
```
List<T> list = new List<String>();
list.add("John");
list.add("Mary");
list.add("Bob");
```

## 2. 𝗚𝗲𝗻𝗲𝗿𝗶𝗰 Set
In Dart, a Set represents a collection of objects in which each object can exist only once.
Example:
```
Set<T> set = new Set<Product>();
set.add(new Product("Shirt"));
set.add(new Product("Pants"));
set.add(new Product("Hat"));
```

## 3. 𝗚𝗲𝗻𝗲𝗿𝗶𝗰 Map
In Dart, Map is a dynamic collection of the key, value pairs.
Example:
```
Map<K, V> map = new Map<String, Customer>();
map["John"] = new Customer("John", 25);
map["Mary"] = new Customer("Mary", 30);
map["Bob"] = new Customer("Bob", 28);
```

## 4. 𝗚𝗲𝗻𝗲𝗿𝗶𝗰 𝗤𝘂𝗲𝘂𝗲
A queue is a collection that is used when the data is to be inserted in a FIFO (First in first out) manner. In Dat, a queue can be manipulated at both ends, i.e. at the start as well as the end of the queue.
Example:
```
Queue<T> queue = new Queue<Order>();
queue.add(new Order("Red Shirt"));
queue.add(new Order("Green Pants"));
queue.add(new Order("Blue Hat"));
```

## 5. 𝗚𝗲𝗻𝗲𝗿𝗶𝗰 Class & 𝗚𝗲𝗻𝗲𝗿𝗶𝗰 Functions

Generics can also be used to create custom classes and functions. This allows us to create classes and functions that can be used with any type of data.
Example:
```
// 𝗚𝗲𝗻𝗲𝗿𝗶𝗰 Class
class MyClass<T> { 
T data;
 MyClass(this.data);
   void printData() {
     print(data); } 
} 

// 𝗚𝗲𝗻𝗲𝗿𝗶𝗰 Functions
void myFunction<T>(T data) {
   print(data); 
}
```
## 𝗚𝗲𝗻𝗲𝗿𝗶𝗰 Example In Flutter
In below example, we have created a generic class called MyClass that can be used to store any type of data.
We have also created a object called WidgetListItem that takes a MyClass object and displays the data stored in it.
And we have used generics to ensure that the data stored in the MyClass object is of the correct type and that the WidgetListItem object can be used with any type of data.

```
import 'package:flutter/material.dart';

class MyClass<T,K> {
  Map<T,K>  data;
  Map<String,String>? type;

  MyClass(this.data, [this.type]) {
//The "type" variable is used to show "runtimeType" of the "MyClass" variables.
    type = {"${data.entries.first.key.runtimeType}":"${data.entries.first.value.runtimeType}"};
  }
}

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // We defines 3 object from generic classes
    MyClass universityName_term = MyClass<String,int>({"CentralUniversity":3});
    MyClass salary_tax = MyClass<int,double>({1000:0.09});
    MyClass firstName_lastName = MyClass<String,String>({"Esmaeil":"Ahmadipour"});

    return MaterialApp(
      title: 'Generics Demo',
      home: Scaffold(
        appBar: AppBar(title: const Text('Using Generics')),
        body: Center(
          child: Padding(
              padding: const EdgeInsets.all(48.0),
              child:Column(
                children: [
                  const WidgetListTitle(),
                  WidgetListItem(myClass: universityName_term,color: Colors.red.shade300),
                  WidgetListItem(myClass: salary_tax,color: Colors.green.shade300),
                  WidgetListItem(myClass: firstName_lastName,color: Colors.blue.shade300),
                ],
              )
          ),
        ),
      ),
    );
  }
}

class WidgetListTitle extends StatelessWidget {
  const WidgetListTitle({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: const [
        Text('Map Side'),
        Text('Side Value'),
        Text('ValueType'),
      ],
    );
  }
}

class WidgetListItem extends StatelessWidget {
  const WidgetListItem({
    Key? key,
    required this.myClass, required this.color,
  }) : super(key: key);

  final MyClass myClass;
  final Color color;

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(8.0),
      color: color,
      child: Column(
        children: <Widget>[

          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              const Text('Kay:'),
              Text('${myClass.data.entries.first.key}'),
              Text(myClass.type!.entries.first.key),
            ],
          ),
          const Divider(),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              const Text('Value:'),
              Text('${myClass.data.entries.first.value}'),
              Text(myClass.type!.entries.first.value),
            ],
          ),
        ],
      ),
    );
  }
}
```