Elijah L.
Posted on October 4, 2021
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:
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
:
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()),
),
),
),
);
}
}
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'),
),
),
);
}
}
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.
Navigator.push(
context,
MaterialPageRoute(builder: (_) => DetailsPage()),
)
But this can be moved into a mixin and can be added to our HomePage
class!
mixin MyRoutesMixin on Widget {
Future<T?> pushDetailsPage<T extends Object?>(BuildContext context) =>
Navigator.push<T>(
context,
MaterialPageRoute(builder: (_) => DetailsPage()),
);
}
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):
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:
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.
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'),
),
],
),
),
);
}
}
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.
mixin MyApplicationStrings {
final example = 'EXAMPLE';
final goBack = 'GO BACK';
final detailsPage = 'Details Page';
final homePage = 'Home Page';
}
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),
),
),
);
}
}
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.
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.
Posted on October 4, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.