Flutter Developer interview questions
100 real questions with model answers and explanations for Flutter Developer candidates.
See a Flutter Developer resume example →Practice with flashcards
Spaced repetition · Hunter Pass
Questions
I make absence explicit in the type and avoid weakening the model with broad null assertions.
- A field that may genuinely be absent is T?, and a successful null check promotes a stable local variable to T.
- For required staged initialization, I prefer a constructor or factory; late is reserved for an enforced initialization contract and throws if read too early.
- The ! operator is only appropriate at a boundary where an external invariant has already been checked, because it converts a compiler warning into a possible runtime failure.
Why interviewers ask this: The interviewer checks whether null safety informs the model rather than being bypassed with late and !.
I use a bound to state the operations T must support, then account for Dart's covariant generic classes at mutation boundaries.
- T extends Object excludes null, while a domain bound such as T extends Comparable<T> lets the implementation call compareTo safely.
- Because List<Cat> is a subtype of List<Animal>, writing a Dog through an Animal view is guarded by a runtime type check and can fail.
- For callbacks I follow function variance: parameter types are contravariant and return types covariant, so the accepted producer or consumer type matters.
Why interviewers ask this: A strong answer connects generic constraints to usable operations and variance to real substitution risks.
I use an extension method to add focused compile-time API to a type I do not own without changing its runtime identity.
- Resolution uses the receiver's static type, so an extension is not virtual dispatch and a value typed as dynamic will not use it.
- A real instance member wins over an extension member, and overlapping equally specific extensions can make the call ambiguous.
- I resolve a collision with an explicit extension override or import prefixes rather than relying on a cast that obscures intent.
Why interviewers ask this: The interviewer evaluates whether the candidate understands static extension resolution and collision handling.
I choose a class modifier by deciding which forms of external subtyping the package promises to support as its API evolves.
- A base class permits external inheritance but not implementation, so subclasses inherit its implementation and must remain base, final, or sealed, leaving room to add concrete members safely.
- An interface class permits external implementation but not inheritance, which exposes a substitutability contract and makes adding a required member a breaking change for implementers.
- A final class blocks external subtyping, while a sealed class also defines a library-controlled subtype family for exhaustive switching, so adding a sealed variant deliberately creates migration work.
Why interviewers ask this: A strong answer treats class modifiers as compatibility guarantees for package authors and consumers, not merely inheritance syntax.
I use an extension type when an existing representation needs a distinct static API without paying for a wrapper object at runtime.
- In extension type UserId(String value), String is the representation type and value is the representation field; declared members expose only the domain operations the boundary should offer.
- The value is represented at runtime by the underlying String, so calls and storage can avoid wrapper allocation, but runtimeType, dynamic code, and runtime inspection cannot rely on a distinct UserId object.
- This zero-cost boundary suits validated IDs and interop values when compile-time separation is enough; I choose a class when runtime identity, encapsulated mutable state, or an unpierceable runtime boundary matters.
Why interviewers ask this: The interviewer checks whether the candidate balances static abstraction and allocation savings against representation leakage at runtime boundaries.
After synchronous code finishes, Dart drains microtasks before taking the next event from the event queue.
- Future.microtask and scheduleMicrotask run ahead of timers, I/O completions, and other queued events already waiting.
- A long synchronous callback or a self-replenishing microtask chain starves rendering and input even though the code is asynchronous in shape.
- I use microtasks only for small ordering-sensitive work and schedule ordinary deferred work as events or move CPU work to an isolate.
Why interviewers ask this: A strong answer explains both ordering and how misuse can starve a Flutter frame.
A Future API should complete once with either a value or an error and preserve one clear asynchronous error path.
- A function declared async converts a thrown exception into an errored Future, including throws before its first await.
- then can provide onError, but catchError must return a value compatible with the Future type if it recovers; otherwise I rethrow with stack context.
- I await or return every meaningful Future so errors reach the caller instead of becoming unhandled zone errors.
Why interviewers ask this: The interviewer evaluates whether asynchronous failures remain typed, observable, and correctly propagated.
I choose a single-subscription stream for one owned sequence and a broadcast stream for independent observers of live events.
- A single-subscription stream can be listened to once and is suitable for file, socket, or generated data whose source should follow one consumer.
- A broadcast stream may drop events when nobody is listening, and pausing one subscription does not globally pause the producer for every listener.
- pause and resume can apply backpressure when the source honors them; otherwise buffering must be bounded or the protocol must define dropping, sampling, or acknowledgement.
Why interviewers ask this: A strong answer covers listener semantics and does not assume Streams automatically solve producer pressure.
An isolate has its own event loop and mutable heap, so coordination happens through messages rather than shared mutable objects.
- SendPort messages must be sendable; mutable object graphs are logically copied, while the runtime may optimize immutable values.
- TransferableTypedData moves large byte payloads without retaining two usable mutable copies, which is better for image or file processing.
- I keep messages coarse enough to offset spawn and serialization costs and return plain results or domain DTOs to the UI isolate.
Why interviewers ask this: The interviewer checks whether isolates are chosen with ownership and transfer costs in mind.
The garbage collector reclaims unreachable Dart objects, but it does not provide deterministic release of external resources.
- Each isolate owns its heap, and references from roots such as stacks, statics, closures, and active callbacks keep objects reachable.
- Short-lived and long-lived objects are handled by generational collection, so retaining a large graph through one listener can promote and preserve the whole graph.
- Sockets, stream subscriptions, controllers, and native handles need explicit close or dispose; Finalizer is only a fallback because collection timing is unspecified.
Why interviewers ask this: A strong answer separates managed memory reachability from deterministic lifecycle ownership.
Flutter retains an existing Element only when the new widget has the same runtimeType and key, making identity a deliberate part of stateful tree design.
- When both match, the Element updates in place and a StatefulElement keeps its State, with didUpdateWidget receiving the previous configuration.
- When either differs, Flutter deactivates the old Element and inflates a new one at that slot, so local State is not transferred and is disposed if the old Element is not reused.
- A matching GlobalKey can reparent its existing Element and State to another location in the same frame, but uniqueness, deactivation, and inherited-dependency updates make it a costly tool for intentional subtree identity.
Why interviewers ask this: A strong answer connects runtimeType and key matching to State retention and explains GlobalKey reparenting as a controlled exception.
A ParentDataWidget writes parent-specific layout metadata onto its child's RenderObject for the matching parent RenderObject to consume.
- Expanded applies FlexParentData such as flex and fit for a Row, Column, or Flex, while Positioned applies StackParentData coordinates for a Stack.
- The ParentDataWidget must reach a child RenderObject owned by that compatible render parent without an intervening incompatible RenderObjectWidget, or the metadata type and layout protocol no longer agree.
- To diagnose the error, I inspect the ownership chain named in the message, compare the expected ParentData type with the actual render parent, and move the widget under the parent that owns that contract.
Why interviewers ask this: The interviewer checks whether ParentDataWidget placement is understood as a typed parent-child layout contract rather than a generic constraints issue.
Slivers participate in a viewport protocol that lays out only the portion needed for the current scroll position and cache extent.
- SliverConstraints include scroll offset, axis, remaining paint extent, and cross-axis extent rather than ordinary BoxConstraints alone.
- A sliver returns SliverGeometry describing paint extent, scroll extent, and visibility so the viewport can compose headers, lists, and grids.
- CustomScrollView lets SliverAppBar, SliverList, and SliverGrid share one coherent scroll position without nesting competing scrollables.
Why interviewers ask this: The interviewer evaluates whether the candidate understands lazy viewport layout beyond using ListView by habit.
Build updates configuration, layout computes geometry, paint records drawing commands, and compositing assembles layers for the engine.
- setState marks an element dirty for build; changed parent data or render properties may then mark a RenderObject for layout or paint.
- Layout follows constraints through the render tree, while paint writes into a retained layer structure rather than directly changing screen pixels.
- Compositing sends the resulting layer scene to the engine, where rasterization turns it into a frame.
Why interviewers ask this: A strong answer separates the phases and the invalidation each kind of change requires.
RepaintBoundary isolates a render subtree so a repaint on one side does not automatically repaint the other side.
- It helps when a frequently animated child sits beside expensive mostly static content, which can then reuse a cached layer.
- It does not prevent widget rebuilds or layout, so adding it cannot fix every kind of frame cost.
- Each boundary can add a composited layer and memory overhead, so I add it around measured repaint hotspots rather than every widget.
Why interviewers ask this: The interviewer checks whether repaint isolation is applied to a measured paint problem with its layer cost understood.
Local keys identify siblings under one parent, while a GlobalKey can identify and reparent one element across the whole app.
- ValueKey works when stable domain identity distinguishes repeated children, and ObjectKey uses object identity.
- GlobalKey can expose State or BuildContext and preserve a subtree during reparenting, but that work triggers dependency updates and is comparatively expensive.
- I avoid creating keys inside build and prefer callbacks or controllers over GlobalKey when cross-tree identity is not actually required.
Why interviewers ask this: A strong answer chooses the narrowest identity mechanism and explains reconciliation cost.
InheritedWidget lets descendants register a dependency that rebuilds them when the inherited value meaningfully changes.
- Calling dependOnInheritedWidgetOfExactType records the current Element as a dependent; a non-listening lookup does not.
- updateShouldNotify compares old and new widgets and should report only changes relevant to consumers of that inherited value.
- For large models I use selectors or InheritedModel-style aspects so one field change does not rebuild every consumer.
Why interviewers ask this: The interviewer evaluates dependency registration, notification, and rebuild granularity.
I schedule work according to whether it must affect the current frame, run after it, or wait as ordinary asynchronous work.
- A persistent frame callback such as a Ticker drives animation each frame, while a transient callback schedules one frame-bound update.
- addPostFrameCallback runs after the current frame is flushed and is appropriate for reading final layout, but it does not itself request another frame.
- I avoid starting a recurring post-frame loop because it can hide lifecycle bugs and consume every frame.
Why interviewers ask this: A strong answer distinguishes scheduler phases rather than treating post-frame callbacks as a general delay mechanism.
At 120 Hz the whole frame is about 8.3 ms, so UI and raster work must both remain consistently below their deadlines.
- Build, layout, and paint execute on the UI isolate, where synchronous parsing or broad rebuilds delay scene production.
- Raster work includes drawing and layer effects, so expensive clipping, saveLayer, or oversized images can miss the raster deadline even with a fast build.
- I inspect UI and raster timelines in profile mode and reduce the phase that actually exceeds budget rather than optimizing widget count blindly.
Why interviewers ask this: The interviewer checks whether performance decisions are tied to phase-specific frame timing.
I write a custom RenderObject only when the feature needs a layout, paint, or hit-test protocol that widget composition cannot express efficiently.
- Examples include one-pass positioning of many children, unusual parent data, or tightly coupled painting and semantics.
- The RenderObject must implement constraint-respecting layout, invalidation setters, painting, hit testing, and accessibility semantics where applicable.
- For ordinary decoration or drawing I prefer CustomPainter, and for standard layouts I keep composition because it is easier to test and maintain.
Why interviewers ask this: A strong answer sets a high practical threshold and names the responsibilities custom rendering creates.
Locked questions
- 21
How do you choose among Riverpod provider types for a feature?
state-management - 22
What is the difference between ref.watch, ref.read, and ref.listen in Riverpod?
state-management - 23
What does autoDispose change in a Riverpod provider's lifecycle?
state-managementprovider-lifecycle - 24
How would you use a Riverpod family without creating an accidental cache leak?
state-managementcaching - 25
What do Riverpod code generation and provider overrides contribute to an application design?
state-managementdesign - 26
When would you choose BLoC over Cubit for a feature?
state-management - 27
How do BLoC event transformers change concurrent event handling?
nlpconcurrencystate-management - 28
How would you model immutable normalized state for a Flutter feature?
flutternormalizationimmutability - 29
Where should derived state be computed?
- 30
Where do side effects belong in a state-management design?
design - 31
How would you structure feature modules in a medium-sized Flutter application?
flutter - 32
What does Clean Architecture mean in a practical Flutter feature?
clean-architectureflutter - 33
What responsibility should a repository have in a Flutter data layer?
flutterrepository-pattern - 34
Why map DTOs into domain models instead of using generated API classes everywhere?
data-transfer-objects - 35
How would you apply dependency injection in Flutter without hiding the object graph?
flutterinjectiondependencies - 36
How would you represent failures across a Flutter feature?
flutter - 37
What conflict policy would you define for an offline-first editable record?
- 38
How would you express cache freshness for a mobile screen?
caching - 39
How would you design cursor pagination state in a Flutter client?
flutterpaginationdesign - 40
How would you keep an evolving GraphQL API and WebSocket update contract compatible with a Flutter client?
graphqlwebsocketsflutter - 41
When would you use MethodChannel directly instead of Pigeon?
platform-channelspigeon - 42
When is Dart FFI a better choice than a platform channel?
platformdartdart-ffi - 43
How should a Flutter plugin handle native lifecycle changes?
flutter - 44
What performance model should a Flutter developer understand about Impeller?
flutterrenderingperformance - 45
How would you control image memory in a Flutter application?
memoryimage-memoryflutter - 46
How do tree shaking and build configuration affect Flutter app size?
configtree-shakingapp-size - 47
What test pyramid would you use for a Flutter feature?
pyramidtest-pyramidflutter - 48
How do you keep Flutter golden tests stable and useful?
golden-testsflutter - 49
What belongs in a Flutter integration test instead of a widget test?
flutterwidgetsintegration - 50
What release gates would you put in CI for a Flutter application?
flutter - 51
A quantity change in one cart row rebuilds all 80 rows, and DevTools reports about 300 widget builds per tap. How would you find and stop the rebuild storm?
widgetsflutter-devtools - 52
A 60 Hz device misses the 16 ms frame budget while opening a product sheet. The UI thread takes 24 ms but raster takes 6 ms. What would you investigate?
frame-performanceconcurrency - 53
A screen nests a ListView with shrinkWrap inside a SingleChildScrollView and takes 700 ms to lay out 2,000 results. How would you redesign it?
- 54
A collapsing header jumps when the paginated feed inserts 20 items above the visible row. How would you preserve the user's scroll position?
- 55
DevTools shows repeated layout passes after adding IntrinsicHeight around each card in a 100-item list. What would you change?
flutter-devtools - 56
An animated progress ring makes a static form repaint every frame, and the repaint rainbow flashes the whole screen. Where would you place RepaintBoundary?
formsrenderingrepaint-isolation - 57
Scrolling a photo feed of 12 MP images grows memory past 600 MB and Android kills the app. The cards display images at only 360 pixels wide. How would you fix it?
memory - 58
Cold start to the first usable screen is 1.8 seconds, and 900 ms is spent before runApp completes. How would you reduce it?
- 59
An Impeller trace shows UI frames near 8 ms but raster frames at 22 ms when a blurred modal appears. How do you interpret and act on it?
rendering - 60
A drag animation is smooth at 60 Hz but drops frames on a 120 Hz phone, where the budget is about 8 ms. The callback allocates a new list on every tick. What would you do?
animationcallbacks - 61
A Riverpod autoDispose provider starts an HTTP request, the user leaves the screen, and the response later updates a closed client. How would you handle the lifecycle?
state-managementprovider-lifecyclehttp - 62
Opening one screen sends the same REST request three times because several widgets watch equivalent Riverpod providers. How would you diagnose and prevent it?
widgetsstate-managementrest - 63
A Riverpod family for search results returns stale data after filters change because the parameter is a mutable Filter object. What would you change?
state-management - 64
Pull-to-refresh sets a Riverpod AsyncNotifier to AsyncLoading, blanks 200 cached rows, and flashes a full-screen spinner for 900 ms even though the old data is still valid. How would you preserve the rows while exposing refresh progress and failure?
state-managementcachingasync - 65
In a BLoC search screen, rapid QueryChanged events run concurrently and old results flash after new ones. Which event concurrency policy would you choose?
state-managementqueriesconcurrency - 66
A BLoC state rebuild causes the same payment success SnackBar to appear twice after rotation. How would you model the side effect?
state-management - 67
After a GraphQL mutation renames a user, the profile updates but the same user in a team list keeps the old name. How would you organize the client cache?
graphqlcaching - 68
An authentication provider and go_router redirect each other, producing an infinite redirect loop during token restoration. How would you make navigation deterministic?
state-managementnavigationauth - 69
A Riverpod Notifier reads DateTime.now and creates an API client internally, making its retry test flaky. How would you refactor it?
refactoringapiresilience - 70
Updating one task invalidates a Riverpod project provider and refetches 500 tasks, causing a visible spinner. How would you make the update more targeted?
state-management - 71
A checkout POST times out after the server may have charged the card. Retrying can create a second order. How would you make the flow safe?
resilience - 72
A WebSocket disconnects for 40 seconds on the subway, and after reconnect the chat has missing and duplicated messages. How would you recover?
websockets - 73
A user pulls to refresh while page 4 is loading; page 4 then appends old results to the refreshed list. How would you prevent the pagination race?
pagination - 74
Two devices edit the same offline note, then reconnect with different titles and bodies. How would you synchronize the writes and surface the conflict?
- 75
Deleting a comment succeeds, but cached post details and the comment-count badge disagree until app restart. What cache invalidation would you implement?
conflictcaching - 76
When an access token expires, 20 parallel requests receive 401 and trigger 20 refresh calls. How would you implement refresh fan-in?
tokens - 77
A 300 MB video upload must show progress, support cancel, and resume after a brief connection loss. How would you design it?
design - 78
A GraphQL response returns useful product data plus an error for the reviews field. The current client discards the whole screen. How would you handle it?
graphql - 79
Decoding a 5 MB JSON catalog blocks the UI isolate for 45 ms, but small responses decode in under 2 ms. When would you use an isolate?
concurrency - 80
A REST client retries every failure three times, making validation errors slow and mobile outages noisy. How would you define a retry policy?
validationrestresilience - 81
A MethodChannel call works in debug but throws MissingPluginException only in the release Android build. How would you investigate it?
platform-channels - 82
After changing a Pigeon API from int to a nullable result object, iOS crashes while decoding the reply from an older generated host file. How would you fix and prevent this?
pigeon - 83
A Kotlin image picker occasionally hangs, and logs show one path calls the MethodChannel result twice after an Activity is recreated. How would you repair the callback lifecycle?
platform-channelscallbacks - 84
After raising targetSdk to 35, notifications still arrive in logs on Android 13+, but 68% of new installs show none and no prompt appeared. How would you repair the flow?
- 85
Product asks the Flutter app to sync every 5 minutes in the background on both iOS and Android, even after the user force-quits it. What would you commit to?
flutter - 86
A product deep link opens correctly from a terminated app but pushes the detail page twice when the app is already warm. How would you unify the flow?
- 87
Tapping a push notification for an order while the app is locked routes to login and loses the order destination after authentication. How would you preserve it?
auth - 88
Android builds after a plugin upgrade, but iOS fails with a CocoaPods deployment-target conflict and duplicate framework symbols. How would you debug it?
deploymentcocoapods - 89
The staging Android flavor installs, but Firebase events appear in production and the signed package cannot use the staging OAuth client. What would you check?
oauth - 90
A staged release reaches 10% and shows a 4% crash rate only on Android 12 when opening a native barcode scanner. What actions would you take?
- 91
A widget test passes in English but reports a 12-pixel overflow in Russian at text scale 1.3. How would you diagnose and fix it?
widgets - 92
Golden tests differ by a few pixels only in Linux CI because text glyphs are rendered differently from macOS. How would you stabilize them?
golden-tests - 93
An integration test taps Submit, but on small Android devices the keyboard covers the button and the test times out. How would you make the test reliable without using coordinates?
integrationintegration-testing - 94
A widget test hangs in pumpAndSettle after an error banner appears with a repeating animation. How would you fix the test?
widgetsanimation - 95
A debounce test sleeps 350 ms and fails intermittently on loaded CI runners. How would you make it deterministic?
debounce - 96
CI sometimes compiles stale generated Pigeon and JSON serialization files after dependencies are restored from cache. How would you redesign the cache and checks?
serializationcachingdependencies - 97
One 2,000-line checkout screen mixes widgets, Dio calls, JSON mapping, and BLoC logic, but a full rewrite is too risky before the next release. How would you refactor it?
widgetsstate-managementrefactoring - 98
A proposed camera package saves two weeks of work but has not released in 18 months and fails on the latest Android target SDK. How would you evaluate it?
decision-making - 99
Support reports that checkout occasionally stays on a spinner, but there is no crash and local reproduction fails. What observability would you add?
observability - 100
In review, a junior developer subscribes to a WebSocket inside build and fixes duplicate messages by adding a boolean flag. How would you handle the code and mentoring?
websocketsmentoring