<FlutterSolution/> flutter · dart · clean architecture

Flutter Prep #32 Dart Fundamentals Quick Reference: Every Core Concept You'll Be Quizzed On

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.

Dart
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.

Dart
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.

Dart
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.

Dart
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.

Dart
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).

Dart
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.

Dart
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.

Dart
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.

Dart
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.

Dart
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.

Dart
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.

Dart
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.

Dart
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.

Dart
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.

Dart
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.

Dart
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.

Dart
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.

Dart
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.

Dart
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.

Dart
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.

Dart
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.

Dart
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.

Dart
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.

Dart
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.

Dart
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 (_).

Dart
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.

Dart
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.

Dart
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.

Dart
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.

Dart
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.

⚠️ The Most Commonly Confused Pair: You will absolutely be asked the difference between extending and implementing. Review the comparison below carefully.

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.

Dart
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.

Dart
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:

  • extends vs implements?
  • const vs final?
  • A mixin vs standard inheritance?
  • A normal constructor vs a factory constructor?
  • late vs 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:

Nachiketa Pandey
// written by

Nachiketa Pandey

Senior Flutter Developer · Cross-Platform & Fintech Applications

Building production Flutter apps at Kotak Neo — clean architecture, BLoC, agentic AI integrations, and fintech-grade security. Sharing everything I learn, one post at a time.

Flutter & Dart Clean Architecture BLoC Firebase FinTech
View Full Portfolio

Comments

Nachiketa
Meet Author// tap to open