When can Dart's mixins become helpful in Flutter
Tags: dart, flutter, design
Published: 2021-10-04 21:08:41

The Dart mixin can be a powerful tool in your projects. But without proper care, it can lead to unnecessary, un-scalable complexity. 

So when should a mixin be used? According to [Dart](https://dart.dev/guides/language/language-tour#adding-features-to-a-class-mixins):

> Mixins are a way of reusing a class’s code in multiple class hierarchies.

## Navigation & Routes

Let's go through an example where we can move a projects navigation to a mixin. Here we have two simple pages: `HomePage` and `DetailsPage`:

```dart
class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home Page')),
      body: Center(
        child: TextButton(
          child: Text('DETAILS PAGE'),
          onPressed: () => Navigator.push(
            context,
            MaterialPageRoute(builder: (_) => DetailsPage()),
          ),
        ),
      ),
    );
  }
}
```

![home page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vxrj4h9cnh7dafc9f1n8.png)
 

```dart
class DetailsPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Details Page')),
      body: Center(
        child: TextButton(
          onPressed: Navigator.of(context).pop,
          child: Text('GO BACK'),
        ),
      ),
    );
  }
}
```

![details page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zqudtmos0yli95or4141.png)
 
At this time we are pushing the `DetailsPage` as most probably would using the `Navigator.push` method. But what happens when have _many_ different places the page is being pushed from? This leads to re-writing the same push call many times.

```dart
Navigator.push(
  context,
  MaterialPageRoute(builder: (_) => DetailsPage()),
)
```

But this can be moved into a mixin and can be added to our `HomePage` class!

```dart
mixin MyRoutesMixin on Widget {
  Future<T?> pushDetailsPage<T extends Object?>(BuildContext context) =>
      Navigator.push<T>(
        context,
        MaterialPageRoute(builder: (_) => DetailsPage()),
      );
}
```
```dart
class HomePage extends StatelessWidget with MyRoutesMixin {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home Page')),
      body: Center(
        child: TextButton(
          child: Text('DETAILS PAGE'),
          onPressed: () => pushDetailsPage(context),
        ),
      ),
    );
  }
}
```

Let's continue building off of this example.

## Logging & Telemetry

Let's say you now have a logger that needs to track when a button is pressed. And your Logger class looks like this (pretend that it's _actually_ sending data to an endpoint on the `log()` method):

```dart
class Logger {
  const Logger();
  void log(String label, DateTime dateTime) {
    print('PRESSED: $label at $dateTime');
    // TODO: complete the logger
  }
}
```

But you shouldn't have to create a new Logger instance each time you need to use it in a widget. So let's do that in a mixin:

```dart
mixin LoggerMixin {
  static const _logger = Logger();
  void logButtonPressed(String label) => _logger.log(label, DateTime.now());
}
```

Let's add it to our DetailsPage widget and test it with a new button.

```dart
class DetailsPage extends StatelessWidget with LoggerMixin {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Details Page')),
      body: Center(
        child: Column(
          children: [
            TextButton(
              onPressed: Navigator.of(context).pop,
              child: Text('GO BACK'),
            ),
            TextButton(
              // Using the new mixin here
              onPressed: () => logButtonPressed('EXAMPLE'),
              child: Text('EXAMPLE'),
            ),
          ],
        ),
      ),
    );
  }
}
```

![details page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tx9bwes51lvnzx27xofv.png)

## Common Strings

Sometimes you will need something as simple as text and other data accessible to your widgets. A mixin be the perfect example for that. Let's move all of our "commonly" used `String`s into a mixin.

```dart
mixin MyApplicationStrings {
  final example = 'EXAMPLE';
  final goBack = 'GO BACK';
  final detailsPage = 'Details Page';
  final homePage = 'Home Page';
}
```
```dart
class HomePage extends StatelessWidget
    with MyRoutesMixin, MyApplicationStrings {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(homePage)),
      body: Center(
        child: TextButton(
          child: Text(detailsPage.toUpperCase()),
          onPressed: () => pushDetailsPage(context),
        ),
      ),
    );
  }
}
```
```dart
class DetailsPage extends StatelessWidget
    with LoggerMixin, MyApplicationStrings {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(detailsPage)),
      body: Center(
        child: Column(
          children: [
            TextButton(
              onPressed: Navigator.of(context).pop,
              child: Text(goBack),
            ),
            TextButton(
              onPressed: () => logButtonPressed(example),
              child: Text(example),
            ),
          ],
        ),
      ),
    );
  }
}
```

## Conclusion

_All of the examples provided can be found here on [DartPad](https://dartpad.dev/?id=9866c1a7e973ae638749a75923db48fd&null_safety=true)._

This is not a perfect example nor use case of each item shown. They are only created to introduce you to how mixins _could_ be used. In fact, there are many more excellent ways to use them. Without careful use, they can become a hot mess.