Top 30 Flutter & Dart Interview Questions and Answers
30 multiple-choice questions on Flutter & Dart, of the kind that come up in a technical interview, drawn from 30 bites in the Flutter & Dart library. Answer them here or read straight down. Every question carries the correct option, why it is correct, and a link to the bite it came from.
Flutter framework, Dart language, packages, Impeller
30 questions. Pick an answer, or open “Show the answer” to read it.
Answers are graded in your browser. Nothing is saved, and no XP or streak is earned here. The app keeps score.
Question 1 of 30
Which statement accurately describes a key difference between final and const in Dart?
Show the answer
Answer: a · A final variable can hold a runtime value like DateTime.now(), while const requires a compile-time constant.
final allows single assignment at runtime, so DateTime.now() is valid, whereas const requires a compile-time constant and deep immutability. Option D is wrong because final only prevents reassignment of the variable, not mutation of the object's contents.
Read the full bite: What is the difference between final and const in Dart?
Question 2 of 30
Which statement accurately describes Dart's positional and named parameter rules?
Show the answer
Answer: c · Optional positional parameters use square brackets and must appear before named ones.
The card states optional positional parameters use square brackets and must precede named ones, while named parameters use curly braces and can be required. D is the most tempting distractor because it repeats the common red-flag misconception that optional positional parameters use curly braces.
Read the full bite: Explain Dart positional vs named parameters and write one signature
Question 3 of 30
When accessing a property on a nullable object in Dart, what is the fundamental difference between using ?. and !?
Show the answer
Answer: c · ?. evaluates to null when the receiver is null, while ! casts to non-nullable and may throw at runtime.
The null-aware operator ?. short-circuits and yields null if the receiver is null, whereas ! forcibly casts away nullability and throws a runtime exception if the value is actually null. Option D is wrong because it confuses ?. with the ?? default-value operator and incorrectly suggests ! is compile-time safe.
Read the full bite: Describe Dart's null-aware ?. and null assertion ! operators
Question 4 of 30
A class declares late final UserService repo and assigns it in initState. What happens if repo is read in build before that assignment?
Show the answer
Answer: a · A LateInitializationError is thrown at the point of access
late shifts definite-assignment checks from compile time to runtime, so accessing the field before assignment throws LateInitializationError. Distractor C is tempting because that is exactly the compile-time error you would receive if the field were not marked late.
Question 5 of 30
Given List<int> nums = [1, 2, 3, 4] and bool includeZero = true, which literal produces [0, 2, 4]?
Show the answer
Answer: a · [if (includeZero) 0, for (var n in nums) if (n.isEven) n]
Option A correctly uses collection-if to prepend 0 and collection-for with a nested collection-if to filter evens inside one literal. Option B is tempting because where looks declarative, but without the spread operator it inserts the Iterable object itself as a single element rather than flattening its contents.
Read the full bite: How do you use collection-if and collection-for to declaratively build a list?
Question 6 of 30
Which statement accurately describes how Dart closures handle captured variables from an enclosing function?
Show the answer
Answer: d · They capture the variable binding by reference in a heap-allocated environment
Dart closures capture the variable binding by reference in the heap, so the inner function retains access even after the outer function returns and its stack frame is popped. This makes B incorrect because the enclosing function's stack frame does not remain on the call stack after it returns.
Read the full bite: What is a Dart closure? Write a function that returns a function.
Question 7 of 30
When a variable is declared as dynamic, why does calling toIntOrDefault on it fail to compile?
Show the answer
Answer: b · Because extension methods are resolved statically and dynamic bypasses static type information
The card explains that extension methods are static sugar resolved at compile time based on the declared type, so dynamic variables bypass static extension resolution. Option A is tempting because it uses precise-sounding terminology, but it incorrectly claims runtime resolution, which contradicts the fundamental static nature of Dart extensions.
Read the full bite: What are Dart extension methods? Implement toIntOrDefault on String.
Question 8 of 30
What is the primary advantage of using a sealed class instead of an enum for exhaustiveness-checked state machines in Dart 3?
Show the answer
Answer: c · Sealed classes let each subtype carry distinct constructor parameters and fields while keeping exhaustive switch safety.
Sealed classes enable algebraic data types where each variant holds distinct payload data while the compiler verifies exhaustive handling, unlike enums which share a single shape. Distractor A is wrong because sealed subclasses must remain in the same library for the compiler to prove exhaustiveness; adding them elsewhere breaks that guarantee.
Read the full bite: Explain Dart switch exhaustiveness for enums versus sealed classes
Question 9 of 30
In a Dart isolate, what is the consequence of a microtask that keeps rescheduling new microtasks recursively?
Show the answer
Answer: a · The event queue is starved until the recursive microtask chain ends
The event loop always drains the entire microtask queue before processing any event queue tasks, so a self-rescheduling microtask chain blocks the event queue indefinitely. Option C is wrong because Dart does not interleave the two queues one-for-one.
Read the full bite: Explain Dart's event loop, microtask queue, event queue, and await behavior
Question 10 of 30
In Dart, what is the result of building a Map<String, User> with {for (var u in users) u.id: u} when two users share the same id?
Show the answer
Answer: c · The last user encountered silently overwrites the previous entry
Dart map literals silently overwrite duplicate keys, so the last user wins without throwing. Many candidates incorrectly assume a runtime exception occurs, but Dart LinkedHashMap simply replaces the previous value.
Read the full bite: Convert List<User> to Map<String, User> keyed by user id
Question 11 of 30
Which statement accurately describes the difference between final and const fields when declared inside a Dart class?
Show the answer
Answer: c · const fields must be static, while final fields can be instance fields
Inside a Dart class, const fields must be static because they are compile-time constants, whereas final fields may be instance-level since they are assigned once at runtime. Distractor A reverses this relationship, a red flag the card specifically identifies.
Read the full bite: Difference between final and const Dart class properties?
Question 12 of 30
When deduplicating emails in Dart, why is emails.toSet().toList() preferred over manually looping with contains?
Show the answer
Answer: d · It preserves first-occurrence order and runs in average O(n) time instead of O(n^2)
Converting to a Set uses Dart's LinkedHashSet to drop duplicates in average O(n) time while keeping the first occurrence of each email in its original position. Option B is tempting because distinct sounds like a standard operation, but it is not part of Dart's core Iterable API and comes from external packages like RxDart.
Question 13 of 30
Which statement accurately contrasts Dart's extends, implements, and with keywords for class reuse?
Show the answer
Answer: a · extends inherits from one superclass; implements requires reimplementing every public member; with injects behavior without subtyping.
Option A is correct because extends provides single inheritance of implementation, implements forces the class to reimplement every public member itself, and with injects reusable behavior without creating an is-a relationship. Option B is wrong because extends does not allow multiple superclasses, implements does not reuse parent code, and with does not establish an is-a inheritance chain.
Read the full bite: What are the key differences between Dart extends, implements, and with?
Question 14 of 30
Which statement accurately describes a semantic difference between a Dart factory constructor and a generative constructor?
Show the answer
Answer: c · A factory constructor can return an existing cached instance or a subtype, while a generative constructor always produces a fresh instance of the exact class.
A factory constructor is not required to create a new instance and may return a cached object or subtype, whereas a generative constructor always allocates a fresh instance of the exact class. Option B is tempting because factories resemble static methods, but they remain part of the constructor namespace and are called with standard constructor syntax.
Read the full bite: What is a Dart factory constructor and common use cases?
Question 15 of 30
You need to parse a dynamic JSON array into List<Product> where some objects may contain missing or mistyped fields. Which approach best preserves type safety and prevents runtime crashes?
Show the answer
Answer: a · Use a factory constructor that checks runtime types and defaults, then map over the list while isolating or skipping invalid rows.
Defensive parsing inside a factory constructor centralizes validation and runtime type checks in the model layer, while isolating or skipping invalid rows during mapping prevents one malformed item from crashing the entire list. Option C spreads uncertainty by pushing null handling into the UI, and C fails because a Map cannot be directly cast to a custom class.
Read the full bite: How do you model Product and safely parse JSON into List<Product>?
Question 16 of 30
In Dart, given class C extends S with M1, M2 where all define foo(), if C overrides foo() and calls super.foo(), which implementation does the call reach?
Show the answer
Answer: a · It reaches M2's implementation because the rightmost mixin becomes the immediate superclass.
Dart applies mixins left-to-right, so the last mixin becomes the immediate superclass and shadows earlier ones; thus super.foo() from C resolves to M2. Distractor B confuses application order with inheritance priority, but left-to-right application means M2 is layered on top of M1, not beneath it.
Read the full bite: How does Dart resolve mixin method conflicts and application order?
Question 17 of 30
Which statement accurately describes how to consume a Future<String> and handle errors idiomatically in Dart?
Show the answer
Answer: b · Use an async function with try/catch around an await, or chain .then() with .catchError() on the Future.
Option B correctly identifies both callback-style and async/await patterns for handling values and errors. Option A is wrong because a Future<String> is a pending object, not an actual String, so assigning it directly causes a type mismatch and synchronous try/catch cannot catch its asynchronous errors.
Question 18 of 30
What is the practical consequence of declaring an async Dart function as void instead of Future<void>?
Show the answer
Answer: a · The caller gets no Future handle, so they cannot await completion or catch async errors.
A void return type hides the implicit Future from the caller, which prevents awaiting completion and catching errors with try/catch. Option C is tempting but wrong because without a Future handle, exceptions become uncaught async errors instead of propagating to the caller.
Read the full bite: Difference between Future<void> and void from an async function
Question 19 of 30
In main(), sync prints 1 and 6 surround a Future(2), microtask(3), Future(4), and microtask(5). What is the output order?
Show the answer
Answer: d · 1, 6, 3, 5, 2, 4
The event loop finishes all synchronous code first, then fully drains the microtask queue before processing any event queue callbacks, yielding 1, 6, 3, 5, 2, 4. Option B is tempting because it follows call order, but Dart strictly processes entire microtask batches before touching events.
Read the full bite: Describe the Dart event loop and queue execution order
Question 20 of 30
What happens when you call listen() again on a single-subscription Dart stream after the first listener has finished?
Show the answer
Answer: d · It throws a StateError because only one listener is ever permitted
A single-subscription stream allows exactly one listener over its entire lifetime, so calling listen again throws a StateError even after the first listener completes. The idea that it resumes where the first left off is a common misconception because the runtime enforces the one-listener contract strictly, treating the stream as consumed rather than pausable.
Read the full bite: Explain single-subscription vs broadcast Streams in Dart
Question 21 of 30
When using Future.wait to fetch user profiles concurrently, how do you isolate individual failures while preserving successful results?
Show the answer
Answer: b · Attach catchError to each future before passing the list to Future.wait, then filter out nulls afterward
Attaching catchError to each future guarantees every item resolves, so Future.wait yields a full list where nulls represent failures that can be filtered out. Wrapping Future.wait in try-catch is a common mistake because it catches the batch error but discards all successful profiles that had already completed.
Read the full bite: Fetch user profiles concurrently and handle individual failures
Question 22 of 30
When managing a StreamController inside a StatefulWidget, what is the primary risk of omitting close() in the dispose method?
Show the answer
Answer: c · The controller retains listeners and internal resources, leaking memory and preventing graceful isolate shutdown.
The correct answer reflects that an open controller holds references to listeners and resources, which leaks memory and can prevent an isolate from shutting down. The most tempting distractor is the first option because it reverses the actual StateError behavior—adding events after close throws, not omitting close itself.
Read the full bite: Explain StreamController, write a broadcast stream example, and why close it?
Question 23 of 30
You need to build a Flutter button that increments a local counter and updates its label each time it is pressed. Which design choice follows Flutter's widget model correctly?
Show the answer
Answer: c · Use a StatefulWidget because the mutable counter belongs in the State object, which persists across rebuilds while the widget stays immutable.
The correct answer reflects the two-class architecture: the immutable widget is recreated, but the State object persists and owns mutable data like a counter. Distractor B is tempting because beginners often believe the StatefulWidget class itself stores mutable fields, yet the card clarifies that mutability lives only in the separate State object.
Read the full bite: Explain StatelessWidget vs StatefulWidget and when to choose each
Question 24 of 30
Which of the following is safe to do inside a State object's build method?
Show the answer
Answer: c · Read inherited properties like Theme from BuildContext and return a widget
Build must remain a pure, declarative function that returns a widget based on current state and context, so reading inherited data is appropriate. Fetching data, mutating state, or initializing controllers are side effects that violate the declarative contract and belong in lifecycle methods like initState.
Read the full bite: What is the build method's purpose and key constraints?
Question 25 of 30
When a Text widget rebuilds with a new string but the same key, what happens to its Element and RenderObject?
Show the answer
Answer: b · The existing Element compares the new Widget and, if compatible, updates the RenderObject without recreating either object.
Because the new Widget matches the existing Element's type and key, the Element is updated in place and calls updateRenderObject on the existing RenderObject, avoiding recreation of both. Distractor A is tempting but wrong because the Element is also reused, not recreated, which preserves state and keeps rebuilds inexpensive.
Read the full bite: Relationship between Widget, Element, and RenderObject trees and their benefits
Question 26 of 30
Why does Scaffold.of(context) return null when called from a build method that returns a Scaffold?
Show the answer
Answer: a · The build method's context belongs to the widget above the Scaffold, so the Scaffold is not an ancestor.
The context in a build method belongs to the widget being built, which sits above the returned Scaffold, so the Scaffold is a descendant and invisible to ancestor lookups like Scaffold.of. Distractor C repeats the common misconception that BuildContext is the widget itself, when it is actually a handle to the Element representing the widget's position in the tree.
Read the full bite: What is BuildContext? Give two tree-interaction examples.
Question 27 of 30
In Flutter, a parent widget rebuilds frequently due to state changes, but its child displays static data. Which approach correctly prevents the child's build method from running unnecessarily?
Show the answer
Answer: c · Extract the child into its own StatelessWidget and invoke it with a const constructor.
Extracting the child into its own StatelessWidget and invoking it with const creates a build boundary, allowing Flutter to reuse the existing element and skip calling build on that subtree. Wrapping it in a RepaintBoundary is a common misconception because that only reduces paint cost during rasterization, not build-phase work.
Read the full bite: How can you prevent unnecessary child rebuilds in Flutter?
Question 28 of 30
In Flutter, two sibling widgets need to read and modify the same piece of data. Where should that state live?
Show the answer
Answer: b · In the closest common ancestor widget, passed down via constructors and callbacks
Placing the state in the closest common ancestor ensures both siblings receive the value and can send events back up via callbacks. Keeping the state inside one child is incorrect because the sibling cannot access it declaratively, violating Flutter's unidirectional data flow.
Read the full bite: Explain lifting state up with a concrete scenario and benefits
Question 29 of 30
What is the effect of calling setState inside didUpdateWidget when reacting to a parent configuration change?
Show the answer
Answer: b · It schedules a second build, making the call redundant
Flutter guarantees that build follows didUpdateWidget, so setState inside it merely queues an extra unnecessary frame. The suppressing-build option is wrong because the framework-triggered build is never canceled by a nested setState call.
Question 30 of 30
When a ReorderableListView child is dragged to a new index without a Key, what happens to its underlying Element during reconciliation?
Show the answer
Answer: d · It remains at the original index and receives whatever widget now occupies that slot.
Without a Key, Flutter matches by runtime type and tree position, so the Element stays at its original slot and is updated with the new widget reference there, leaving its State behind. Option A describes what happens only when a stable Key provides explicit identity, allowing Flutter to reparent the Element to its new position.
Read the full bite: What are Keys in Flutter and why are they critical?
Could you explain these out loud?
That is what an interview actually tests. Tezvyn gives you questions like these with what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.