How do mixins differ from interfaces in Dart?
#dart#mixins#interfaces
Answer
Overview
In Dart, mixins and interfaces (abstract classes used with
text
implementsInterface (Abstract Class + implements)
An interface defines a contract -- a set of method signatures that implementing classes MUST provide. In Dart, interfaces are just abstract classes used with
text
implementsdart// Interface definition abstract class Drawable { void draw(); // Must be implemented void resize(); // Must be implemented } abstract class Serializable { String toJson(); // Must be implemented void fromJson(Map m); // Must be implemented } // Implementing multiple interfaces class Widget implements Drawable, Serializable { void draw() => print('Drawing widget'); void resize() => print('Resizing widget'); String toJson() => '{...}'; void fromJson(Map m) { ... } // Must implement ALL methods from BOTH interfaces }
Mixin -- Reusable Implementation Block
A mixin provides actual implementation that can be added to a class without inheritance. It's for sharing behavior rather than defining contracts.
dart// Mixin definition -- has implementation mixin Loggable { final _log = <String>[]; void log(String message) { _log.add('${DateTime.now()}: $message'); print(message); } List<String> get history => List.unmodifiable(_log); } mixin Cacheable { final _cache = <String, dynamic>{}; void cache(String key, dynamic value) => _cache[key] = value; T? getFromCache<T>(String key) => _cache[key] as T?; void clearCache() => _cache.clear(); } // Use with 'with' class UserService with Loggable, Cacheable { Future<User> getUser(int id) async { final cached = getFromCache<User>('user_$id'); // From Cacheable if (cached != null) return cached; log('Fetching user $id from API'); // From Loggable final user = await api.fetchUser(id); cache('user_$id', user); // From Cacheable return user; } }
Key Differences
| Feature | Interface (implements) | Mixin (with) |
|---|---|---|
| Has implementation | No (abstract, must override all) | Yes -- provides code |
| Must override all | Yes | Only abstract members |
| Can have fields | Yes (but must be overridden) | Yes (shared state) |
| Multiple | Yes -- many implements | Yes -- many with |
| Access text | No | Yes (via text |
| Purpose | Define a contract/API | Share implementation |
| Keyword | text | text |
Mixin with on Clause (Constrained Mixin)
dart// Mixin that can only be used with StatefulWidget mixin AutoDisposeStateMixin<T extends StatefulWidget> on State<T> { final _disposables = <Function>[]; void addDisposable(Function fn) => _disposables.add(fn); void dispose() { for (final fn in _disposables) fn(); super.dispose(); // Can call super because 'on State<T>' } } class MyScreen extends StatefulWidget { ... } class _MyScreenState extends State<MyScreen> with AutoDisposeStateMixin { // dispose() automatically calls all disposed functions }
When to Use Which
| Use Interface | Use Mixin |
|---|---|
| Define contract/API others must follow | Share implementation across unrelated classes |
| Dependency injection (swap implementations) | Add cross-cutting behavior (logging, caching) |
| Programming to abstractions | DRY principle across class hierarchies |
Quick Rule:
= 'I will fulfill this contract'.textimplements= 'I want these pre-built behaviors'.textwith