Answer
Overview
Mixins in Dart allow you to reuse code across multiple class hierarchies without inheritance. A mixin is a class whose methods can be used by other classes without being a parent class.
What is a Mixin?
A mixin is a way to add functionality to classes without using traditional inheritance.
dart// Traditional inheritance - limited to one parent class Animal {} class Dog extends Animal {} // ✅ class Cat extends Animal {} // ✅ class RobotDog extends Animal, Robot {} // ❌ Multiple inheritance not allowed // With mixins - can add multiple behaviors class Dog extends Animal with CanBark, CanRun {} // ✅ class RobotDog extends Robot with CanBark, CanRun {} // ✅
Syntax
Define a Mixin
dartmixin CanFly { void fly() { print('Flying!'); } double get altitude => 100.0; } mixin CanSwim { void swim() { print('Swimming!'); } }
Use a Mixin
dartclass Duck extends Animal with CanFly, CanSwim { void quack() { print('Quack!'); } } void main() { final duck = Duck(); duck.fly(); // From CanFly mixin duck.swim(); // From CanSwim mixin duck.quack(); // From Duck class }
Mixin vs Class
dart// Regular class - can be extended AND used as mixin class Flyable { void fly() => print('Flying'); } // Pure mixin - cannot be extended, only mixed in mixin Swimmable { void swim() => print('Swimming'); } // Usage class Bird extends Animal with Flyable {} // ✅ class Fish extends Animal with Swimmable {} // ✅ class Eagle extends Flyable {} // ✅ Can extend class class Shark extends Swimmable {} // ❌ Cannot extend mixin
Mixin with 'on' Keyword
Restrict which classes can use the mixin.
dartclass Animal { String name; Animal(this.name); } // This mixin can only be used with Animal or its subclasses mixin CanBark on Animal { void bark() { print('$name says: Woof!'); // Can access Animal's properties } } class Dog extends Animal with CanBark { Dog(String name) : super(name); } class Robot with CanBark {} // ❌ Error: Robot is not Animal
Flutter Use Cases
1. Ticker Mixin (Animations)
dartimport 'package:flutter/material.dart'; class AnimatedScreen extends StatefulWidget { _AnimatedScreenState createState() => _AnimatedScreenState(); } class _AnimatedScreenState extends State<AnimatedScreen> with SingleTickerProviderStateMixin { // Mixin for animation late AnimationController _controller; void initState() { super.initState(); _controller = AnimationController( vsync: this, // 'this' provides Ticker from mixin duration: Duration(seconds: 2), )..repeat(); } void dispose() { _controller.dispose(); super.dispose(); } Widget build(BuildContext context) { return RotationTransition( turns: _controller, child: FlutterLogo(size: 100), ); } }
2. Multiple Animations
dartclass _MyWidgetState extends State<MyWidget> with TickerProviderStateMixin { // Multiple tickers late AnimationController _controller1; late AnimationController _controller2; void initState() { super.initState(); _controller1 = AnimationController(vsync: this, duration: Duration(seconds: 1)); _controller2 = AnimationController(vsync: this, duration: Duration(seconds: 2)); } }
3. AutomaticKeepAlive (TabBar/PageView)
dartclass TabContent extends StatefulWidget { _TabContentState createState() => _TabContentState(); } class _TabContentState extends State<TabContent> with AutomaticKeepAliveClientMixin { // Keeps state alive bool get wantKeepAlive => true; // Prevent disposal Widget build(BuildContext context) { super.build(context); // Must call when using AutomaticKeepAliveClientMixin return ListView.builder( itemCount: 100, itemBuilder: (context, index) => ListTile(title: Text('Item $index')), ); } }
4. WidgetsBinding Observer
dartclass _AppState extends State<App> with WidgetsBindingObserver { void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.paused) { print('App paused'); } else if (state == AppLifecycleState.resumed) { print('App resumed'); } } Widget build(BuildContext context) => Container(); }
Custom Mixins
Example: Logger Mixin
dartmixin LoggerMixin { void log(String message) { print('[${runtimeType}] $message'); } void logError(String error) { print('[${runtimeType}] ERROR: $error'); } } class UserService with LoggerMixin { void fetchUser() { log('Fetching user...'); // API call log('User fetched successfully'); } } class ProductService with LoggerMixin { void fetchProducts() { log('Fetching products...'); logError('Network error'); } }
Example: Validation Mixin
dartmixin ValidationMixin { bool isValidEmail(String email) { return RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(email); } bool isValidPhone(String phone) { return RegExp(r'^\d{10}$').hasMatch(phone); } bool isNotEmpty(String? value) { return value != null && value.isNotEmpty; } } class RegistrationForm extends StatefulWidget { _RegistrationFormState createState() => _RegistrationFormState(); } class _RegistrationFormState extends State<RegistrationForm> with ValidationMixin { String? validateEmail(String? value) { if (!isNotEmpty(value)) return 'Email required'; if (!isValidEmail(value!)) return 'Invalid email'; return null; } Widget build(BuildContext context) { return TextFormField( validator: validateEmail, decoration: InputDecoration(labelText: 'Email'), ); } }
Multiple Mixins
dartmixin CanWalk { void walk() => print('Walking'); } mixin CanRun { void run() => print('Running'); } mixin CanJump { void jump() => print('Jumping'); } class Human with CanWalk, CanRun, CanJump { void moveAround() { walk(); run(); jump(); } }
Mixin Order Matters
dartmixin A { void greet() => print('A'); } mixin B { void greet() => print('B'); } class MyClass with A, B {} // B's greet() wins (last one) void main() { MyClass().greet(); // Prints: B }
Best Practices
Important: Use mixins for behavior, not state
✅ Do
dart// Good - Behavior mixin mixin Logger { void log(String msg) => print(msg); } // Good - Single responsibility mixin NetworkMixin { Future<void> fetchData() async {} }
❌ Don't
dart// Bad - State in mixin (use composition instead) mixin UserData { String userName = ''; // Avoid state int userAge = 0; }
Prefer Composition Over Mixins for State
dart// ✅ Better - Use composition for state class UserData { String userName; int userAge; UserData(this.userName, this.userAge); } class UserScreen { final UserData data; UserScreen(this.data); }
Common Flutter Mixins
| Mixin | Purpose |
|---|---|
text | Single animation |
text | Multiple animations |
text | Keep state in TabBar/PageView |
text | App lifecycle events |
text | Route navigation awareness |