The `covariant` Keyword: Loosening Type Rules

The `covariant` keyword tells Dart's analyzer to relax its strict rules for method overriding, letting a subclass method accept a more specific parameter type. It's often used in Flutter widgets.
WHY IT EXISTS: Dart's type system is sound, which means it prevents you from assigning a value of one type to a variable of another incompatible type. When overriding methods, a subclass parameter must be the same type or a less specific type (contravariant) than the superclass parameter. Sometimes, this is too restrictive for practical framework design, creating a need for a controlled escape hatch.
THE MENTAL MODEL: Think of covariant as a promise you make to the compiler. You're saying, "I know this method override breaks the normal type safety rules by accepting a more specific type, but I guarantee I will only call it with this specific subtype." The analyzer trusts you at compile time but inserts a check to verify your promise at runtime. If your promise is broken, the app crashes.
HOW IT WORKS: Normally, if a superclass method is void myMethod(Animal animal), a subclass can only override it with void myMethod(Object object) (less specific), not void myMethod(Dog dog) (more specific). Using covariant lets you do the latter: void myMethod(covariant Dog dog). This tells the static analyzer to allow the code. However, if any code holding a reference to the base Animal class calls this method with a Cat instance on your subclass, a runtime error will be thrown.
WHEN TO USE IT: Use covariant when you're specializing a class and need to tighten the type of a parameter in an overridden method. This is very common in framework code like Flutter, where generic base classes (like StatefulWidget) have methods that are implemented by subclasses with very specific types (like _MyFancyButtonState). It signals a deliberate design choice in an inheritance hierarchy.
WHEN NOT TO USE IT: Avoid covariant in general application logic. It's a tool for framework-level patterns where you control the call sites. Using it carelessly silences important static analysis warnings and moves a bug from compile time to runtime, which is almost always worse. If you find yourself needing it, question your class hierarchy first; there may be a better design.
ONE CANONICAL EXAMPLE: In Flutter, the StatefulWidget.createState() method is an abstract method that returns a State. When you create MyWidget, you implement createState to return a _MyWidgetState. Another example is the == operator. The base Object class defines operator ==(Object other). When you override it in MyClass, you write bool operator ==(covariant MyClass other) to safely compare it only to other instances of MyClass.
Read the original → dart.dev
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.