flutter
/

Dart Metadata – Annotations and Code Generation

Last Sync: Today

On this page

11
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

flutter

Dart Metadata – Annotations and Code Generation

What are Metadata (Annotations)?

Metadata (also called annotations) are used to provide additional information about your code. They are denoted by the @ symbol followed by an identifier. Annotations can be attached to libraries, classes, methods, fields, parameters, and more. Dart comes with several built‑in annotations, and you can also define your own. Annotations are often used by tools (like the Dart analyzer, documentation generators, and code generation packages) to provide extra behavior or information.

Built‑in Annotations

Dart provides a few built‑in annotations that are recognized by the analyzer and the compiler:

  1. @override

Indicates that a method overrides a method from a superclass. If the superclass method does not exist, the analyzer will report an error. This helps catch typos or mismatched signatures.

DARTRead-only
1
class Animal {
  void makeSound() {}
}

class Dog extends Animal {
  @override
  void makeSound() {
    print('Bark');
  }
}

  1. @deprecated

Marks a library element as deprecated (discouraged). The analyzer will show a warning when the element is used. You can optionally provide a message explaining the deprecation and suggesting alternatives.

DARTRead-only
1
@deprecated
// or @Deprecated('Use newMethod instead')
void oldMethod() {}

void main() {
  oldMethod(); // warning
}

  1. @required (from meta package)

Indicates that a named parameter is required. The analyzer will warn if the parameter is omitted. This is commonly used in Flutter constructors. It is not built‑in; you must import package:meta/meta.dart.

DARTRead-only
1
import 'package:meta/meta.dart';

class MyWidget {
  final String title;
  MyWidget({@required this.title});
}

  1. @immutable (from meta package)

Indicates that a class is immutable (all fields are final). The analyzer will warn if the class has non‑final fields.

DARTRead-only
1
import 'package:meta/meta.dart';

@immutable
class Point {
  final int x, y;
  const Point(this.x, this.y);
}

  1. @protected (from meta package)

Indicates that a member should be used only within its own library or by subclasses. The analyzer will warn if accessed from outside.

DARTRead-only
1
class Base {
  @protected
  void internalMethod() {}
}

class Sub extends Base {
  void call() {
    internalMethod(); // OK
  }
}

Creating Custom Annotations

You can define your own annotations by creating a class whose constructors can be invoked with const. The class name (or a constant of that type) becomes the annotation.

DARTRead-only
1
class Todo {
  final String description;
  final String? assignee;
  final DateTime? due;

  const Todo(this.description, {this.assignee, this.due});
}

@Todo('Refactor this method', assignee: 'Alice')
void complexMethod() {}

@Todo('Add error handling')
void riskyFunction() {}

Using Annotations with Code Generation

Custom annotations are most powerful when used with code generation tools like build_runner and packages such as json_serializable, freezed, or riverpod. These packages read annotations at build time and generate code based on them. For example, json_serializable uses @JsonSerializable to generate fromJson and toJson methods.

DARTRead-only
1
import 'package:json_annotation/json_annotation.dart';

part 'user.g.dart';

@JsonSerializable()
class User {
  final String name;
  final int age;

  User(this.name, this.age);

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

Accessing Annotations at Runtime (Mirrors)

Dart’s dart:mirrors library allows you to inspect annotations at runtime. However, mirrors are not available on all platforms (e.g., Flutter web), and they can increase code size. For most cases, compile‑time code generation is preferred over runtime reflection.

DARTRead-only
1
import 'dart:mirrors';

class Info {
  final String message;
  const Info(this.message);
}

@Info('Important class')
class MyClass {}

void main() {
  var mirror = reflectClass(MyClass);
  var metadata = mirror.metadata;
  for (var m in metadata) {
    if (m.reflectee is Info) {
      print((m.reflectee as Info).message);
    }
  }
}

Key Takeaways

    • Annotations (metadata) are attached to code elements using @.
    • Built‑in annotations include @override, @deprecated, and @required (from meta).
    • Custom annotations are defined as const classes.
    • Annotations are widely used for code generation (e.g., json_serializable).
    • Use dart:mirrors for runtime inspection, but prefer compile‑time generation.

Try it yourself

class Author {
  final String name;
  const Author(this.name);
}

@Author('John Doe')
class Book {
  final String title;
  const Book(this.title);
}

void main() {
  // You can't directly access annotations at runtime without mirrors,
  // but they are used by tools.
  print('Book class is annotated with Author.');
}

Test Your Knowledge

Q1
of 4

Which annotation is used to indicate a method overrides a superclass method?

A
@override
B
@Overrides
C
@overrideMethod
D
@Override
Q2
of 4

What must a custom annotation class have to be usable as an annotation?

A
A default constructor
B
A const constructor
C
A static method
D
An abstract method
Q3
of 4

Which package provides the `@required` annotation?

A
dart:core
B
dart:annotations
C
package:meta
D
package:annotations
Q4
of 4

How can annotations be processed at build time?

A
Using mirrors
B
Using code generation with build_runner
C
Using the dart runtime
D
Annotations cannot be processed

Frequently Asked Questions

Can I use annotations on libraries?

Yes, annotations can be placed on library declarations. For example: @Todo('Refactor this library') library my_lib;. They can also be placed on top‑level declarations like import and export.

Do annotations affect runtime performance?

Annotations themselves are just metadata; they do not impact runtime performance unless you use mirrors to inspect them. Code generation tools use them at build time, so there’s no runtime overhead.

What is the difference between `@deprecated` and `@Deprecated`?

@deprecated is an older annotation; @Deprecated (with a capital D) is the modern version that accepts a message. Both work, but @Deprecated is preferred because you can provide a deprecation message. Example: @Deprecated('Use newMethod instead').

Can I create an annotation with parameters?

Yes, define a const constructor with parameters. When applying the annotation, you can pass those parameters as you would with a normal constructor call (using positional or named parameters).

Are annotations inherited?

No, annotations are not inherited by subclasses. If you annotate a superclass, its subclasses do not automatically get that annotation.

Previous

dart callable classes

Next

dart records

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.