Explain clearly about immutability vs mutability in flutter with some proper examples
#dart#flutter#immutability#mutability#const#final#state#widgets
Answer
Overview
Immutability means once an object is created, it cannot be changed. Mutability means the object can be modified after creation.
This concept is fundamental to Flutter — it directly affects how widgets rebuild, how state is managed, and how performance is optimized.
Immutable — Cannot Change After Creation
An immutable object's values are fixed forever once created. To "change" it, you must create a new object.
dart// String is immutable in Dart String name = 'Alice'; name = 'Bob'; // ❌ This does NOT modify 'Alice' // ✅ It creates a NEW String 'Bob' and points name to it // const makes the variable itself immutable too const pi = 3.14159; // pi = 3.15; // ❌ Compile error — const cannot be reassigned // final — reference is immutable (assigned once) final greeting = 'Hello'; // greeting = 'Hi'; // ❌ Error — final can only be set once
Immutable Class Example
dartclass User { final String name; // Cannot change after construction final int age; // Cannot change after construction const User({required this.name, required this.age}); // To "update", create a new object with copyWith User copyWith({String? name, int? age}) { return User( name: name ?? this.name, age: age ?? this.age, ); } } void main() { const user = User(name: 'Alice', age: 25); // user.name = 'Bob'; // ❌ Error — final field // ✅ Create a NEW User with changed name final updatedUser = user.copyWith(name: 'Bob'); print(user.name); // Alice (original unchanged) print(updatedUser.name); // Bob (new object) }
Mutable — Can Change After Creation
A mutable object's values can be modified directly without creating a new object.
dart// List is mutable by default List<int> numbers = [1, 2, 3]; numbers.add(4); // ✅ Modifies the SAME list numbers[0] = 10; // ✅ Modifies the SAME list print(numbers); // [10, 2, 3, 4] // Map is mutable by default Map<String, int> scores = {'Alice': 90}; scores['Bob'] = 85; // ✅ Modifies the SAME map // var allows reassignment var count = 0; count = 5; // ✅ Allowed — var is mutable
Mutable Class Example
dartclass MutableUser { String name; // No final — can be changed int age; // No final — can be changed MutableUser({required this.name, required this.age}); } void main() { final user = MutableUser(name: 'Alice', age: 25); user.name = 'Bob'; // ✅ Directly modifies the same object user.age = 30; // ✅ Directly modifies the same object print(user.name); // Bob }
Note:
means the reference is immutable (can't point to a different object), but the object's fields are still mutable if not declaredtextfinal user.textfinal
Immutability in Flutter Widgets
This is where immutability becomes critical. Flutter's entire widget system is built on immutability.
StatelessWidget — Fully Immutable
dartclass GreetingCard extends StatelessWidget { final String name; // All fields MUST be final final int age; // Cannot change after creation const GreetingCard({required this.name, required this.age}); Widget build(BuildContext context) { return Text('Hello, $name! Age: $age'); } } // Usage GreetingCard(name: 'Alice', age: 25); // To change name → Flutter creates a NEW GreetingCard widget // The old one is discarded and replaced
StatefulWidget — Widget is Immutable, State is Mutable
dartclass Counter extends StatefulWidget { final String title; // ✅ Immutable — widget config never changes const Counter({required this.title}); _CounterState createState() => _CounterState(); } class _CounterState extends State<Counter> { int count = 0; // ✅ Mutable — state CAN change void increment() { setState(() { count++; // Mutating state triggers rebuild }); } Widget build(BuildContext context) { return Column( children: [ Text('${widget.title}: $count'), ElevatedButton(onPressed: increment, child: Text('Add')), ], ); } }
Key Insight: The
class itself is immutable (all fields aretextStatefulWidget). Only the separatetextfinalobject is mutable.textState
Making Collections Immutable
dart// ❌ Mutable list — can be modified var mutableList = [1, 2, 3]; mutableList.add(4); // ✅ Works // ✅ Immutable list with const const immutableList = [1, 2, 3]; // immutableList.add(4); // ❌ Runtime error — cannot modify const list // ✅ Unmodifiable list (runtime immutability) final unmodifiable = List.unmodifiable([1, 2, 3]); // unmodifiable.add(4); // ❌ Runtime error — Unsupported operation // ✅ Immutable map const config = {'theme': 'dark', 'lang': 'en'}; // config['theme'] = 'light'; // ❌ Runtime error
Why Flutter Prefers Immutability
1. Performance — textconst
Widgets Are Cached
text
constdart// ✅ Flutter reuses the SAME instance — no rebuild needed const SizedBox(height: 16); const Text('Hello World'); const Icon(Icons.star, color: Colors.yellow); // ❌ Without const — new instance created every build SizedBox(height: 16); // New object each time parent rebuilds
2. Predictable State — No Accidental Mutations
dart// ❌ Dangerous — mutable state shared by reference class AppState { List<String> items = []; // Mutable } final state = AppState(); final reference = state.items; // Same list! reference.add('oops'); // Accidentally modified state! print(state.items); // ['oops'] — state corrupted // ✅ Safe — immutable state prevents accidental changes class SafeState { final List<String> items; const SafeState({required this.items}); SafeState addItem(String item) { return SafeState(items: [...items, item]); // New list, new state } }
3. Easier Comparison — Widget Rebuild Optimization
dart// Flutter compares old widget vs new widget // Immutable objects with == override make this fast and reliable class UserState { final String name; final int age; const UserState({required this.name, required this.age}); bool operator ==(Object other) => other is UserState && name == other.name && age == other.age; int get hashCode => Object.hash(name, age); } // BLoC/Riverpod can skip rebuilds when state hasn't changed // oldState == newState → skip rebuild → better performance
Comparison Table
| Aspect | Immutable | Mutable |
|---|---|---|
| Can change after creation? | No | Yes |
| Dart keywords | text text | text |
| How to "update" | Create new object ( text | Modify directly |
| Thread safety | Safe (no race conditions) | Risky (shared mutations) |
| Performance | Better ( text | More memory-efficient for frequent changes |
| Predictability | High (no surprise changes) | Low (hard to track mutations) |
| Flutter widgets | All widgets are immutable | Only text |
| State management | BLoC, Riverpod prefer immutable states | text |
Quick Reference
dart// Immutable variables const name = 'Alice'; // Compile-time constant final age = 25; // Runtime constant (set once) // Immutable class fields class User { final String name; // Cannot reassign const User(this.name); // const constructor } // Mutable variables var count = 0; // Can reassign int total = 100; // Can reassign // Mutable class fields class Cart { List<String> items = []; // Can modify list contents int quantity = 0; // Can reassign }
Best Practice: Default to immutable. Use
for all fields,textfinalconstructors for widgets, andtextconstfor updates. Only make something mutable when you have a clear reason (liketextcopyWithintextState).textStatefulWidget