Dart 3 in depth: New class modifiers
Discover the new class modifiers in Dart 3
Context
If you have some experience with the Dart language, you probably know that any Dart class can be used as an interface, mixin or it can be extended. the language is quite permissive, which has many advantages but also many disadvantages: As a user, when we use a class that comes from a package, we are quite happy with the extensibility offered by default in Dart that allows us to extend or even modify the behavior of this class; As a package developer, one would probably want to keep some control over what the package client can and cannot do. Dart 3 introduces new class modifiers and we will discuss them in this article.
Features
The new class modifiers add the ability for a developer to specify whether a type can be extended, mixed or implemented from outside of the library where it was defined.
There are 5 new class modifiers introduced in Dart 3:
base class
Classes or mixins defined using this modifier can be extended but not implemented. Note that classes that extend a base class should be "base", "sealed" or "final"
interface class
interface classes define classes that cannot be extended. The only change they bring is that they remove the ability for a class to have child classes.
sealed class
The sealed class allows you to take advantage of pattern matching. They are implicitly abstract and final and hence cannot be instantiated. A few advantages are coming along with this type of class; for example, the compiler/analyzer can check if you've covered each subtype in a switch case.
final class
Class defined as final cannot be extended, implemented or mixed in (outside the library they were declared)
mixin class
The name speaks for itself. Classes defined with this keyword may be used as a mixin
Note: When no modifier is used, the current behavior for a class or mixin is the same except that types defined as "class" can not be used as mixins anymore.
Update (08/05/2023)
The introduction of new class modifiers to the Dart language comes with its own set of complexities. There's a table from the feature specification document that "explains" how they work:
Useful links: