Dart Reference
Free reference guide: Dart Reference
About Dart Reference
The Dart Reference is a comprehensive, searchable cheat sheet covering the full Dart language as used in Flutter and server-side Dart development. It covers 6 categories: Basics (var/final/const, string interpolation, conditionals, loops, enums, arrow functions), Classes (class definition, named constructors, inheritance, mixins, factory constructors), Async (Future/async/await, Stream, Future.wait/any, StreamController, Completer), Collections (List, Map, Set, where/map/reduce, spread and collection-if/for operators), Null Safety (Type?, ?. / ?? / ??=, null assertion !, late), and Generics (generic classes, generic functions, extends constraints). Every entry includes a real code example.
Dart is the language powering Flutter for cross-platform mobile, desktop, and web applications. This reference is particularly useful for Flutter developers who switch between Dart-specific syntax and framework code. The Null Safety section is especially important for Flutter 3.x and later, where sound null safety is enforced. It covers all the null-aware operators: the conditional accessor (?.), the null coalescing operator (??), the null-aware assignment (??=), the non-null assertion (!), and late initialization for fields that are guaranteed to be set before use.
The Async section covers Dart's entire async model. Future/async/await handles one-shot asynchronous operations like HTTP requests. Stream and async* generators handle continuous sequences of values — useful for real-time data, file reading in chunks, or WebSocket connections. StreamController gives you full manual control over stream events. Future.wait() runs multiple futures concurrently and collects all results, while Future.any() returns the first future to complete. The Completer class lets you manually complete a Future from outside the async function.
Key Features
- Covers 6 categories: Basics, Classes, Async, Collections, Null Safety, Generics
- Full Null Safety coverage: Type?, ?., ??, ??=, ! assertion, and late initialization
- Async model: Future/async/await, Stream with async*, StreamController, Future.wait, Completer
- Class features: named constructors, Point.fromJson(), mixins with with keyword, factory constructors for caching
- Collections: List/Map/Set operations, where/map/reduce transformations, spread operator, collection if/for
- var (mutable), final (runtime immutable), const (compile-time immutable) clearly explained with examples
- Generic classes and functions with extends constraints (T extends Comparable<T>)
- Dart-specific features: cascade operator, arrow functions, string interpolation with ${expr}
Frequently Asked Questions
What is the difference between var, final, and const in Dart?
var declares a mutable variable with type inference — the type is inferred at compile time but the value can be reassigned. final declares a runtime-immutable variable: it can only be set once, but its value can be determined at runtime. const declares a compile-time constant: the value must be known at compile time and is deeply immutable. For example, const pi = 3.14159 is evaluated at compile time, while final startTime = DateTime.now() is set once at runtime when the line executes.
How does Null Safety work in Dart?
Dart's sound null safety means variables cannot be null unless you explicitly declare them as nullable with a ? suffix (String? name). The null-aware conditional accessor (?.) calls a method or property only if the object is not null: name?.length returns null if name is null rather than throwing. The null coalescing operator (??) provides a default: name ?? "Guest" returns "Guest" if name is null. The ??= assignment sets a value only if the variable is null. The ! operator asserts a nullable value is non-null — it throws if the value is actually null.
What is the difference between Future and Stream in Dart?
A Future represents a single asynchronous value that will be available at some point in the future — like a HTTP response or a file read. A Stream represents a sequence of asynchronous events over time — like socket messages, user events, or an async generator. You await a Future to get its value. You listen to or await for a Stream to process each event. Use async* with yield to create a Stream generator, and await for to consume it.
What are mixins in Dart and when should I use them?
A mixin is a way to reuse code across multiple class hierarchies without inheritance. Defined with the mixin keyword, a mixin adds methods and properties to a class using the with keyword. For example, mixin Swimmer adds a swim() method that can be added to any class: class Duck with Swimmer. Unlike inheritance, you can apply multiple mixins to a single class. Use mixins when you want to share behavior (like logging, serialization, or animation) across unrelated classes.
What is a factory constructor in Dart?
A factory constructor is a special constructor that does not always create a new instance — it can return an existing one from a cache. This is the standard way to implement singleton-like patterns or instance caching in Dart. For example, the Logger class uses a static map as a cache and returns an existing Logger instance if one already exists for the given name. Factory constructors are declared with the factory keyword and must return an instance of the class type.
How do collection operators (spread, collection-if, collection-for) work in Dart?
Dart provides powerful collection literals. The spread operator (...) inserts all elements of one collection into another: [0, ...list, 3]. The null-aware spread (...?) handles nullable collections safely. Collection-if adds elements conditionally: [if (isLoggedIn) dashboardRoute]. Collection-for generates elements from an iterable: [for (var item in items) item.toUpperCase()]. These operators allow you to build complex collections declaratively in a single expression without temporary variables.
What is the StreamController and when do I need it?
StreamController gives you manual control over a Stream — you can add events (controller.add(value)), add errors (controller.addError()), and close the stream (controller.close()). You expose controller.stream as a public property and drive it from elsewhere in your code. This is useful when you need to bridge non-async APIs (like callbacks or event listeners) into Dart streams, or when you need to multicast events to multiple listeners using StreamController.broadcast().
How do generic constraints work in Dart?
The extends keyword in a generic parameter adds a type constraint. For example, T max<T extends Comparable<T>>(T a, T b) requires that T implements Comparable<T>, which gives you access to the compareTo() method inside the function. Without the constraint, you cannot call any methods specific to a type on the generic parameter. Constraints allow you to write reusable, type-safe utility functions that work with any type satisfying the constraint.