Emmanuel John I'm a full-stack software developer, mentor, and writer. I am an open source enthusiast. In my spare time, I enjoy watching sci-fi movies and cheering for Arsenal FC.

Advanced uses of Dart mixins: Avoiding duplicate methods

3 min read 1070

Dart Logo

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.

What are Dart mixins?

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.

Drawbacks of Dart mixins

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.

Avoiding duplicate methods in Dart classes

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 in Dart

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');
  }
}

User Moderator Vendor

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).

Multiple inheritance in Dart

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.

Creating mixins with Dart

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.


More great articles from LogRocket:


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');
  }
}

Conclusion

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.

: Full visibility into your web and mobile apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.

.
Emmanuel John I'm a full-stack software developer, mentor, and writer. I am an open source enthusiast. In my spare time, I enjoy watching sci-fi movies and cheering for Arsenal FC.

Leave a Reply