interviewDeck

Your one-stop platform to prepare, practice and ace your interviews.

Loading your questions…

All Questions

Filters & tools

Dart Interview Questions and Answers

100 hand-picked Dart interview questions with detailed answers. Open the interactive version above to search, filter by difficulty, run code, bookmark questions and track your progress.

What is Dart, and why is it used for Flutter development?

Dart is an object-oriented, client-optimized programming language developed by Google. It is the primary language for Flutter because it supports AOT (Ahead-of-Time) compilation for fast production performance and JIT (Just-in-Time) compilation for Hot Reload during development.

What is the difference between var, final, and const in Dart?

var declares a variable whose type is inferred and whose value can change.

final can only be assigned once at runtime.

const represents a compile-time constant and must be initialized with a constant value.

var name = 'John';
final date = DateTime.now();
const pi = 3.14159;

What is Null Safety in Dart?

Null Safety helps prevent null reference errors by distinguishing between nullable and non-nullable types. A variable declared as String cannot hold null, while String? can.

Dart also provides operators like ?, !, and ?? to safely work with nullable values.

String name = 'John';
String? middleName;
print(middleName ?? 'Not Available');

Explain async, await, and Future in Dart.

Future represents a value that will be available later. Functions marked with async return a Future, and the await keyword pauses execution until the Future completes without blocking the main thread.

Future<String> fetchUser() async {
  await Future.delayed(Duration(seconds: 2));
  return 'John';
}

Which Object-Oriented Programming (OOP) concepts does Dart support?

Dart fully supports the four main OOP principles:

  • Encapsulation – Bundle data and methods inside classes.
  • Inheritance – Extend existing classes using extends.
  • Polymorphism – Override methods in subclasses.
  • Abstraction – Use abstract classes and interfaces to hide implementation details.

Dart also supports mixins, extensions, and interfaces for code reuse.

What are the differences between List, Set, and Map in Dart?

List is an ordered, index-accessible collection that allows duplicates. Set is an unordered collection of unique elements—duplicates are automatically discarded. Map stores key-value pairs with unique keys, allowing average O(1) lookup by key.

var list = [1, 2, 2, 3];
var set = {1, 2, 2, 3}; // {1, 2, 3}
var map = {'a': 1, 'b': 2};

What are Generics in Dart, and why are they useful?

Generics let classes and functions operate on a type specified at the call site rather than hardcoding a type, giving compile-time type safety while allowing reusable code. Dart's core collections (List<T>, Map<K, V>) are all generic.

class Box<T> {
  final T value;
  Box(this.value);
}
final intBox = Box<int>(5);

What are Extension Methods in Dart?

Extension methods let you add new functionality to an existing type (even ones you don't own, like String or int) without modifying its source or subclassing it. They are resolved statically at compile time.

extension StringExtension on String {
  bool get isValidEmail => contains('@');
}

print('a@b.com'.isValidEmail); // true

What are Mixins in Dart, and how do they differ from inheritance?

A mixin lets a class reuse a set of methods/fields from multiple sources without traditional single inheritance, using the with keyword. Unlike inheritance (an 'is-a' relationship with one superclass), mixins model 'can-do' capabilities and can be composed from multiple mixins at once.

mixin Flyer {
  void fly() => print('Flying');
}
class Bird extends Animal with Flyer {}

How does Dart's event loop work, and what's the difference between the microtask queue and the event queue?

Dart runs single-threaded within an isolate using an event loop. The microtask queue (used internally for resolving Futures) is always fully drained before the event loop processes the next item in the event queue (I/O callbacks, timers, user input). This means scheduleMicrotask callbacks always run before a pending Timer, even a zero-duration one.

Future(() => print('event queue'));
scheduleMicrotask(() => print('microtask'));
// Output: microtask, then event queue

What is a Stream in Dart, and what's the difference between single-subscription and broadcast streams?

A Stream is a sequence of asynchronous events. A single-subscription stream allows only one listener and buffers events until listened to (used for things like file reads). A broadcast stream allows multiple listeners simultaneously but doesn't buffer—listeners only get events emitted after they subscribe.

final controller = StreamController<int>.broadcast();
controller.stream.listen((v) => print('A: $v'));
controller.stream.listen((v) => print('B: $v'));
controller.add(1);

How do you run multiple Futures concurrently in Dart?

Future.wait([...]) runs a list of Futures concurrently and completes when all of them finish (or fails fast if one throws, unless eagerError: false). This is much faster than awaiting each Future sequentially when the operations are independent.

final results = await Future.wait([
  fetchUser(),
  fetchPosts(),
]);

What are Enhanced Enums in Dart?

Since Dart 2.17, enums can have fields, constructors, methods, and implement interfaces, not just be a list of named values. This lets you attach data/behavior directly to each enum constant instead of using a separate switch statement.

enum Status {
  active(label: 'Active', color: Colors.green),
  inactive(label: 'Inactive', color: Colors.grey);

  final String label;
  final Color color;
  const Status({required this.label, required this.color});
}

What are Records and Pattern Matching in Dart 3?

Records let you return multiple values from a function without defining a class, e.g. (int, String). Pattern matching (via switch expressions, destructuring, and if-case) lets you deconstruct records, lists, maps, and objects concisely and exhaustively.

(int, String) getUser() => (1, 'John');

final (id, name) = getUser();
print('$id $name');

What are sealed classes in Dart, and how do they enable exhaustive switches?

A sealed class restricts all direct subtypes to the same library, letting the compiler know every possible subtype. This allows switch statements/expressions over the sealed type to be checked for exhaustiveness at compile time—if you add a new subtype and forget a case, Dart flags it as an error, not a runtime bug.

sealed class Result<T> {}
class Success<T> extends Result<T> { final T data; Success(this.data); }
class Failure<T> extends Result<T> { final String error; Failure(this.error); }

String show(Result r) => switch (r) {
  Success(:final data) => 'Data: $data',
  Failure(:final error) => 'Error: $error',
};

What is a factory constructor in Dart, and when would you use one?

A factory constructor doesn't always create a new instance—it can return a cached instance, an instance of a subclass, or run logic before construction (like validating/parsing JSON). This differs from a normal constructor, which always creates a new instance of exactly that class.

class User {
  final String name;
  User._(this.name);

  factory User.fromJson(Map<String, dynamic> json) {
    return User._(json['name'] as String);
  }
}

What are named constructors and initializer lists in Dart?

Named constructors let a class have multiple constructors for different creation scenarios (e.g. Point.origin()). An initializer list (after the colon, before the body) runs before the constructor body and is used to initialize final fields or call super/assert before the object exists.

class Point {
  final double x, y;
  Point(this.x, this.y);
  Point.origin() : x = 0, y = 0;
}

What is the difference between static and instance members in Dart?

Instance members (fields/methods) belong to a specific object and can access this. Static members belong to the class itself, are shared across all instances, and are accessed via the class name—useful for constants, factory helpers, or utility functions that don't need instance state.

class MathUtils {
  static const double pi = 3.14159;
  static double square(double x) => x * x;
}
print(MathUtils.square(4));

How does operator overloading work in Dart?

Dart lets classes override operators like +, ==, and [] by defining an operator method, enabling natural syntax for custom types like vectors or money values.

class Vector {
  final double x, y;
  Vector(this.x, this.y);
  Vector operator +(Vector o) => Vector(x + o.x, y + o.y);
}

How does exception handling work in Dart?

Dart uses try/catch/on/finally. on SomeType catch (e) catches a specific exception type, plain catch (e, stackTrace) catches anything, and finally always runs (cleanup). Custom exceptions are made by implementing Exception or extending Error.

try {
  riskyCall();
} on FormatException catch (e) {
  print('Bad format: $e');
} catch (e, st) {
  print('Unknown error: $e');
} finally {
  cleanup();
}

What do the spread operator and collection if/for do in Dart?

The spread operator (...) inlines the elements of one collection into another literal; ...? spreads a nullable collection safely. Collection if/for let you conditionally include elements or generate them with a loop directly inside a list/set/map literal, avoiding manual list-building boilerplate.

final base = [1, 2, 3];
final combined = [0, ...base, if (base.isNotEmpty) 4, for (var i in base) i * 2];

What is cascade notation (..) in Dart?

The cascade operator (..) lets you perform a sequence of operations on the same object without repeating its name, and returns the original object rather than the result of the last call.

var sb = StringBuffer()
  ..write('Hello')
  ..write(' ')
  ..write('World');
print(sb.toString());

What is a typedef in Dart, and when would you use one?

A typedef creates an alias for a type, most commonly a function signature, making code more readable and giving a named, reusable type for callbacks (e.g. a validator function passed around your app).

typedef Validator = String? Function(String? value);

void setValidator(Validator v) { /* ... */ }

What does the late keyword do in Dart?

late marks a non-nullable variable that will be initialized after its declaration but before first use—useful for fields initialized in initState(), or for lazy, expensive computations that should only run when first accessed. Accessing a late variable before it's initialized throws a LateInitializationError.

late String name; // initialized later

void setup() {
  name = 'John';
}

What is the copyWith pattern in Dart, and why is it used?

copyWith creates a new instance of an immutable class with some fields replaced, keeping the rest unchanged. Since fields are typically final, this is the standard way to 'update' immutable state objects—common in state management (BLoC/Riverpod state classes).

class UserState {
  final String name;
  final int age;
  const UserState({required this.name, required this.age});

  UserState copyWith({String? name, int? age}) =>
      UserState(name: name ?? this.name, age: age ?? this.age);
}

What is the difference between var, dynamic, and Object in Dart?

var infers a static type at compile time from the assigned value, and that type is then fixed. dynamic disables static type checking entirely—you can reassign any type and call any method without a compile error (errors surface at runtime instead). Object is the root of Dart's type hierarchy—every value is an Object, but you must cast before calling type-specific members.

var a = 5;      // inferred as int, fixed
dynamic b = 5;  // can become anything
b = 'hello';    // OK
Object c = 5;   // must cast to use int-specific methods

What are some commonly used String methods in Dart?

Common methods: substring(), split(), trim(), toUpperCase()/toLowerCase(), replaceAll(), contains(), startsWith()/endsWith(), padLeft()/padRight(), and indexOf(). Strings in Dart are immutable—every method returns a new String rather than mutating in place.

'  Hello '.trim().toUpperCase(); // 'HELLO'
'5'.padLeft(3, '0'); // '005'

How does string interpolation work in Dart?

$variable embeds a variable's value directly inside a string, and ${expression} embeds the result of any expression. This is generally preferred over manual string concatenation with + for readability and (for multiple concatenations) performance.

var name = 'John';
print('Hello, $name! Length: ${name.length}');

What is the relationship between num, int, and double in Dart?

num is the abstract superclass of both int (whole numbers) and double (64-bit floating point). Declaring a variable as num lets it hold either an int or a double, useful for generic arithmetic code that shouldn't care which one it gets.

num value = 5;
value = 5.5; // also valid, still num
print(5 ~/ 2); // 2 (integer division)

Explain map(), where(), reduce(), and fold() on a Dart List.

map() transforms each element into a new value (lazy—returns an Iterable). where() filters elements matching a predicate. reduce() combines all elements into one using an accumulator function, but throws on an empty list. fold() is like reduce but takes an explicit initial value, so it works safely on empty lists too.

final nums = [1, 2, 3, 4];
final doubled = nums.map((n) => n * 2).toList();
final evens = nums.where((n) => n.isEven).toList();
final sum = nums.fold(0, (acc, n) => acc + n);

What is the difference between Iterable and List in Dart?

Iterable is a lazy, one-directional sequence—operations like map/where aren't computed until you iterate (e.g. via toList() or a for-loop). List is a concrete, indexable, eagerly-evaluated collection stored in memory, supporting [] index access and length in O(1).

How do you sort a List with a custom comparator in Dart?

list.sort((a, b) => ...) sorts in place using a comparator that returns negative/zero/positive, similar to Java/JS. For sorting by a derived key, combine with compareTo on that key, or use a named comparator function for readability.

final people = [Person('Bob', 30), Person('Ann', 25)];
people.sort((a, b) => a.age.compareTo(b.age));

What are some useful Map methods in Dart?

map.entries exposes key-value pairs for iteration. putIfAbsent(key, () => value) inserts only if the key is missing. update(key, (v) => ..., ifAbsent: () => ...) updates an existing value or inserts a default. map.map((k, v) => ...) transforms a Map into a new Map.

final counts = <String, int>{};
for (final word in words) {
  counts.update(word, (v) => v + 1, ifAbsent: () => 1);
}

What set operations does Dart's Set support?

union() combines two sets. intersection() returns elements present in both. difference() returns elements in the first set but not the second. These are useful for tasks like finding common tags or de-duplicating combined lists efficiently.

final a = {1, 2, 3};
final b = {2, 3, 4};
print(a.intersection(b)); // {2, 3}
print(a.difference(b));   // {1}

What is the difference between named and positional parameters in Dart?

Positional parameters are passed in order and are required by default unless wrapped in [] (optional positional). Named parameters (wrapped in {}) are passed by name at the call site, improving readability, and are optional by default unless marked required.

void greet(String name, {String greeting = 'Hello'}) {
  print('$greeting, $name');
}
greet('John', greeting: 'Hi');

What does the required keyword do in Dart?

required marks a named parameter as mandatory—callers must supply it, and the compiler enforces this at the call site, even though named parameters are otherwise optional by default. It's commonly combined with non-nullable types on constructors.

class User {
  final String name;
  User({required this.name});
}

What is a closure in Dart, and how does it capture variables?

A closure is a function that captures variables from its surrounding lexical scope, keeping a reference to them (not a copy) even after the outer function has returned. This is why a counter closure keeps incrementing the same captured variable across calls.

Function makeCounter() {
  int count = 0;
  return () => ++count;
}

final counter = makeCounter();
print(counter()); // 1
print(counter()); // 2

What is arrow function syntax in Dart, and when can you use it?

The => (fat arrow) syntax is shorthand for a function body that's a single expression, implicitly returning that expression's value—equivalent to a { return expr; } block but more concise. It cannot be used for bodies needing multiple statements.

int square(int x) => x * x;
// equivalent to:
int square2(int x) { return x * x; }

What is a higher-order function in Dart?

A higher-order function either accepts a function as an argument or returns a function as its result. Since functions are first-class values in Dart, this enables patterns like passing callbacks (onPressed), or building function factories (like a debounce or memoize wrapper).

void Function() withLogging(void Function() action) {
  return () {
    print('Before');
    action();
    print('After');
  };
}

What should you consider when writing recursive functions in Dart?

Every recursive function needs a clear base case to terminate, and each recursive call should progress toward it. Dart does not guarantee tail-call optimization, so very deep recursion (tens of thousands of levels) can throw a stack overflow—an iterative approach or trampolining is safer for unbounded depth.

int factorial(int n) => n <= 1 ? 1 : n * factorial(n - 1);

How does Dart handle interfaces, given it doesn't have an explicit 'interface' keyword?

Every Dart class implicitly defines an interface. Any class can act as an interface by having another class implements it, forcing that class to provide its own implementation of all members (no inherited behavior). An abstract class (marked abstract, can have unimplemented methods) is used when you want to share some default implementation via extends, while still requiring subclasses to implement specific methods.

abstract class Shape {
  double area();
}
class Circle implements Shape {
  final double radius;
  Circle(this.radius);
  @override
  double area() => 3.14159 * radius * radius;
}

What is the difference between extends, implements, and with in Dart?

extends inherits from one superclass, gaining its implementation and being able to override it. implements adopts a class's interface (its member signatures) without any inherited implementation—you must implement everything yourself. with applies one or more mixins, adding their implementation to the class without traditional single inheritance.

class Bird extends Animal with Flyer, Swimmer implements Pet {}

How do you create a private constructor in Dart, and why would you?

Prefixing a constructor name with an underscore (e.g. User._()) makes it private to the library, preventing external code from calling it directly. This is commonly used to force object creation through a controlled factory constructor, or to implement the Singleton pattern.

class Singleton {
  Singleton._internal();
  static final Singleton instance = Singleton._internal();
}

How do getters and setters work in Dart?

A get defines a computed property accessed like a field (no parentheses), and a set defines custom logic that runs when a property is assigned. Together they let you expose a field-like API while adding validation or derived computation behind the scenes.

class Circle {
  double radius;
  Circle(this.radius);
  double get area => 3.14159 * radius * radius;
  set diameter(double d) => radius = d / 2;
}

What is the difference between a switch statement and a switch expression in Dart 3?

A switch statement executes code per matching case using case/break. A switch expression (Dart 3) directly evaluates to a value using => per case, is more concise, supports pattern matching, and the compiler enforces exhaustiveness for enums and sealed types.

String describe(int n) => switch (n) {
  0 => 'zero',
  int() when n < 0 => 'negative',
  _ => 'positive',
};

What do the null-aware operators ?., ??, and ??= do in Dart?

?. calls a method/accesses a property only if the receiver isn't null, otherwise evaluates to null. ?? returns the left value if it's non-null, otherwise the right (fallback) value. ??= assigns the right value only if the variable is currently null.

String? name;
print(name?.length);      // null, no exception
print(name ?? 'Unknown'); // 'Unknown'
name ??= 'Default';       // assigns only if null

What does the assert statement do in Dart?

assert(condition, message) throws an AssertionError if the condition is false, but only in debug builds—asserts are stripped entirely in release/profile mode, so they're safe for cheap sanity checks without impacting production performance.

void setAge(int age) {
  assert(age >= 0, 'Age cannot be negative');
}

What is the difference between throw and rethrow in a catch block?

throw e inside a catch block throws the exception again but resets the stack trace to the current point, losing the original error location. rethrow re-throws the caught exception while preserving its original stack trace, which is important for debugging and logging.

try {
  riskyCall();
} catch (e) {
  logger.log(e);
  rethrow;
}

What are sync* and async* generator functions in Dart?

sync* functions return an Iterable and use yield to lazily produce values one at a time as the caller iterates. async* functions return a Stream and use yield to asynchronously emit values over time. Both can use yield* to delegate to another iterable/stream.

Stream<int> countStream(int max) async* {
  for (var i = 1; i <= max; i++) {
    await Future.delayed(const Duration(seconds: 1));
    yield i;
  }
}

What is a Completer in Dart, and when would you use one?

Completer<T> lets you manually create and control a Future, calling complete(value) or completeError(error) whenever the async operation actually finishes—useful when wrapping callback-based APIs (that don't natively return a Future) into a Future-based API.

Future<String> waitForCallback() {
  final completer = Completer<String>();
  legacyApi.onDone((result) => completer.complete(result));
  return completer.future;
}

What is a Zone in Dart?

A Zone is an execution context that can intercept and modify behavior for code running within it—such as capturing all uncaught async errors (runZonedGuarded), overriding print, or tracking timers. It's the mechanism Flutter apps use to globally catch errors thrown inside async callbacks that would otherwise crash silently.

How do you manually spawn an Isolate in Dart, and how do isolates communicate?

Isolate.spawn(entryPoint, message) starts a new isolate running a top-level/static function. Since isolates share no memory, they communicate exclusively via message passing using SendPort/ReceivePort—messages are copied between isolates, not shared by reference.

final receivePort = ReceivePort();
await Isolate.spawn(_worker, receivePort.sendPort);
final result = await receivePort.first;

void _worker(SendPort sendPort) {
  sendPort.send('done');
}

How do you encode and decode JSON in Dart using dart:convert?

jsonDecode(str) parses a JSON string into Dart objects (Map/List/primitives). jsonEncode(obj) serializes a Dart object back into a JSON string. Custom classes need their own toJson()/fromJson() since jsonEncode only knows how to handle built-in types by default.

import 'dart:convert';

final map = jsonDecode('{"name":"John"}');
final json = jsonEncode({'name': 'John'});

Why can't you use dart:io in a Flutter Web app?

dart:io provides file system, socket, and process APIs that assume a native OS environment—none of which exist in a browser sandbox, so the library isn't available when compiling to JavaScript/WASM for web. Cross-platform code should conditionally import platform-specific implementations, or use packages (like http) that abstract over the difference.

What does the environment/sdk constraint in pubspec.yaml control?

The environment: sdk: field specifies which Dart SDK versions the package/app is compatible with, using semantic version ranges. Pub uses it to ensure your dependencies (and your own code, if you use newer language features like Dart 3 records) only run on a compatible SDK.

environment:
  sdk: '>=3.0.0 <4.0.0'

What should you evaluate before adding a third-party package from pub.dev?

Check the package's pub points/popularity/likes score, how recently it was updated, whether it has null safety support, open issue/PR activity (is it maintained?), and whether it has a compatible license. Also check if it's a thin wrapper you could implement yourself to avoid an unnecessary dependency.

What is 'Effective Dart', and why does it matter?

Effective Dart is Google's official style and usage guide covering naming conventions (UpperCamelCase for types, lowerCamelCase for members), documentation practices, and usage recommendations (e.g. prefer final over var when possible). Following it keeps code consistent across teams and is what the default lints package enforces.

Why prefer immutable data structures in Dart, especially in Flutter apps?

Immutable objects can't be changed after creation, which eliminates a whole class of bugs from shared references being mutated unexpectedly from another part of the app, makes equality checks meaningful (two immutable objects with the same data are truly equal), and works well with widget rebuild optimizations that rely on comparing old vs new state.

What is the difference between const and final when applied to an object instance (not just a primitive)?

final means the variable's reference can't be reassigned, but the object it points to could still be internally mutable (if its fields aren't all final). const creates a fully compile-time-constant, deeply immutable object—all its fields must also be const, and identical const expressions are automatically canonicalized to the same instance in memory.

final list = [1, 2, 3];
list.add(4); // OK, list itself is mutable

const list2 = [1, 2, 3];
// list2.add(4); // Error: unmodifiable

How do you deep copy a nested object/list/map in Dart?

Dart has no built-in deep-copy. A shallow copy (List.from(list) or the spread operator) only copies the top-level references—nested lists/maps/objects are still shared. A true deep copy requires manually recursively cloning nested structures, or serializing to JSON and back for simple data-only structures.

final shallow = [...original]; // nested objects still shared
final deep = jsonDecode(jsonEncode(original)); // works only for JSON-safe data

What is the Comparable interface used for in Dart?

Implementing Comparable<T> and its compareTo() method lets your custom class define a natural sort order, so it works automatically with List.sort() (with no comparator needed) and sorted collections like SplayTreeSet.

class Person implements Comparable<Person> {
  final int age;
  Person(this.age);
  @override
  int compareTo(Person other) => age.compareTo(other.age);
}

people.sort(); // uses compareTo automatically

What does Iterable.generate() do, and when is it useful?

Iterable.generate(count, generator) lazily produces a sequence of a given length by calling a generator function with each index, avoiding manually building a list with a loop—handy for building a range of computed values (like a list of days, or a 2D grid coordinate list).

final squares = List.generate(5, (i) => i * i); // [0, 1, 4, 9, 16]

What is List.unmodifiable, and why use it?

List.unmodifiable(source) creates a fixed-length, read-only view over the given elements—attempting add/remove/index-assignment throws an UnsupportedError. It's useful for exposing a collection field publicly without letting external code mutate your internal state.

class Cart {
  final List<Item> _items = [];
  List<Item> get items => List.unmodifiable(_items);
}

What is type promotion in Dart?

After an is type check (or a null check like if (x != null)), Dart's compiler 'promotes' the variable to the narrower type within that scope, so you can call type-specific members without an explicit cast. Promotion only works on local variables and final fields the compiler can prove aren't reassigned in between.

void printLength(Object value) {
  if (value is String) {
    print(value.length); // promoted to String here, no cast needed
  }
}

What does the covariant keyword do in Dart?

covariant lets a subclass override a method parameter with a more specific (narrower) type than the superclass declares, moving what would otherwise be a compile-time type error into a runtime check instead—used when you're confident the narrower type is always what's actually passed in practice.

class Animal {
  void chase(Animal other) {}
}
class Cat extends Animal {
  @override
  void chase(covariant Cat other) {} // narrows Animal -> Cat
}

How do you implement the Singleton pattern in Dart?

Use a private constructor plus a static final instance, or a factory constructor that always returns the same cached instance. Dart guarantees the static field is initialized lazily and only once, making this thread-safe without extra locking (isolates don't share memory, so there's no cross-isolate race either).

class ApiClient {
  ApiClient._internal();
  static final ApiClient _instance = ApiClient._internal();
  factory ApiClient() => _instance;
}

What are Extension Types in Dart 3?

Extension types wrap an existing type with a new static-only interface—unlike a regular class wrapper, there's no runtime object/allocation overhead; the compiler enforces the new type's API at compile time while the underlying representation type is used at runtime. Useful for adding type safety (e.g. distinguishing a raw int UserId from a ProductId) with zero cost.

extension type UserId(int value) {}

void greet(UserId id) => print('User #${id.value}');
greet(UserId(42));

What do the base, interface, final, and sealed class modifiers control in Dart 3?

These modifiers restrict how a class can be used outside its own library: base forces subclasses to extend (not implement) it, preserving guaranteed behavior. interface allows implementing but not extending outside the library. final prevents any subclassing outside the library entirely. sealed restricts all direct subtypes to the same library, enabling exhaustive switches.

How does equality work for Records in Dart?

Records have structural equality built in automatically—two records are == if they have the same shape (same fields/names) and each corresponding field is equal, with no need to override ==/hashCode manually like you would for a regular class.

print((1, 'a') == (1, 'a')); // true
print((x: 1, y: 2) == (x: 1, y: 2)); // true

How do you destructure a List or Map using pattern matching in Dart 3?

Dart 3 pattern matching lets you destructure lists (final [a, b, ...rest] = list;) and maps (final {'name': name} = map;) directly into variables in one step, including nested destructuring, instead of manually indexing/accessing keys.

final [first, second] = [10, 20];
final {'name': name, 'age': age} = {'name': 'John', 'age': 30};

What are pattern guards (the 'when' clause) in a Dart switch?

A when clause adds an extra boolean condition to a switch case, so the case only matches if both the pattern matches AND the guard evaluates to true—letting you express conditional logic within pattern matching without nesting an if inside every case body.

String classify(int n) => switch (n) {
  int() when n < 0 => 'negative',
  int() when n == 0 => 'zero',
  _ => 'positive',
};

What is the difference between == and identical() in Dart?

== is value equality and can be overridden by a class to compare fields. identical(a, b) checks true reference/object identity—whether both variables point to the exact same object in memory—and cannot be overridden.

final a = Point(1, 2);
final b = Point(1, 2);
print(a == b);         // true if Point overrides ==
print(identical(a, b)); // false, different instances

Why should you override toString() on custom classes?

By default, toString() returns something unhelpful like Instance of 'User'. Overriding it to return meaningful field values makes debugging (print statements, debugger output, test failure messages) far more useful.

class User {
  final String name;
  User(this.name);
  @override
  String toString() => 'User(name: $name)';
}

What is the difference between HashMap, LinkedHashMap, and SplayTreeMap in dart:collection?

HashMap offers no guaranteed iteration order but the fastest average operations. LinkedHashMap (what Dart's default {} map literal actually uses) preserves insertion order while iterating. SplayTreeMap keeps keys sorted according to a comparator, at the cost of O(log n) operations instead of average O(1).

What is Queue in dart:collection, and why use it instead of List?

Queue<T> (backed by a doubly-linked list, via ListQueue) supports efficient O(1) insertion/removal at both ends (addFirst/removeFirst/addLast/removeLast), unlike a plain List where removing from the front is O(n) because remaining elements must shift.

import 'dart:collection';
final queue = Queue<int>();
queue.addLast(1);
queue.addFirst(0);
print(queue.removeFirst()); // 0

How does garbage collection work in Dart, at a high level?

Dart uses a generational garbage collector: short-lived objects (most allocations, like temporary widget instances) are collected quickly in a fast 'young generation' scavenger, while long-lived objects that survive multiple collections are promoted to an 'old generation' collected less frequently with a more thorough mark-and-sweep/compact pass.

What is tree shaking in Dart's AOT compilation?

Tree shaking analyzes which code is actually reachable from the app's entry point and strips out unused classes, functions, and even unused icon glyphs from a font, reducing final binary size. This is only fully effective in AOT release builds—debug/JIT builds keep everything for fast iterative recompilation.

What are Future.value and Future.error used for?

Future.value(x) creates an already-completed Future wrapping a value—useful for satisfying a Future-returning interface with a synchronously-known result. Future.error(e) creates an already-failed Future—useful for returning a consistent error type from a function that must always return a Future, even in an early-exit error path.

Future<int> getCached(int? cached) {
  if (cached != null) return Future.value(cached);
  return fetchFromNetwork();
}

How do you transform a Stream's data using map/where/transform?

Like Iterables, stream.map() and stream.where() return a new Stream that lazily applies the transformation/filter as events flow through, without needing to buffer or wait for the source stream to finish. transform() applies a custom StreamTransformer for more complex reusable pipeline logic.

final evens = numberStream.where((n) => n.isEven).map((n) => n * 2);

What can you do with a StreamSubscription after calling stream.listen()?

The returned StreamSubscription lets you pause() (buffer events without processing), resume(), and cancel() (stop listening entirely and release resources)—essential for cleaning up subscriptions in a widget's dispose() to avoid calling setState on an unmounted widget.

late StreamSubscription sub;

@override
void initState() {
  super.initState();
  sub = stream.listen((data) => setState(() {}));
}

@override
void dispose() {
  sub.cancel();
  super.dispose();
}

What is FutureOr<T>, and when is it used?

FutureOr<T> declares that a value can be either a plain T or a Future<T>—used in APIs (like Future.then's callback) that need to accept either a synchronous or asynchronous result without forcing the caller to always wrap synchronous values in Future.value().

FutureOr<int> maybeAsync(bool useAsync) {
  return useAsync ? Future.value(1) : 1;
}

What do part and part of do in Dart?

part 'file.dart'; and part of 'library.dart'; split a single logical library across multiple files while sharing the same top-level scope (including private members)—commonly used by code generators (e.g. Freezed, json_serializable) to keep generated code in a sibling .g.dart file that can still access the original file's private members.

// user.dart
part 'user.g.dart';

// user.g.dart
part of 'user.dart';

What do the show, hide, and as keywords do when importing a library in Dart?

show imports only the listed names from a library. hide imports everything except the listed names. as prefix imports the library under a namespace prefix, useful for resolving naming conflicts between two libraries that export the same identifier.

import 'package:collection/collection.dart' show ListEquality;
import 'package:http/http.dart' as http;

What is the role of the main() function in a Dart program?

main() is the required entry point of every Dart program/app. It can optionally accept List<String> args for command-line arguments (in server/CLI Dart), and can be declared async when startup requires awaiting async initialization (like Firebase.initializeApp()) before calling runApp().

void main(List<String> args) {
  print('Hello, ${args.isNotEmpty ? args[0] : 'World'}');
}

How does privacy/encapsulation work in Dart, given there's no 'private' keyword?

Dart enforces privacy at the library (file) level, not the class level: any identifier prefixed with an underscore (_name) is only visible within the same library. There's no true class-level 'private' like in Java—two classes in the same file can access each other's underscore-prefixed members.

class _InternalHelper {
  int _secret = 42;
}

Why is StringBuffer preferred over repeated string concatenation in a loop?

Since Dart strings are immutable, each + concatenation in a loop allocates a brand-new string, making repeated concatenation O(n²) overall. StringBuffer accumulates pieces internally and builds the final string once with toString(), making it O(n).

final sb = StringBuffer();
for (final word in words) {
  sb.write(word);
  sb.write(' ');
}
final result = sb.toString();

What do runtimeType and noSuchMethod provide on Dart's Object class?

runtimeType returns the actual runtime Type of an object (useful for debugging/logging, though comparing types with is is usually preferred for logic). noSuchMethod can be overridden to intercept calls to undefined methods/properties—used by some mocking frameworks and dynamic proxy-style APIs.

What do Enum.values and enum.index provide in Dart?

MyEnum.values returns a List of all declared enum constants in declaration order—handy for building a dropdown of all options. instance.index returns that constant's zero-based position in the declaration order, useful for serialization to a compact integer form (though this breaks if you reorder the enum later).

enum Status { active, inactive }
print(Status.values); // [Status.active, Status.inactive]
print(Status.active.index); // 0

What is a redirecting constructor in Dart?

A redirecting constructor forwards to another constructor of the same class instead of having its own body, avoiding duplicated initialization logic between multiple named constructors.

class Point {
  final double x, y;
  Point(this.x, this.y);
  Point.origin() : this(0, 0); // redirects to the main constructor
}

How do conditional imports work in Dart, and why are they used?

Conditional imports pick a different implementation file at compile time based on which platform library is available (e.g. dart.library.io vs dart.library.html), letting the same public API compile correctly for both native and web targets even though their underlying implementations differ completely.

import 'storage_stub.dart'
  if (dart.library.io) 'storage_io.dart'
  if (dart.library.html) 'storage_web.dart';

When should you use a top-level function versus a static method in Dart?

A top-level function (declared outside any class) is best for standalone utility logic unrelated to a specific type. A static method is better when the logic is conceptually tied to a class (e.g. a factory-like helper or a named constant computation), improving discoverability via the class namespace and avoiding polluting the global namespace.

What is the time complexity of List.removeAt(0) versus List.removeLast() in Dart?

removeLast() is O(1) since it only touches the end. removeAt(0) (or any removal from the front/middle) is O(n) because every subsequent element must shift left by one to fill the gap. If you frequently remove from the front, a Queue (doubly-linked list) is the better data structure.

What is the difference between using .then()/.catchError() and async/await for handling a Future?

.then()/.catchError() is the callback-chaining style—functionally equivalent to async/await but can become harder to read once chained several levels deep. async/await with try/catch reads like synchronous code and is generally preferred for readability, though both compile to the same underlying Future machinery.

// callback style
fetchUser().then((user) => print(user)).catchError((e) => print(e));

// async/await style
try {
  final user = await fetchUser();
  print(user);
} catch (e) {
  print(e);
}

What happens if a Future's error is never caught?

An uncaught error on a Future is reported to the current Zone's uncaught error handler—in a Flutter app without a custom zone/handler, this typically crashes the app or prints an 'Unhandled exception' to the console. This is exactly why production apps wrap runApp() in runZonedGuarded to catch these globally.

What is the difference between cascade notation (..) and regular method chaining (.)?

Regular chaining (.) calls a method on the result of the previous call, so the receiver changes at each step. Cascade notation (..) calls each method on the same original object, ignoring each call's return value, and the whole cascade expression evaluates to that original object.

final list = <int>[]..add(1)..add(2); // cascade: works even though add() returns void

What is the difference between a static factory method and a factory constructor in Dart?

A static method that returns an instance is just a regular function attached to the class namespace—callers must know it's not a 'real' constructor. A factory constructor is called with ClassName(...) syntax like a normal constructor, but internally can return a cached/subtype instance—preserving normal constructor call syntax while still allowing that flexibility.

What does the null-aware spread operator (...?) do?

...? spreads a collection into a literal only if it's non-null, safely skipping it (rather than throwing) if the source collection is null—avoiding a manual null check before spreading.

List<int>? maybeNulls;
final combined = [1, 2, ...?maybeNulls]; // [1, 2] if maybeNulls is null

When a class applies multiple mixins, what determines which implementation wins if they define the same method?

Mixins are applied left-to-right, and each one 'layers' on top of the previous, similar to a linear inheritance chain. The rightmost mixin in the with clause takes precedence for any conflicting method, since it's applied last and is closest to the concrete class in the resulting chain.

class C extends A with MixinX, MixinY {} // MixinY's methods win over MixinX's on conflict

What requirements must a class satisfy to have a const constructor?

All of the class's fields must be final, and the constructor itself must be marked const. Any field values passed in when creating a const instance must themselves be compile-time constants. This guarantees the resulting object is fully immutable and safe to canonicalize/reuse.

class Point {
  final double x, y;
  const Point(this.x, this.y);
}
const p = Point(1, 2);

What is a 'mixin class' in Dart 3, and how does it differ from a plain mixin?

A mixin class can be used both as a regular class (extended, instantiated) and as a mixin (applied with with)—unifying what previously required declaring separate class and mixin definitions if you wanted both capabilities for the same behavior.

mixin class Loggable {
  void log(String msg) => print('[LOG] $msg');
}
// usable as: class Service extends Loggable {}
// or as:     class Service2 with Loggable {}