Skip to main content
A covariant field in Dart is an instance variable modified by the covariant keyword, allowing a subclass to override the field with a stricter (narrower) subtype than the one declared in its superclass. This explicitly bypasses Dart’s default static type-checking rules, which normally require setter parameters to be contravariant or invariant. In Dart, declaring a mutable field implicitly generates both a getter and a setter. According to standard object-oriented subtyping rules, overriding a getter allows for a covariant return type, but overriding a setter requires a contravariant parameter type. Because a field represents both a getter and a setter, overriding a field with a narrower type creates a static type error on the implicit setter. The covariant keyword suppresses this compile-time error on the implicit setter and defers the type validation to runtime.
class SuperType {}
class SubType extends SuperType {}

class BaseClass {
  SuperType? myField;
}

class DerivedClass extends BaseClass {
  // Narrows the type of myField from SuperType? to SubType?.
  // The 'covariant' keyword prevents a static analysis error.
  @override
  covariant SubType? myField; 
}
Alternatively, the covariant keyword can be applied directly to the field in the base class. This establishes a contract that permits any future subclass to tighten the field’s type without needing to redeclare the keyword:
class BaseClass {
  // Declares that subclasses may narrow this field's type
  covariant SuperType? myField;
}

class DerivedClass extends BaseClass {
  @override
  SubType? myField; // Valid, covariance was established in the superclass
}
Mechanics and Type Safety
  • Static Analysis: When covariant is applied, the Dart analyzer permits the narrower type declaration. Within the subclass, the field is statically recognized as the narrower type, allowing direct access to the subtype’s specific members without manual casting.
  • Runtime Enforcement: By using covariant, type safety for the implicit setter is shifted from compile-time to runtime. If an instance of the subclass is upcast to the base class, and an incompatible sibling type is assigned to the field, the Dart Virtual Machine will throw a TypeError during execution.
BaseClass instance = DerivedClass();

// Compiles successfully due to the BaseClass reference, 
// but throws a runtime TypeError because 'instance' is actually 
// a DerivedClass, which expects a SubType?, not a base SuperType.
instance.myField = SuperType(); 
Tired of Poor Dart Skills? Fix That With Deep Grasping!Learn More