Lesson 19 min

Mixins & Extensions

00:00 / 00:00

Mixins — Horizontal Code Reuse

Dart has single inheritance, but sometimes you need to share behavior across classes that don't share a common ancestor. Mixins solve this without the fragility of multiple inheritance.

A mixin is a class that can be "mixed in" to another class, adding its methods and fields without creating an inheritance relationship.

dart
// Define a mixin mixin Logging { void log(String message) { print('[${runtimeType}] $message'); } void logError(String error) { print('[${runtimeType}] ERROR: $error'); } } mixin Cacheable { final Map<String, dynamic> _cache = {}; T? getFromCache<T>(String key) => _cache[key] as T?; void setCache(String key, dynamic value) => _cache[key] = value; void clearCache() => _cache.clear(); } // Mix them into any class class UserService with Logging, Cacheable { Future<User?> getUser(String id) async { final cached = getFromCache<User>(id); if (cached != null) { log('Cache hit for user $id'); return cached; } log('Fetching user $id from API...'); final user = await fetchFromApi(id); if (user != null) setCache(id, user); return user; } } class ProductService with Logging, Cacheable { // Gets the same logging and caching capabilities }

Restricting Mixins with on

dart
// This mixin can ONLY be applied to classes that extend Widget mixin AnimationMixin on State { late AnimationController _controller; // Can safely call State methods like setState here void startAnimation() { _controller.forward(); setState(() {}); } }

Extensions — Adding Methods to Existing Types

Extensions let you add methods to any type — including types you don't own, like String, int, or third-party classes:

dart
// Extend Dart's String type extension StringUtils on String { bool get isEmail => RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(this); String get capitalized => isEmpty ? this : '${this[0].toUpperCase()}${substring(1)}'; String truncate(int maxLength) => length <= maxLength ? this : '${substring(0, maxLength)}...'; } // Now use them naturally on any String final email = 'user@example.com'; print(email.isEmail); // true print('hello'.capitalized); // 'Hello' print('JavaShark Academy'.truncate(10)); // 'JavaShark ...' // Extend int extension IntUtils on int { Duration get seconds => Duration(seconds: this); Duration get milliseconds => Duration(milliseconds: this); bool get isEven => this % 2 == 0; } await Future.delayed(2.seconds); // So clean! await Future.delayed(500.milliseconds);

Extensions on Generic Types

dart
extension ListUtils<T> on List<T> { T? get firstOrNull => isEmpty ? null : first; T? get lastOrNull => isEmpty ? null : last; List<T> safeSublist(int start, [int? end]) { final s = start.clamp(0, length); final e = (end ?? length).clamp(s, length); return sublist(s, e); } } final items = <String>[]; print(items.firstOrNull); // null — no crash!

Summary

Mixins solve the code-reuse problem that multiple inheritance was meant to solve, but without the diamond-problem fragility. Extensions are one of Dart's most developer-friendly features — they let you write clean, expressive APIs on top of any type, making your codebase feel like the language was designed specifically for your domain.

WhatsApp