Dart Fundamentals Quick Reference: Every Core Concept You'll Be Quizzed On
A fast, strictly-business glossary of foundational Dart semantics. You'll be asked to define at least a few of these cold—bookmark this as your pre-interview checklist.
Variables, Types, & Mutability
var
A keyword used to declare a variable without explicitly specifying its type. The type is inferred at compile time from the assigned value, and is fixed permanently after that first assignment.
var name = 'Dash'; // Inferred as String
// name = 42; // ERROR: A value of type 'int' can't be assigned to a variable of type 'String'.
dynamic
A type that disables static type checking, deferring it entirely to runtime. Unlike var, a dynamic variable can be reassigned to a completely different type later. In short: var infers once and locks the type, dynamic skips type checking entirely.
dynamic value = 'Dash';
value = 42; // Perfectly fine at compile time
// value.foo(); // Fails at RUNTIME, not compile time
const
Declares a compile-time constant. The value must be known at the moment the code compiles, and it is deeply, transitively immutable. Cannot be assigned the result of a runtime function.
const double pi = 3.14159;
// const now = DateTime.now(); // ERROR: value isn't known at compile time
final
A variable that can be set only once, but its value can be determined at runtime. In short: const is compile-time + deeply immutable, final is runtime-assignable-once.
final startTime = DateTime.now(); // Allowed!
// startTime = DateTime.now(); // ERROR: final variable can only be set once
late
A modifier promising the compiler that a non-nullable variable will be initialized before it is read. It defers initialization past declaration (and past constructor execution for fields) without forcing you to make the type nullable.
late String databasePath; // Non-nullable, but not set yet
void initDb() {
databasePath = '/local/db.sqlite'; // Must be called before reading databasePath
}
Nullable Types & Null-Aware Operators
Dart is null-safe. You explicitly mark types that can hold null with ?. You manage them with ! (assert non-null, throws if wrong), ?? (provide a default if null), and ?. (safely access a member only if the object isn't null).
String? name; // Can be null
int length = name?.length ?? 0; // If name is null, length is 0
String forced = name!; // Throws runtime exception if name is null
Collections & Syntax Sugar
HashMap / Map
A key-value collection type. In Dart, a standard Map literal is backed by a LinkedHashMap which preserves insertion order. If you need a strictly unordered version, you can explicitly use HashMap from dart:collection.
Map<String, int> ages = {
'Alice': 28,
'Bob': 32,
}; // Maintains insertion order
Cascade Notation (..)
Allows you to chain multiple method calls or field assignments on the same object without repeatedly typing its reference. It returns the object itself, rather than the result of the method call.
final paint = Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0; // Returns the Paint object
Spread (...) & Null-Aware Spread (...?)
A concise syntax to insert all elements of one collection into another collection literal. The null-aware version (...?) only attempts the spread if the source collection isn't null.
List<int> defaults = [1, 2];
List<int>? extras;
List<int> all = [0, ...defaults, ...?extras]; // Result: [0, 1, 2]
Functions
Fat Arrow (=>)
Shorthand syntax for a function or method body that consists of a single expression. It implicitly returns the result of that expression, completely replacing the { return expression; } block.
bool isAdult(int age) => age >= 18;
// Exact equivalent of: bool isAdult(int age) { return age >= 18; }
Function Parameters & Types
Dart functions support positional (required by default), optional positional (wrapped in []), and named parameters (wrapped in {}). Named parameters are optional by default unless marked with the required keyword.
void configure(
String env, // Positional (required)
[int? port], // Optional Positional
{bool debug = false, // Optional Named (with default)
required String key} // Required Named
) { ... }
Anonymous Function
A function without a name, often passed inline as a callback to higher-order functions like .map() or .where(). While often written with the fat arrow shorthand, an anonymous function can also have a full block body.
var list = ['apples', 'bananas'];
// Anonymous function with a block body
list.forEach((item) {
print(item.toUpperCase());
});
typedef
Creates a named alias for a type. It is most frequently used to define a clean, readable name for a complex function signature, making it easier to pass callbacks around.
typedef IntOperation = int Function(int a, int b);
int execute(int x, int y, IntOperation op) => op(x, y);
Generics (<T>)
Allows a class or function to operate on a type parameter (like T) decided by the caller. It provides compile-time type safety without having to write duplicate code for every possible type.
class Box<T> {
T value;
Box(this.value);
}
var stringBox = Box<String>('Hello'); // value is strictly a String here
Classes & Object-Oriented Basics
class
The blueprint for creating objects. It bundles state (fields/variables) and behavior (methods) together into a single logical structure.
class User {
String name;
User(this.name);
void greet() => print('Hi, $name');
}
this
Refers to the current instance of the class. It is almost exclusively used to disambiguate a class field from a constructor parameter or local variable of the exact same name (e.g., this.name = name;).
super
Refers to the parent class. It is used to call a parent's constructor or to invoke a parent method that has been overridden in the child class (e.g., super.initState();).
Constructors
Functions that initialize an object. Dart supports a default constructor, named constructors (for alternative ways to build the object), and a shorthand parameter syntax (this.field) to assign fields automatically.
class Point {
double x, y;
Point(this.x, this.y); // Default with syntactic sugar
Point.origin() : x = 0, y = 0; // Named constructor with initializer list
}
getter (get)
A special method that reads a value. It looks like a property access to the caller (no parentheses) but executes code under the hood to compute or retrieve the value.
double width = 5, height = 5;
double get area => width * height;
// Usage: print(area);
setter (set)
A special method that assigns a value. It looks like standard variable assignment to the caller, making it ideal for adding validation logic before updating private state.
int _age = 0;
set age(int value) {
if (value < 0) throw Exception('Invalid');
_age = value;
} // Usage: obj.age = 25;
Static Variables & Methods
Members declared as static belong to the class itself rather than any individual instance. They are shared across all instances and accessed via ClassName.member.
class MathConstants {
static const double pi = 3.14159;
static double doubleIt(double val) => val * 2;
}
// Usage: MathConstants.doubleIt(MathConstants.pi);
Advanced Architecture & Patterns
override
The @override annotation indicates that a method intentionally replaces a method inherited from a superclass or interface. While not strictly required by the compiler, omitting it is terrible practice as it disables safety checks if the parent method changes.
class Circle extends Shape {
@override
void draw() { ... }
}
Abstract Class
A class that cannot be instantiated directly and is meant to be extended or implemented. It is unique because it can contain both concrete (implemented) methods and abstract (unimplemented) methods.
abstract class Repository {
void save(String data); // Abstract method (no body)
void log() => print('Saving...'); // Concrete method
}
Implicit Interfaces
In Dart, every class implicitly defines an interface containing all its instance members. This is why you can use implements on any standard class—Dart doesn't need a separate interface keyword.
class Logger {
void log(String msg) => print(msg);
}
// MockLogger must implement log(), even though Logger is just a normal class
class MockLogger implements Logger {
@override
void log(String msg) {}
}
Enums (Enhanced)
A fixed set of named constant values. Dart features enhanced enums, meaning enums aren't just labels—they can have their own fields, methods, and constructors, behaving much like classes.
enum Status {
pending(0), success(1), error(2);
final int code;
const Status(this.code); // Enum constructor
bool get isDone => this != pending;
}
Factory Method (factory)
A constructor that doesn't necessarily create a new instance of its class every time. It can return a cached instance from memory, return an instance of a subclass, or run complex validation logic before returning an object.
class Logger {
static final Map<String, Logger> _cache = {};
final String name;
// Private constructor
Logger._internal(this.name);
// Factory returns cached instance if it exists
factory Logger(String name) {
return _cache.putIfAbsent(name, () => Logger._internal(name));
}
}
Singleton Class
A pattern restricting a class to exactly ONE instance throughout the app's lifetime. In Dart, this is usually achieved via a private constructor and a static instance getter, or by pairing a private constructor with a factory constructor.
class Database {
Database._privateConstructor(); // Private named constructor
static final Database instance = Database._privateConstructor();
// Optional: wire it to a factory so `Database()` returns the singleton
factory Database() => instance;
}
Inheritance, Composition, & Structure
Encapsulation
Bundling data and methods together while restricting direct outside access to internal state. Because Dart has no private keyword, encapsulation is achieved at the library (file) level by prefixing a variable or method name with an underscore (_).
class Wallet {
double _balance = 0; // Private to this file
double get balance => _balance; // Read-only public access
void deposit(double amount) => _balance += amount;
}
Inheritance
The mechanism where a child class acquires the fields and methods of a parent class via the extends keyword. It establishes an "is-a" relationship.
class Vehicle {
void start() => print('Starting engine');
}
class Car extends Vehicle {
// Inherits start() automatically
}
Polymorphism
The ability to treat different classes through a common parent interface type. Each subclass provides its own specific behavior for a shared method, allowing the caller to use them interchangeably without knowing the exact runtime type.
void makeNoise(Animal a) => a.speak(); // Caller just sees 'Animal'
makeNoise(Dog()); // Outputs: Woof
makeNoise(Cat()); // Outputs: Meow
Mixin
A way to reuse a class's code across multiple independent class hierarchies without using inheritance, applied via the with keyword. A class can extend only one superclass, but it can mix in multiple mixins.
mixin Swimmer {
void swim() => print('Swimming');
}
class Duck extends Bird with Swimmer { } // Has Bird traits + Swimmer methods
Extensions
Allows you to add new functionality to an existing type (even core types you don't own, like String or int) without modifying its source code or subclassing it.
extension StringCasing on String {
String toCapitalized() => length > 0 ?'${this[0].toUpperCase()}${substring(1)}':'';
}
// Usage: print('hello'.toCapitalized()); // Hello
Interface vs Abstract Class
To be completely precise: Dart has no separate interface keyword for declarations. Any class can serve as an interface. An abstract class is Dart's closest equivalent to a traditional interface that allows default behavior, because it can mix unimplemented methods with implemented ones. A "pure interface" in Dart is just any class (regular or abstract) that is consumed using the implements keyword, forcing the child to rewrite every member.
extends
Inherits the parent's implementation. You get all the parent's method bodies and fields for free, and you only override what you want to change. You are limited to extending exactly ONE parent class.
class Worker {
void clockIn() => print('Clocked in');
}
class Manager extends Worker {
// Gets clockIn() for free automatically
}
implements
Inherits only the parent's signature/contract. You inherit absolutely no code or behavior. You MUST write your own implementation for every single method and field the parent defines. You can implement MULTIPLE interfaces.
class Worker {
void clockIn() => print('Clocked in');
}
class Contractor implements Worker {
@override
void clockIn() {
// MUST rewrite this logic from scratch
print('Contractor logged hours');
}
}
✅ Quick Self-Check
Without scrolling up, can you articulate the exact difference between:
extendsvsimplements?constvsfinal?- A
mixinvs standardinheritance? - A normal constructor vs a
factoryconstructor? latevs a Nullable type (?)?
If you stumbled on any of these, click back to them in the jump grid. They are guaranteed interview topics.
Official Resources
For exhaustive semantic details, rely strictly on the official documentation:

Comments
Post a Comment