Question #104EasyDart BasicsImportant

Difference between dynamic and flutter generics ?

#flutter

Answer

Overview

text
dynamic
and Generics both enable working with different types, but in fundamentally different ways — one sacrifices type safety, the other preserves it.


dynamic — No Type Safety

text
dynamic
tells Dart to skip type checking entirely. Errors surface at runtime.

dart
dynamic value = 42;
value = 'hello';    // ✅ No compile error
value = [1, 2, 3]; // ✅ No compile error

// NO compile-time protection
dynamic list = [1, 2, 3];
list.nonExistent(); // ❌ Runtime error — NoSuchMethodError
int x = list;       // ❌ Runtime error — type mismatch

Generics — Type-Safe Reusability

Generics let you write one implementation that works for many types while maintaining full compile-time type safety.

dart
// Generic class — T is a type parameter
class Box<T> {
  T value;
  Box(this.value);

  T getValue() => value;
  void setValue(T newValue) { value = newValue; }
}

void main() {
  final intBox = Box<int>(42);
  intBox.setValue(100);    // ✅ Only int allowed
  // intBox.setValue('hi'); // ❌ Compile error

  final strBox = Box<String>('hello');
  strBox.setValue('world'); // ✅

  // Type inference
  final autoBox = Box(3.14); // Box<double> inferred
}

Generic Functions

dart
// Generic function
T getFirst<T>(List<T> items) {
  if (items.isEmpty) throw StateError('Empty list');
  return items.first; // Returns T, not dynamic
}

// Usage
final first = getFirst([1, 2, 3]);       // int
final name = getFirst(['Alice', 'Bob']); // String

Generic Constraints

dart
// Constrain T to only numeric types
T add<T extends num>(T a, T b) => (a + b) as T;

add(3, 4);       // ✅ int
add(1.5, 2.5);   // ✅ double
// add('a', 'b');  // ❌ String doesn't extend num

In Flutter — Common Generic Usage

dart
// BLoC — Bloc<Event, State>
class CounterBloc extends Bloc<CounterEvent, int> { ... }

// Riverpod — Provider<T>
final counterProvider = StateNotifierProvider<CounterNotifier, int>(...);

// Future and Stream
Future<List<User>> fetchUsers() async { ... }
Stream<String> kafkaStream() async* { ... }

// Map and List
Map<String, int> scores = {};
List<Widget> widgets = [];

Comparison

Feature
text
dynamic
Generics
Type checkingRuntimeCompile-time
IDE support❌ Limited✅ Full autocomplete
Safety❌ No✅ Yes
Reusability✅ Always✅ With type params
PerformanceSlightly slower✅ No overhead

Rule: Use Generics over

text
dynamic
wherever possible. Generics give you flexibility AND type safety.
text
dynamic
only when truly dealing with unknown runtime types (like raw JSON parsing).