One of the difficulties I’ve encountered with Dart is the restriction on only inheriting or extending from a single class at a time. You can get around this constraint, especially in more complex architectures, by using Dart mixins to improve class inheritance and code reusability.
When it comes to class reusability, Dart has a lot to offer, most especially since the introduction of mixin keyword in Dart 2.1. Reusing code from any class across multiple class hierarchies is made easy with Dart mixins.
In this tutorial, we’ll learn more about mixins in general, look at Dart mixins, and learn about avoiding duplicate methods in Dart classes.
Here is what we will cover:
To follow along with this article, you should have a basic understanding of Dart. I’ve used dartpad.dev to write the example programs in this tutorial.
Dart mixins are special classes beginning with the keyword mixin
that contain a collection of methods that other classes can use. Dart mixins encourage code reuse and help you avoid the limitations that come with multiple inheritance. It allows you to add extra features to classes in a way that standard class inheritance doesn’t allow.
Let consider role assignment in a typical ecommerce application:
void main() { Moderator().viewAllProducts(); } class User { void viewAllProducts() { print('Viewed all products'); } void purchaseProduct() { print('Purchased a designer bag'); } } class Vendor { void createStore() { print('Created EA sports stores'); } void deleteStore() { print('Delete EA sports stores'); } } class Moderator { void approveStore() { print('Approved EA sports stores'); } void viewAllProducts() { print('Viewed all products'); } }
The methods in each class are pretty self-explanatory. Looking closely at the code snippet, you’ll notice a method duplication (viewAllProducts
) in both the User
and Moderator
classes. Although this works, it isn’t a good practice.
Although Flutter mixins reduce the drawbacks encountered with inheritance, it cannot replace the inheritance because it has its own drawbacks.
The excessive use of mixins such as class Vendor
with CanViewAllProducts,CanRemoveProduct,...
violates the principle of single responsibility, making it difficult to understand the class purpose. Furthermore, at runtime, code execution tends to jump around in different mixins, making debugging and following the order of execution difficult.
Also, using mixins can result in long compile times. Beware of the risks before using mixins and you’ll be more likely to enjoy their benefits.
In order to avoid the code duplication, we’d take advantage of Dart class inheritance.
In Dart, inheritance is the process by which one class derives its properties and methods from another class.
Let’s remove the viewAllProducts
method duplication and allow the Moderator
class to derive the viewAllProducts
method from the User
class:
void main() { Moderator().viewAllProducts(); } class User { void viewAllProducts() { print('Viewed all products'); } void purchaseProduct() { print('Purchased a designer bag'); } } class Moderator extends User { void approveStore() { print('Approved EA sports stores'); } }
Executing the above snippet on dartpad.dev, you should have the same result as the previous snippet with a clean code.
This works fine, but then there is an issue with inheritance that we’ll address moving forward. Have you noticed that our Moderator
class also derived the purchaseProduct
method from the User
class, which is quite unnecessary since we want the Moderator
to derive only the viewAllProducts
from the User
class? This is where Dart mixins come in handy.
Multilevel inheritance occurs when a class inherits another child class. Dart allows a class to inherit methods and properties from another child class.
Let’s consider the following snippet:
void main() { Vendor().viewAllProducts(); Vendor().purchaseProducts(); Vendor().approveStore(); Vendor().createStore(); Vendor().deleteStore(); } class User { void viewAllProducts() { print('Viewed all products'); } void purchaseProducts() { print('Purchased 2 designer bags'); } } class Moderator extends User { void approveStore() { print('Approved EA sports stores'); } } class Vendor extends Moderator { void createStore() { print('Created EA sports stores'); } void deleteStore() { print('Delete EA sports stores'); } }
Here, the Vendor
class inherits the approveStore
method from the Moderator
class. Since the Moderator
class is a child class to the User
class, the viewAllProducts
and purchaseProducts
methods inherited from its parent the User
class is passed to the Vendor
class (its child class).
When a class inherits from more than one parent class, this is known as multiple inheritance.
Let’s consider a fourth type of user Admin
with the following methods:
approveStore
deleteStore
viewAllProducts
You may think of avoiding code duplication by allowing the Admin
class to inherit the above methods from the Moderator
and Vendor
classes. Unfortunately, Dart doesn’t support multiple inheritance, which means a class cannot extend more than one class. So, we turn to Dart mixins.
To create a mixin, we’ll take advantage of the mixin
keyword:
mixin MixinName { //Define reusable methods }
Let’s address the issue with inheritance we mentioned earlier, where our Moderator
class also derived the purchaseProduct
method from the User
class, but we want the Moderator
to derive only the viewAllProducts
from the User
class:
mixin CanViewAllProducts { void viewAllProducts() { print('Viewed all products'); } }
To use CanViewAllProducts
mixins, we’ll take advantage of the with
keyword as follows:
class Moderator with CanViewAllProducts { void approveStore() { print('Approved EA sports stores'); } }
Now, our Moderator
class can access viewAllProducts
method via the canViewAllProducts
mixin without inheriting unnecessary methods. CanViewAllProducts
mixins can be reused across multiple classes that require its methods.
Dart allows us to combine inheritance with mixins as follows:
mixin CanViewAllProducts { void viewAllProducts() { print('Viewed all products'); } } class User { void purchaseProducts() { print('Purchased 2 designer bags'); } } class Vendor extends User with CanViewAllProducts { void createStore() { print('Created EA sports stores'); } }
Dart also allows a class to use more than one mixin by seperating each mixins with comma.
Let’s see an example:
mixin CanRemoveProduct { void removeProduct() { print('Remove product'); } } class Vendor extends User with CanViewAllProducts,CanRemoveProduct { void createStore() { print('Created EA sports stores'); } }
Dart mixins are useful when developing applications that will likely grow in complexity. When developing complex Dart applications, you may see the need to extend multiple classes at the same time which is not supported in Dart.
Dart mixins can help you overcome the limitations of only inheriting or extending from a single class at a time as well as improve class inheritance and code reusability. For more developer content, you may follow me @5x_dev on Twitter.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to balance vibrant visuals with accessible, user-centered options like media queries, syntax, and minimized data use.
Learn how to implement one-way and two-way data binding in Vue.js, using v-model and advanced techniques like defineModel for better apps.
Compare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.