Question #347MediumDart BasicsImportant

How do mixins differ from interfaces in Dart?

#dart#mixins#interfaces

Answer

Overview

In Dart, mixins and interfaces (abstract classes used with

text
implements
) both enable code reuse and define contracts -- but they work very differently.


Interface (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
implements
.

dart
// 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

FeatureInterface (implements)Mixin (with)
Has implementationNo (abstract, must override all)Yes -- provides code
Must override allYesOnly abstract members
Can have fieldsYes (but must be overridden)Yes (shared state)
MultipleYes -- many implementsYes -- many with
Access
text
super
NoYes (via
text
on
clause)
PurposeDefine a contract/APIShare implementation
Keyword
text
implements
text
with

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 InterfaceUse Mixin
Define contract/API others must followShare implementation across unrelated classes
Dependency injection (swap implementations)Add cross-cutting behavior (logging, caching)
Programming to abstractionsDRY principle across class hierarchies

Quick Rule:

text
implements
= 'I will fulfill this contract'.
text
with
= 'I want these pre-built behaviors'.