In short: this is a free bank of 320 Flutter interview questions and answers for 2026 — 160 multiple-choice questions and 160 short-answer questions across Dart, the widget tree, state management, async and isolates, navigation, networking, animations, testing, performance, architecture, and release. Every question is tagged Junior, Mid, or Senior, and a learning-mode switch hides the answers so you can quiz yourself, then reveal the answer and explanation one question at a time.
Skip the intro — start the 320 practice questionsWhether you are preparing for your first Flutter role or a senior interview, the fastest way to get sharper is active recall: try to answer before you read the solution. Keep the toggle on for a quiz-style flow — pick an option, submit, then read the explanation — or switch it off to revise every answer at once. Use the level filter to focus on Junior, Mid, or Senior questions, and the topic links to drill a single area such as state management or async. The topic mix is cross-checked against Flutter's architectural overview, Dart's language docs, and the official Flutter docs for state management, testing, and performance profiling.

The Complete Flutter Guide: Build Android, iOS and Web apps
Want to go from interview-ready to job-ready? Build industry-standard apps with Riverpod, Firebase, animations, REST APIs, and more.
Get 85% off on UdemyHow to use these Flutter interview questions
The bank is grouped into 14 topics, mirroring how real Flutter interviews move from language fundamentals to architecture and release. Each topic opens with a question-shaped heading, then mixes multiple-choice and short-answer questions so you switch between recognising the right answer and recalling it from memory. Junior questions check whether you know the building blocks, Mid questions probe how you use them in real apps, and Senior questions ask about trade-offs, performance, and architecture.
- Quiz mode (default): leave the switch on, choose an answer, submit, then read why it is right.
- Revision mode: turn the switch off to show every answer for fast review before an interview.
- Targeted practice: filter by level, or jump to a topic such as state management or async.
320 Flutter interview questions and answers
Use the controls below to switch between quiz and revision mode, filter by experience level, and track how many multiple-choice questions you have answered correctly.
What Dart language questions come up in Flutter interviews?
Types, null safety, futures, collections, and the language features Flutter is built on. 13 MCQs · 13 short answers
What does the const keyword guarantee for a Dart variable?
Correct answer: B. A compile-time constant that is fully immutable
Why: const is a canonicalised compile-time constant; final is assign-once at runtime.
What is the difference between var, final, and const?
Answer: var declares a mutable variable whose type is inferred. final is assigned once at runtime and cannot be reassigned afterwards. const is a compile-time constant that is deeply immutable and canonicalised, so identical const values share one instance.
Which operator safely accesses a member only when the value is non-null?
Correct answer: C. ?.
Why: ?. is null-aware access; ! asserts non-null, ?? supplies a default, .. is cascade.
What does null safety mean in Dart?
Answer: Types are non-nullable by default, so a String can never hold null, while String? opts into nullability. The compiler forces you to handle the nullable case before using a value, which removes a whole class of null-reference crashes.
Explain how async and await work in Dart.
Answer: Marking a function async makes it return a Future and lets you use await inside it. await suspends the function until the awaited Future completes, then resumes with its value, without blocking the isolate. This lets you write asynchronous code in a sequential, readable style.
What is the difference between final and const?
Correct answer: B. final is assigned once at runtime; const must be known at compile time
Why: Both prevent reassignment, but only const is a compile-time constant.
What does the late keyword do?
Correct answer: B. Declares a non-nullable variable initialised after its declaration
Why: late allows a non-nullable field assigned before first use, and can defer initialisation lazily.
What are mixins and when would you use one?
Answer: A mixin lets you reuse a class's members in many class hierarchies without classic inheritance, applied with the with keyword. You use mixins to share behaviour across unrelated classes, for example TickerProviderStateMixin to provide a vsync for animations.
What is the difference between named and positional parameters?
Answer: Positional parameters are matched by order, while named parameters are passed by name inside braces and may be optional or required. Named parameters make call sites readable and let callers skip optional arguments.
In a ?? b, what does the null-coalescing operator return?
Correct answer: A. a if it is non-null, otherwise b
Why: ?? returns the left operand unless it is null, in which case it returns the right.
What is the cascade operator and why is it useful?
Answer: The cascade .. performs a sequence of operations on the same object without repeating its reference, and the whole expression evaluates to that object. It is handy for configuring an object or builder fluently in one statement.
How do extends, implements, and with differ?
Correct answer: B. extends inherits, implements reimplements an interface, with applies a mixin
Why: Each expresses a different reuse relationship: inheritance, interface, and mixin.
What is the difference between dynamic and Object?
Answer: Object? is the statically checked top type, so you must check or cast before calling type-specific members. dynamic switches off static type checking, allowing any member access that is only verified at runtime. Prefer Object for safety and use dynamic deliberately.
How do sealed classes and pattern matching help model state?
Answer: A sealed class restricts a type to a known set of subtypes, so the compiler can verify a switch handles every case exhaustively. Combined with pattern matching, they model finite states such as Loading, Success, and Error cleanly, catching missing cases at compile time.
What is a factory constructor used for?
Correct answer: B. To return an existing, cached, or subtype instance rather than always a new one
Why: A factory controls instance creation, e.g. caching or returning a subtype.
What does the spread operator do in a collection literal?
Answer: The spread ... inserts all elements of one collection into another collection literal. The null-aware form ...? simply skips the spread when the source collection is null.
What does an async function always return?
Correct answer: C. A Future
Why: An async function returns a Future that completes with the value or error.
How do getters and setters work in Dart?
Answer: A getter, declared with get, is a computed property you access like a field. A setter, declared with set, runs code when you assign to the property. Together they let you expose computed or validated values using field-like syntax.
Which collection type stores unique, unordered values?
Correct answer: C. Set
Why: A Set holds unique elements with no guaranteed order.
For a final list versus a const list, what can change?
Answer: A final list fixes the reference but still lets you mutate its contents, such as adding elements. A const list is deeply immutable and created at compile time, so neither the reference nor its elements can change.
What is the difference between == and identical()?
Correct answer: B. == tests equality (overridable); identical() tests whether two references are the same object
Why: identical() is reference identity; == is value equality you can override.
What does the ??= operator do?
Answer: ??= assigns the right-hand value only when the variable is currently null, and leaves it unchanged otherwise. It is a concise way to lazily initialise a nullable variable on first use.
What is an enum, and how is an enhanced enum different?
Answer: An enum defines a fixed set of named constant values. Enhanced enums in modern Dart can also declare fields, constructors, and methods, so each value can carry data and behaviour rather than just being a label.
What does an extension method let you do?
Correct answer: B. Add behaviour to an existing type without subclassing it
Why: Extensions add methods and getters to existing types at the call site.
What does sound null safety guarantee?
Correct answer: B. A non-nullable type can never hold null at runtime
Why: With sound null safety the compiler proves non-nullable values are never null.
After await, what is the value type of Future<List<int>>?
Correct answer: B. List<int>
Why: await unwraps the Future to its completed value, here a List<int>.
What are the most common Flutter basics interview questions?
Widgets, the build method, BuildContext, hot reload, and how a Flutter app starts. 13 MCQs · 13 short answers
What is a widget in Flutter?
Answer: A widget is an immutable description of part of the user interface. Flutter composes a tree of widgets and rebuilds it as state changes. Almost everything, including layout, styling, and gesture handling, is expressed as a widget.
What is the relationship between main and runApp?
Answer: main is the Dart entry point that runs first when the app starts. Inside it you call runApp with a root widget, which inflates that widget as the root of the tree and attaches it to the screen.
In Flutter, almost everything you see in the UI is a...
Correct answer: B. Widget
Why: Flutter UIs are composed from widgets, including layout and styling.
What is the entry point of a Flutter app?
Correct answer: A. runApp() called from main()
Why: main() calls runApp() with the root widget to start the app.
What is the difference between the widget tree and the element tree?
Answer: The widget tree is the immutable configuration you write in build methods. The element tree is the mutable runtime structure that holds each widget's location, lifecycle, and state and links it to a render object. Elements persist across rebuilds while widgets are recreated each time.
What does a Scaffold provide?
Answer: Scaffold implements the basic Material visual layout. It offers slots for an app bar, body, floating action button, drawer, bottom navigation, and snackbars, so you don't have to build that chrome by hand.
What does a widget's build method return?
Correct answer: B. A widget (tree)
Why: build describes part of the UI by returning a widget subtree.
What is BuildContext?
Correct answer: B. A handle to a widget's location in the tree
Why: Context locates a widget in the tree and is used to look up ancestors and inherited data.
How does Flutter achieve a consistent UI across platforms?
Answer: Flutter draws its own widgets with its rendering engine, Impeller, rather than wrapping native platform controls. Because it controls every pixel, the UI looks and behaves consistently across Android, iOS, web, and desktop from a single codebase.
Which widget gives you Material structure such as an app bar, body, and floating action button?
Correct answer: B. Scaffold
Why: Scaffold provides the standard Material visual layout.
What is the difference between EdgeInsets.all and EdgeInsets.symmetric?
Answer: EdgeInsets.all applies the same inset to all four sides. EdgeInsets.symmetric lets you set horizontal and vertical insets separately. Both describe spacing for widgets such as Padding or Container.
What is Theme and how do you read it?
Answer: Theme carries colours, typography, and component styling down the tree through an InheritedWidget. You read it with Theme.of(context), which lets widgets adapt to the app's design and to light or dark mode.
What does hot reload preserve that hot restart does not?
Correct answer: B. The current app state
Why: Hot reload keeps state while updating the tree; hot restart resets to initial state.
At a high level, what does setState do?
Answer: setState tells Flutter that a StatefulWidget's state has changed and it should rebuild. You mutate the relevant state inside its callback, and Flutter schedules a rebuild of that widget on the next frame.
How do MaterialApp and CupertinoApp mainly differ?
Correct answer: B. In the design language and default widgets they provide
Why: They set up Material vs iOS-style theming, navigation, and widgets.
Why does using the correct BuildContext matter?
Answer: BuildContext identifies a widget's position in the tree and is used to find ancestors and inherited widgets such as Theme or Navigator. A context taken from the wrong location may not find the expected ancestor, which is why helpers like Builder give you a context lower in the tree.
How can the widget tree be immutable while the UI still changes?
Answer: Widgets are cheap, throwaway configuration objects recreated on each build. The persistent, mutable parts live in elements and State objects. Flutter diffs the new widget tree against the existing elements and updates only what actually changed, which keeps rebuilding efficient.
What is the role of pubspec.yaml?
Correct answer: B. It declares dependencies, assets, fonts, and project metadata
Why: pubspec.yaml is the project manifest read by the Dart and Flutter tooling.
What do the widget, element, and render trees represent?
Correct answer: B. Configuration, instantiation and lifecycle, and layout and paint respectively
Why: Widgets configure, elements hold lifecycle and identity, render objects do layout and painting.
What is the difference between Image.asset and Image.network?
Answer: Image.asset loads an image bundled with the app and declared in pubspec.yaml. Image.network downloads an image from a URL at runtime and can display loading and error states while it fetches.
What does MediaQuery.of(context) provide?
Correct answer: B. Screen size, padding, text scale, and device metrics
Why: MediaQuery exposes device and window metrics for responsive layout.
Which widget centres its single child?
Correct answer: B. Center
Why: Center centres its child within itself.
How do you add assets and custom fonts to a Flutter app?
Answer: You place the files in the project and declare them under the assets and fonts sections of pubspec.yaml. Flutter then bundles them into the app, and you reference them by path or by the declared font family.
What does hot reload do, and what are its limits?
Answer: Hot reload injects changed source into the running app and rebuilds the UI while keeping the current state, which makes UI iteration fast. It cannot apply changes to code that runs once, such as main, global variables, or static initialisers, where a hot restart is needed.
What is the difference between Expanded and Flexible?
Correct answer: B. Expanded forces the child to fill available space; Flexible lets it take up to that space
Why: Expanded is Flexible with fit: FlexFit.tight.
Why does Flutter use a declarative UI model?
Correct answer: B. Because the UI is rebuilt as a function of state, so you describe what it should look like
Why: Declarative UI maps state to widgets, removing imperative view mutation.
Which Flutter widget and key questions get asked?
Stateless vs stateful, common widgets, builders, and when keys actually matter. 12 MCQs · 12 short answers
When do you use a StatefulWidget instead of a StatelessWidget?
Answer: Use a StatefulWidget when the widget must change during its lifetime in response to input, timers, or async results, so you can hold state and call setState. Use a StatelessWidget when the output depends only on its constructor inputs and never changes itself.
When should you choose a StatelessWidget over a StatefulWidget?
Correct answer: B. When its appearance depends only on its inputs and never changes itself
Why: Stateless widgets have no mutable state; use them for input-driven, unchanging UI.
Where does a StatefulWidget keep its mutable state?
Correct answer: B. In a separate State object
Why: StatefulWidget is immutable; its State object holds data that survives rebuilds.
What is the difference between ListView and ListView.builder?
Answer: A plain ListView builds all of its children immediately, which suits short, fixed lists. ListView.builder builds items lazily as they scroll into view, which is essential for long or infinite lists to keep memory and build time bounded.
What problem do keys solve in dynamic lists?
Answer: Without keys, Flutter matches widgets to elements by position and type, so reordering or removing stateful items can attach the wrong state to a widget. Keys give items a stable identity so their state follows them when the list changes.
Why prefer ListView.builder over a ListView with a fixed children list?
Correct answer: B. It lazily builds only the items near the viewport, saving memory
Why: builder constructs items on demand, scaling to long or infinite lists.
What is the main difference between Container and SizedBox?
Correct answer: B. SizedBox is a lightweight box for sizing and spacing; Container bundles padding, decoration, and constraints
Why: Use SizedBox for simple sizing; Container when you need decoration or several effects.
What is the difference between a Column and a ListView?
Answer: A Column lays out a fixed set of children and does not scroll, so it overflows if they exceed the available space. A ListView is scrollable and can build many children lazily, which suits long or dynamic content.
What does the Builder widget solve in practice?
Answer: Builder gives you a fresh BuildContext located below the current widget. That matters when you need a context that can find an ancestor, such as a Scaffold or a Provider, created in the same build method that the outer context cannot see.
When do you actually need to give a widget a Key?
Correct answer: B. When same-type widgets are reordered, added, or removed in a list and must keep state
Why: Keys let Flutter match elements to widgets correctly when positions change.
How does a GlobalKey differ from a ValueKey?
Correct answer: B. GlobalKey uniquely identifies a widget across the whole tree and exposes its state or context; ValueKey only distinguishes siblings
Why: GlobalKeys are powerful but heavier, so use them sparingly.
Explain GlobalKey and a downside of overusing it.
Answer: A GlobalKey uniquely identifies a widget across the whole tree and exposes its state and context, which is useful for things like form state. Overusing it couples widgets together, makes the data flow harder to follow, and is comparatively expensive, so prefer passing data through constructors.
What is SizedBox used for?
Answer: SizedBox creates a box with a specific width and height, and with no child it adds fixed spacing between widgets. It is a lightweight choice when you only need sizing or a gap, without the extra features of Container.
What does SafeArea do?
Correct answer: B. Insets its child to avoid notches, status bars, and other system intrusions
Why: SafeArea pads content away from device UI such as notches and the status bar.
How do Wrap and Row differ?
Answer: A Row places its children on a single horizontal line and overflows if they don't fit. A Wrap arranges children in runs and moves to the next line when space runs out, which suits chips, tags, and flowing content.
Why might you use a Builder widget?
Correct answer: B. To obtain a BuildContext below the current widget, for example to reach an ancestor provided in the same build method
Why: Builder gives a fresh context under the current widget where the inherited ancestor exists.
Which widget displays styled text?
Correct answer: B. Text
Why: Text renders a string with an optional TextStyle.
What are slivers and when do you reach for them?
Answer: Slivers are scrollable regions with custom scroll behaviour placed inside a CustomScrollView. You use slivers like SliverList, SliverGrid, and SliverAppBar when you need effects such as a collapsing header or several scrolling sections that a plain ListView cannot express.
How does Padding differ from a Container with padding?
Answer: Padding only insets its child by the given amount and nothing more. A Container can add padding too but also supports decoration, constraints, margins, and alignment. Use Padding when spacing is all you need.
What is the difference between GestureDetector and InkWell?
Correct answer: B. InkWell adds a Material ripple and needs a Material ancestor; GestureDetector only detects gestures
Why: Use InkWell for Material feedback and GestureDetector for raw gestures.
Why are most Flutter widgets immutable, and how does interactivity still work?
Answer: Immutable widgets are cheap to create and safe to compare, which makes rebuilding the tree efficient. Interactivity lives in the mutable State objects and elements behind StatefulWidgets, so the configuration stays immutable while the runtime state changes through setState.
What does AnimatedBuilder help you avoid?
Correct answer: B. Rebuilding an entire subtree on each animation tick by isolating the animating part
Why: It rebuilds only the builder output per tick, keeping the rest static.
What is the difference between Visibility, Offstage, and Opacity?
Answer: Visibility shows or hides a child and can optionally keep its layout space. Offstage lays the child out but does not paint it or hit-test it. Opacity paints the child at a given transparency. They differ in whether the child is laid out, painted, and interactive.
Why does Flutter favour composition over inheritance for building UI?
Correct answer: B. Small widgets combined together are more flexible and reusable than deep class hierarchies
Why: Composition keeps widgets small and recombinable, which Flutter is designed around.
How do Flutter layout and constraint questions work?
Constraints go down, sizes go up; Row, Column, Flex, Stack, and overflow fixes. 12 MCQs · 12 short answers
Explain how constraints work in Flutter layout.
Answer: A parent passes constraints, the minimum and maximum width and height, down to its child. The child chooses its own size within those constraints and reports it back up, and the parent then positions it. The slogan is: constraints go down, sizes go up, and the parent sets position.
A Row arranges its children...
Correct answer: B. Horizontally along the main axis
Why: Row lays children out horizontally; Column is vertical.
How would you fix a RenderFlex overflow in a Row?
Answer: Find the child that is too wide and constrain it: wrap it in Expanded or Flexible so it shares the available width, shorten its content, or make the Row scrollable. The overflow happens because the children's combined width exceeds the Row's width.
What is the main axis of a Column?
Correct answer: B. Vertical
Why: A Column main axis runs vertically; its cross axis is horizontal.
Which phrase best summarises Flutter's layout algorithm?
Correct answer: B. Constraints go down, sizes go up, and the parent sets position
Why: Parents pass constraints down, children pick sizes within them, and parents position children.
What is the difference between Expanded and Flexible?
Answer: Both let a child flex inside a Row or Column, but Expanded forces the child to fill all the space it is assigned (a tight fit), while Flexible lets the child take up to that space (a loose fit). Expanded is simply Flexible with FlexFit.tight.
What commonly causes a RenderFlex overflow in a Row?
Correct answer: B. Children whose combined width exceeds the available horizontal space
Why: Wrap a child in Expanded or Flexible, or make the row scrollable, to fix it.
What do MainAxisAlignment and CrossAxisAlignment control?
Answer: MainAxisAlignment positions children along the primary direction of a Row or Column, horizontal for a Row and vertical for a Column. CrossAxisAlignment positions them along the perpendicular direction. Together they control alignment on both axes.
What does wrapping a child in Expanded inside a Row do?
Correct answer: B. Makes the child take the remaining space along the main axis
Why: Expanded gives the child a tight flex fit to fill leftover main-axis space.
How do you overlap widgets on top of each other?
Answer: Use a Stack, which paints its children in order, one over another. Wrap a child in Positioned to place it relative to the stack's edges, or leave it unpositioned to align it within the stack using the stack's alignment.
What does MainAxisAlignment control in a Row or Column?
Correct answer: B. How children are positioned and spaced along the main axis
Why: It distributes children along the main axis, for example spaceBetween or center.
Why does putting a ListView directly inside a Column throw an unbounded-height error?
Correct answer: B. The Column passes unbounded vertical constraints, so the ListView cannot decide its height
Why: Give it bounded height, e.g. wrap in Expanded, so it receives finite constraints.
Why does a ListView inside a Column throw, and how do you fix it?
Answer: A Column gives its children unbounded height, but a ListView wants to expand to fill the available vertical space, so it cannot resolve a height. Fix it by wrapping the ListView in Expanded or Flexible, giving it a fixed height with SizedBox, or using shrinkWrap for a short list.
What is LayoutBuilder, and when is it better than MediaQuery?
Answer: LayoutBuilder rebuilds with the constraints its parent passes, so you can adapt to the space available to that particular widget. MediaQuery reports the size of the whole screen. Prefer LayoutBuilder when a widget should react to its own box rather than the entire window.
How do you position a widget at a specific offset within a Stack?
Correct answer: B. Wrap it in Positioned
Why: Positioned sets a child location relative to the Stack edges.
What is the difference between padding and margin?
Answer: Padding is the space inside a widget's boundary, between its edge and its child. Margin is the space outside the boundary that separates the widget from its neighbours. A Container lets you set both.
What does LayoutBuilder give you?
Correct answer: B. The parent constraints, so you can build differently for the available size
Why: LayoutBuilder exposes incoming constraints, useful for adaptive layouts.
How do you make a layout adapt to phone, tablet, and desktop?
Answer: Choose breakpoints based on the available width using LayoutBuilder or MediaQuery, then switch between layouts, such as a single column on phones and a master-detail or grid on larger screens. Widgets like Wrap, Flexible, and GridView help the content reflow gracefully.
What is the difference between padding and margin on a Container?
Correct answer: B. Padding is space inside the border; margin is space outside it
Why: Padding insets the child within the box; margin offsets the box from neighbours.
Why can IntrinsicHeight be expensive?
Correct answer: B. It performs an extra layout pass to measure children before sizing them
Why: Intrinsic sizing adds a speculative measurement pass, costly in large or nested trees.
What does shrinkWrap do on a ListView, and what is the trade-off?
Answer: shrinkWrap makes a ListView size itself to its content instead of expanding to fill the viewport, which lets it sit inside a Column or another scrollable. The trade-off is that it builds all children to measure them, losing the lazy performance benefit, so it suits only short lists.
What does the Spacer widget do?
Answer: Spacer inserts flexible empty space along the main axis of a Row or Column, pushing the surrounding widgets apart. It behaves like an Expanded with an empty child and honours a flex factor, so it is handy for distributing items evenly.
For responsive layouts, when is LayoutBuilder preferable to MediaQuery?
Correct answer: B. When you need the space available to that widget rather than the whole screen size
Why: MediaQuery is screen-level; LayoutBuilder reacts to local box constraints.
What is the difference between Align and Center?
Answer: Center places its child in the middle of the available space. Align is more general and can place the child at any alignment, such as topLeft or bottomRight, using an Alignment value. Center is just Align with Alignment.center.
What do interviews ask about Flutter state management?
setState, InheritedWidget, Provider, Riverpod, and BLoC — trade-offs and when to use each. 14 MCQs · 14 short answers
What is the difference between ephemeral state and app state?
Answer: Ephemeral, or local, state belongs to a single widget, such as the current page of a PageView or whether a checkbox is ticked, and setState handles it well. App, or shared, state is used across many widgets or screens, such as the logged-in user or a cart, and is better managed with Provider, Riverpod, or BLoC.
What must you do inside the setState callback?
Correct answer: B. Mutate the state that affects the build output
Why: Mutating state in setState tells Flutter to rebuild with the new values.
How does Provider expose and update state?
Answer: Provider places a value, often a ChangeNotifier, above the widgets that need it. Descendants read it with context.watch or a Consumer to rebuild on change, or context.read for one-off access. When the notifier calls notifyListeners, the listening widgets rebuild.
What does lifting state up mean?
Correct answer: B. Moving shared state to the nearest common ancestor so several widgets can use it
Why: Shared state lives in a common parent and flows down to children.
What does InheritedWidget provide?
Correct answer: B. Efficient propagation of data down the tree, rebuilding only dependents
Why: Descendants that depend on it rebuild when its data changes; it underpins Provider.
What is a ChangeNotifier and how does it trigger rebuilds?
Answer: A ChangeNotifier holds mutable state and exposes notifyListeners. When you change state and call notifyListeners, every widget listening through Provider rebuilds. It is a simple observable model that works well for small and medium apps.
Why might you choose Riverpod over classic Provider?
Answer: Riverpod removes the dependency on BuildContext, so providers are global, compile-safe, and easy to test in isolation. It avoids pitfalls like ProviderNotFound, supports auto-disposal and families, and composes providers cleanly, which suits apps where testability and scalability matter.
What is Provider built on top of?
Correct answer: B. InheritedWidget
Why: Provider wraps InheritedWidget to expose values and listen for changes.
Explain the BLoC pattern and its main benefit.
Answer: BLoC separates business logic from the UI by taking a stream of events and emitting a stream of immutable states. Widgets dispatch events and rebuild from the emitted states. The benefit is a predictable, testable, and traceable flow that scales well for complex features, at the cost of extra boilerplate.
What is the difference between ref.watch, ref.read, and ref.listen in Riverpod?
Answer: ref.watch subscribes a build method to a provider and rebuilds when it changes. ref.read reads the current value once without subscribing, which suits callbacks. ref.listen runs a side effect, such as showing a snackbar, when a provider changes, without causing a rebuild.
What does a ChangeNotifier do?
Correct answer: B. Holds state and calls notifyListeners() to trigger rebuilds of its listeners
Why: notifyListeners tells listening widgets, via Provider, to rebuild.
When is setState the right tool?
Answer: setState is right for simple, local UI state that only one widget cares about, such as toggling a password's visibility or tracking a single field. It is built in, needs no packages, and keeps simple widgets simple.
What is the main advantage of Riverpod over classic Provider?
Correct answer: B. It does not depend on BuildContext and is compile-safe and easy to test
Why: Riverpod providers are global, type-safe, and testable without the widget tree.
In the BLoC pattern, data flows as...
Correct answer: B. Events in, states out, usually over streams
Why: BLoC maps incoming events to a stream of immutable states the UI listens to.
How do you avoid rebuilding too much when shared state changes?
Answer: Subscribe each widget only to the data it needs, using Consumer or Selector in Provider, or select in Riverpod, so it rebuilds only when its slice changes. Also split large widgets so unaffected parts can stay const and untouched.
What is the difference between ref.watch and ref.read in Riverpod?
Correct answer: B. watch subscribes and rebuilds on change; read gets the value once without listening
Why: Use watch in build and read for one-off reads such as inside callbacks.
Why is immutable state important in state management?
Answer: Immutable state makes changes explicit: you create a new state object rather than mutating the old one, which makes equality checks cheap and rebuilds predictable. It avoids subtle bugs from shared mutable references and pairs well with patterns like BLoC and features like undo and redo.
Why use Selector in Provider or select in Riverpod?
Correct answer: B. To rebuild only when a specific slice of state changes, reducing rebuilds
Why: Selecting a sub-value avoids rebuilding when unrelated parts of the model change.
When is setState a perfectly good choice?
Correct answer: B. For local, ephemeral UI state confined to one widget
Why: Local UI state such as a toggle or form field is fine with setState.
What is the difference between context.watch and context.read in Provider?
Answer: context.watch subscribes the calling widget to the provider so it rebuilds on changes, and you use it in build. context.read fetches the value once without subscribing and is meant for event handlers such as onPressed, where you don't want a rebuild.
What does lifting state up achieve?
Answer: Moving shared state to the nearest common ancestor of the widgets that use it gives them one source of truth to read and update. It avoids duplicated copies of the same data drifting out of sync.
Why is immutability preferred for state objects?
Correct answer: B. It makes state changes predictable, comparable, and easier to debug and test
Why: Immutable state avoids hidden mutation bugs and enables cheap equality checks.
How do you decide which state management approach to use?
Answer: Match the tool to the complexity: setState for local state, Provider or Riverpod for most shared app state, and BLoC or Riverpod notifiers when you need strict event-to-state flows and traceability. Favour the simplest option that keeps the app testable and maintainable.
What are FutureProvider and StreamProvider in Riverpod for?
Answer: They expose asynchronous data as a provider that emits an AsyncValue with loading, data, and error states. Widgets can render each state cleanly without manually wiring a FutureBuilder, and the result is cached and shareable across the app.
What is the risk of creating an expensive object inside build?
Correct answer: B. It is recreated on every rebuild, losing its state and wasting work
Why: Create such objects in initState or a provider, not in build.
Why should you dispose controllers and notifiers?
Answer: Controllers, notifiers, and stream subscriptions hold resources and listeners that leak memory and can fire after a widget is gone if not released. Disposing them in dispose, or using auto-dispose providers, prevents leaks and stale callbacks.
What does notifyListeners() trigger?
Correct answer: B. A rebuild of widgets listening to that ChangeNotifier
Why: It signals listeners that the model changed so they can rebuild.
How does Provider help with dependency injection, not just state?
Correct answer: B. It exposes services and objects down the tree so widgets read them via context instead of creating them
Why: Providers supply shared services such as repositories and clients to the subtree.
What Flutter lifecycle and rebuild questions should you prepare?
State and app lifecycle callbacks, when build runs, and how to limit rebuilds. 10 MCQs · 10 short answers
Which State method runs once when the State object is first created?
Correct answer: B. initState
Why: initState runs once for one-time setup before the first build.
Where should you release controllers, listeners, and streams?
Correct answer: B. dispose
Why: dispose is the cleanup hook called when the State is permanently removed.
Describe the main lifecycle methods of a State object.
Answer: initState runs once when the State is created for one-time setup. didChangeDependencies runs after initState and when inherited dependencies change. build runs whenever the widget must render. didUpdateWidget runs when the parent rebuilds with new configuration. dispose runs once when the State is removed, for cleanup.
What belongs in initState versus dispose?
Answer: initState is for one-time setup such as creating controllers, subscribing to streams, or kicking off an initial load. dispose is for tearing those down: disposing controllers, cancelling subscriptions, and removing listeners so nothing leaks.
When is didChangeDependencies called?
Correct answer: B. After initState and whenever an inherited dependency the widget uses changes
Why: It runs after initState and when an InheritedWidget it depends on changes.
When does didChangeDependencies run, and why is it useful?
Answer: It runs right after initState, and again whenever an InheritedWidget the State depends on changes, such as Theme or a Provider. It is the correct place for context-dependent initialisation that is not safe to do in initState.
What is didUpdateWidget used for?
Answer: It is called when the parent rebuilds and gives the same State a new widget configuration. You use it to react to changed inputs, for example re-subscribing when an id passed into the widget changes, and you can compare oldWidget with the new widget to decide what to do.
What does didUpdateWidget let you respond to?
Correct answer: B. The parent rebuilding this widget with a new configuration
Why: Use it to react when the incoming widget configuration changes.
How do you respond to the app being backgrounded or resumed?
Answer: Implement WidgetsBindingObserver, register it with WidgetsBinding.instance.addObserver, and handle didChangeAppLifecycleState, which reports states like resumed, inactive, paused, and detached. Use it to pause work, save data, or refresh on resume, and remove the observer in dispose.
Why must you check mounted after an await before calling setState?
Answer: An async gap can outlive the widget, so by the time the Future completes the State may already be disposed. Calling setState on a disposed State throws, so you guard it with a mounted check to avoid the error and skip unnecessary work.
Why should initState avoid BuildContext-dependent calls like MediaQuery.of?
Correct answer: B. Inherited widgets are not fully available yet; use didChangeDependencies instead
Why: Context-dependent lookups are safe in didChangeDependencies, not early in initState.
How do you observe app lifecycle states such as paused and resumed?
Correct answer: B. Implement WidgetsBindingObserver and handle didChangeAppLifecycleState
Why: Register a WidgetsBindingObserver to receive app lifecycle callbacks.
Why should build be fast and free of side effects?
Answer: build can run many times, even several times per second during animation or scrolling. If it performs heavy work or side effects such as network calls, the app janks and behaves unpredictably, so build should only describe the UI.
What causes excessive rebuilds, and how do you diagnose them?
Answer: Common causes are calling setState on a large ancestor, listening to a provider that changes often, or recreating objects inside build. You diagnose them with Flutter DevTools' rebuild profiler and the performance overlay, then narrow the state scope and add const where you can.
How often can build be called?
Correct answer: B. Many times, whenever the widget needs to be rebuilt
Why: build can run frequently, so it must be cheap and side-effect free.
What is the order of initState, didChangeDependencies, and build on first render?
Answer: On first render the order is initState, then didChangeDependencies, then build. initState does setup that does not need context, didChangeDependencies handles inherited dependencies, and build then produces the UI.
What happens to a State object when its widget moves in the tree?
Answer: If Flutter can reuse the element, often guided by keys, the same State is preserved and moved with it. If not, the old State is disposed and a new one is created via initState. Keys decide whether Flutter keeps the same element and State across the change.
Why must build be free of side effects?
Correct answer: B. Because it may be called many times and should only describe the UI
Why: Side effects in build cause bugs and wasted work due to frequent rebuilds.
What is the most effective way to reduce unnecessary rebuilds?
Correct answer: B. Keep setState scope small, split widgets, and mark stable subtrees const
Why: Localising state and using const limits the part of the tree that rebuilds.
What does mounted tell you in a State object?
Correct answer: B. Whether the State is still in the tree, so it is safe to call setState
Why: Check mounted before setState after an await to avoid calling it on a disposed State.
How are async, Futures, Streams, and isolates tested?
async/await, Future vs Stream, FutureBuilder, and offloading work to isolates. 13 MCQs · 13 short answers
What does await do?
Correct answer: B. Pauses the async function until the awaited Future completes, without blocking the UI
Why: await suspends the function and resumes when the Future completes.
What is the difference between a Future and a Stream?
Correct answer: B. A Future is a single async value; a Stream is a sequence of async events over time
Why: Use Future for one-off results and Stream for repeated or continuous events.
What is a Future in Dart?
Answer: A Future represents a value that will be available later, the result of an asynchronous operation such as a network call or file read. It completes once, with a value or an error, and you react to it with await, or with .then and .catchError.
What is a Stream, and how does it differ from a Future?
Answer: A Stream is a sequence of asynchronous events delivered over time that you listen to. Unlike a Future, which yields a single result, a Stream can emit many values, errors, and a done event, which suits user input, sockets, or sensor data.
Which widget rebuilds based on the latest snapshot of a Future?
Correct answer: B. FutureBuilder
Why: FutureBuilder rebuilds as the Future moves through waiting, done, and error states.
How do you turn a Future into UI with FutureBuilder?
Answer: You give FutureBuilder the Future and a builder. The builder receives an AsyncSnapshot whose connectionState and data or error you inspect to show loading, success, or error UI. Create the Future once outside build so it is not refetched on every rebuild.
How do you consume a Stream in the UI?
Answer: Use a StreamBuilder, which subscribes to the stream and rebuilds with each new snapshot so you can render the latest value, an error, or a waiting state. For non-UI use you call stream.listen and remember to cancel the subscription when done.
Which widget listens to a Stream and rebuilds on each event?
Correct answer: B. StreamBuilder
Why: StreamBuilder rebuilds with each new value emitted by the stream.
Why move heavy JSON parsing to compute or an isolate?
Correct answer: B. Because otherwise the work runs on the UI isolate and causes jank
Why: Offloading CPU-heavy work keeps the UI isolate free to render smoothly.
Explain isolates and how they differ from threads.
Answer: An isolate is an independent worker with its own memory and event loop, and isolates communicate only by passing messages over ports rather than sharing memory. Unlike threads that share memory and need locks, isolates avoid data races by design, which makes Dart concurrency safer but requires copying data between them.
When and how do you use compute?
Answer: Use compute to run a top-level or static function on a separate isolate for CPU-heavy work such as parsing large JSON or processing images, so the UI isolate stays responsive. You call compute with the function and its argument, and it returns a Future with the result.
How do isolates communicate?
Correct answer: B. By passing messages over ports; they do not share memory
Why: Isolates exchange data via SendPort and ReceivePort messages.
What is the difference between .then and await?
Answer: Both handle a Future's result. .then registers a callback that runs when the Future completes and chains further callbacks. await pauses an async function until the Future completes and reads the value inline, which usually reads more clearly. The effect is equivalent.
How do you handle errors in asynchronous code?
Answer: With await, wrap the call in try/catch and use finally for cleanup. With the callback style, use .catchError or the onError argument, and for streams provide an onError handler. Always handle errors so failures surface instead of being silently swallowed.
What does an async* function return?
Correct answer: B. A Stream, using yield to emit values
Why: async* generators produce a Stream and emit with yield.
What is the difference between async, async*, and sync*?
Answer: async returns a Future for a single asynchronous result. async* returns a Stream and emits values with yield. sync* returns a lazily produced Iterable, also using yield. The starred forms are generators that produce sequences rather than one value.
What does Future.delayed do, and when is it used?
Answer: Future.delayed returns a Future that completes after a given duration, optionally running a computation afterwards. It is used for simple timed delays, retry backoff, or simulating latency in demos and tests.
How do you handle errors from an awaited Future?
Correct answer: B. Wrap the await in a try/catch block
Why: Awaited errors are thrown into the function and caught by try/catch.
How do you run several asynchronous operations in parallel?
Answer: Start the Futures without awaiting them one by one, then pass them to Future.wait, which runs them concurrently and completes when all finish, returning the results in order. This is faster than awaiting each in sequence when the operations are independent.
What is the difference between a single-subscription and a broadcast stream?
Correct answer: B. A single-subscription stream allows one listener; a broadcast stream allows many
Why: Use a broadcast stream when multiple listeners must receive the same events.
Why can the UI still freeze even when you use async/await?
Correct answer: B. Because async code runs on the UI isolate; only the awaited gaps yield, so heavy synchronous work between awaits still blocks
Why: async does not create threads, so CPU-bound work must go to an isolate.
Why can heavy work still jank the UI despite using async and await?
Answer: async and await do not create new threads; the code still runs on the UI isolate and only yields at await points. A long synchronous computation between awaits blocks the event loop and the current frame, causing jank. The fix is to move CPU-bound work to another isolate via compute.
What is the difference between a microtask and an event in Dart's event loop?
Answer: Dart drains the microtask queue completely before handling the next item from the event queue. Microtasks, scheduled via scheduleMicrotask or completed Futures, run sooner; events include I/O, timers, and gestures. Flooding the microtask queue can starve events, so use microtasks sparingly.
What does Future.delayed do?
Correct answer: B. Completes after a given duration, useful for simple delays
Why: Future.delayed returns a Future that completes after the specified time.
What does Future.wait do?
Correct answer: B. Runs multiple Futures concurrently and completes when all have finished
Why: Future.wait awaits a list of Futures in parallel and returns their results.
Why handle a Future explicitly instead of silently not awaiting it?
Correct answer: B. An unhandled Future error can go unnoticed, so being explicit surfaces errors and documents intent
Why: Fire-and-forget Futures can swallow errors; handle or unawaited them deliberately.
What do interviews ask about networking and local storage in Flutter?
http and dio, JSON serialisation, error handling, and shared_preferences vs sqflite vs Hive. 12 MCQs · 12 short answers
How do you make a simple GET request and decode the response?
Answer: You call http.get with the URL, check the statusCode, then pass response.body to jsonDecode to get a Map or List, and map that into your model. In real apps you also handle errors, timeouts, and non-2xx responses.
Which package is commonly used for basic HTTP requests in Flutter?
Correct answer: B. http
Why: The http package provides simple GET, POST, and other request helpers.
What does dio add over the basic http package?
Correct answer: B. Interceptors, global config, timeouts, cancellation, and easier error handling
Why: dio is a feature-rich client with interceptors and richer configuration.
How do you parse JSON into model objects safely?
Answer: Define a model with a fromJson factory that reads typed fields from the decoded map, or generate it with json_serializable to avoid hand-written mistakes. Centralising parsing in the model keeps the rest of the app working with typed objects rather than dynamic maps.
When would you use dio instead of the http package?
Answer: Use dio when you need more than simple requests: interceptors for auth tokens and logging, global base options and timeouts, request cancellation, retries, and richer error objects. For a small app with a few calls, the http package is enough.
How do you turn a decoded JSON map into a typed Dart object?
Correct answer: B. Write a fromJson factory or use code generation such as json_serializable
Why: Dart maps JSON via a fromJson factory or generated serialisation code.
Which local storage option fits simple key-value settings?
Answer: shared_preferences fits small primitive key-value data such as a theme flag, an onboarding-seen boolean, or a username. It is asynchronous and easy to use, but it is not meant for large or structured data.
How do you choose between shared_preferences, sqflite, and Hive?
Answer: Use shared_preferences for small primitive settings, sqflite when you need relational, queryable structured data with SQL, and Hive when you want fast, lightweight object storage without SQL. The choice depends on data size, structure, and your query needs.
What does jsonDecode return for a JSON object?
Correct answer: B. A Map<String, dynamic>
Why: jsonDecode yields dynamic structures, typically a map for a JSON object.
Which storage suits small key-value preferences such as a theme flag?
Correct answer: B. shared_preferences
Why: shared_preferences stores simple primitive key-value pairs.
Why introduce a repository layer for data access?
Answer: A repository hides where data comes from, whether network, cache, or database, behind one clean interface. The UI and business logic depend only on that interface, which makes it easy to swap implementations, add caching, and test with a fake repository.
When would you choose sqflite over shared_preferences?
Correct answer: B. For structured, relational, queryable data with many rows
Why: sqflite is a SQLite database suited to structured, queryable data.
How should you represent loading, success, and error states for a request?
Answer: Model them explicitly, for example with a sealed class or an AsyncValue, so the UI is forced to handle each case. This avoids ambiguous nulls and forgotten error paths, and makes it simple to show a spinner, the content, or a retry prompt.
Why should network requests not be triggered directly inside build?
Answer: build can run many times, so a request placed there fires repeatedly and wastes bandwidth and battery. Trigger the request once from initState, an event handler, or a provider, and feed the result into the UI through state.
What is Hive typically used for?
Correct answer: B. Fast, lightweight local NoSQL storage of objects without SQL
Why: Hive is a fast key-value and object store that avoids SQL boilerplate.
How do you handle request timeouts and retries?
Answer: Set a timeout on the call, for example with .timeout on the Future or via dio's options, and catch it to show an error or retry. For retries, use a limited number of attempts with backoff, and make the operation idempotent so repeats are safe.
Why wrap network results in a sealed result or state type?
Correct answer: B. To represent loading, success, and error explicitly so the UI handles every case
Why: Explicit states make loading and error handling exhaustive and predictable.
How do you keep API keys and secrets out of the codebase?
Answer: Keep secrets out of committed source. Inject them at build time with --dart-define or environment configuration, and store user tokens in secure storage such as flutter_secure_storage. Never hard-code or log secrets, and rotate any that leak.
How do you cache network data for offline or faster access?
Answer: Store fetched data locally in Hive, sqflite, or a file, and read from the cache first while refreshing in the background. A repository is a natural place for this cache-then-network strategy, and you add timestamps or invalidation rules to keep the data fresh.
Which HTTP status range indicates a successful response?
Correct answer: B. 2xx
Why: 2xx codes indicate success; 4xx are client errors and 5xx server errors.
What does jsonEncode do, and when do you use it?
Answer: jsonEncode converts a Dart structure of maps, lists, and primitives, typically a model's toJson output, into a JSON string. You use it when sending data in a request body or saving structured data as text.
Why should network calls not run directly inside build?
Correct answer: B. build runs often, so the call would fire repeatedly; trigger it from initState, an event, or a provider
Why: Frequent rebuilds would re-fire the request, so start it once outside build.
What is the benefit of a repository layer between the UI and data sources?
Correct answer: B. It hides data-source details and gives the UI one clean, testable API
Why: A repository abstracts network and cache sources behind a single interface.
How do you usually keep an API token out of source code?
Correct answer: B. Load it from secure storage or build-time configuration, not committed code
Why: Secrets belong in secure storage or build config, never in committed code.
Which Flutter animation questions are common?
Implicit vs explicit animations, AnimationController, Tween, and Hero transitions. 10 MCQs · 10 short answers
What is the difference between implicit and explicit animations?
Answer: Implicit animations, such as AnimatedContainer or AnimatedOpacity, animate automatically whenever you give them new target values over a duration. Explicit animations use an AnimationController that you start, stop, and drive yourself, giving fine control over timing, repetition, and direction.
What is the role of an AnimationController?
Answer: An AnimationController produces values, usually from 0 to 1, over a duration, ticking once per frame via a vsync provider. You drive animations from it, often mapping its value through a Tween and a Curve, and you must dispose it when the widget is removed.
What is the difference between implicit and explicit animations?
Correct answer: B. Implicit animations such as AnimatedContainer tween automatically to new values; explicit ones use an AnimationController you drive
Why: Implicit widgets tween to new values for you; explicit ones give manual control.
What does an AnimationController need to function?
Correct answer: B. A vsync ticker provider, usually via a TickerProviderStateMixin
Why: The controller drives per-frame values using a vsync ticker.
What does a Tween do, and how is it combined with a controller?
Answer: A Tween defines a begin and end value of any type, such as colours or offsets, and interpolates between them. You attach it to an AnimationController, often via animate, so as the controller runs from 0 to 1 the Tween produces the in-between values.
How do you animate a property change with the least code?
Answer: Use an implicit animation widget like AnimatedContainer, AnimatedOpacity, or AnimatedPositioned and change its target values inside setState. Flutter tweens from the old value to the new one over the given duration and curve automatically.
What is a Tween used for?
Correct answer: B. Defining the range of values an animation interpolates between
Why: A Tween maps the controller 0 to 1 value onto a begin and end range.
Which widget animates between values automatically when its properties change?
Correct answer: B. AnimatedContainer
Why: AnimatedContainer tweens to new values over a duration implicitly.
What is a Hero animation, and how do you set one up?
Answer: A Hero animation flies a shared element between two routes during navigation. You wrap the element on both screens in a Hero with the same tag, and Flutter animates its size and position from the source to the destination as the route transitions.
What does a Hero widget do?
Correct answer: B. Animates a shared-element transition between two routes
Why: Matching Hero tags animate an element flying between screens.
Why must you dispose an AnimationController?
Correct answer: B. To stop its ticker and free resources, preventing leaks
Why: An undisposed controller keeps ticking and leaks memory.
How do you build a staggered animation?
Answer: Drive several Tweens from a single AnimationController, giving each one an Interval curve that defines the slice of the 0-to-1 timeline during which it animates. By offsetting these intervals, the properties start and finish at different times to create a staggered effect.
What is the role of a CurvedAnimation?
Correct answer: B. It applies an easing curve to an animation value for non-linear motion
Why: CurvedAnimation maps linear progress through an easing curve.
What does a CurvedAnimation add?
Answer: A CurvedAnimation applies an easing curve, such as Curves.easeInOut, to a linear animation so motion accelerates and decelerates naturally. You wrap the controller in a CurvedAnimation and drive your Tween from that instead of the raw linear value.
Which widget rebuilds efficiently as an animation runs?
Answer: AnimatedBuilder, or ListenableBuilder, rebuilds only the part of the tree returned by its builder on each tick, while you keep the rest static via the child parameter. This avoids rebuilding the whole widget every frame.
Which widget fades a child as its opacity changes over time?
Correct answer: A. AnimatedOpacity
Why: AnimatedOpacity implicitly animates opacity changes.
How do you run several animations with different timings from one controller?
Correct answer: B. Use Interval curves and multiple Tweens driven by one controller, a staggered animation
Why: Staggered animations assign each Tween an Interval of the controller timeline.
Why must AnimationControllers be disposed, and where?
Answer: An AnimationController keeps a ticker running and holds listeners, so if it is not disposed it leaks memory and may call back into a removed widget. You dispose it in the State's dispose method, the same place you release other controllers.
How do you repeat or reverse an animation?
Answer: AnimationController offers forward, reverse, and repeat methods; repeat loops the animation and can reverse each cycle. You can also listen to status changes to chain or reverse at the ends, which drives looping effects like pulsing or spinners.
Why prefer AnimatedBuilder or ListenableBuilder for explicit animations?
Correct answer: B. They rebuild only the animating subtree each tick rather than the whole widget
Why: Scoping the rebuild keeps the animation cheap and smooth.
What Flutter testing questions should you expect?
Unit, widget, integration, and golden tests, plus mocking and the test pyramid. 12 MCQs · 12 short answers
What does a unit test verify in Flutter?
Correct answer: B. A single function, method, or class in isolation
Why: Unit tests check logic in isolation without the widget tree.
What are the three main kinds of tests in Flutter?
Answer: Unit tests check a single function or class in isolation. Widget tests build a widget in a test harness and verify its rendering and interaction without a full app. Integration tests run the whole app on a device or emulator to verify end-to-end flows.
What is a widget test?
Correct answer: B. A test that builds a widget in a test environment and interacts with it without the full app
Why: Widget tests pump a widget and assert on its rendered output and behaviour.
Which call builds a widget in a widget test?
Correct answer: B. tester.pumpWidget
Why: pumpWidget mounts the widget into the test harness.
How do you write a basic unit test?
Answer: In a file under the test directory you use the test function, set up inputs, call the code under test, and assert the result with expect and a matcher. You group related tests with group and share setup with setUp and tearDown.
What does tester.pump do after an interaction?
Correct answer: B. Advances the clock and rebuilds so you can settle frames and animations
Why: pump triggers a frame; pumpAndSettle runs until animations finish.
How does a widget test work?
Answer: You use testWidgets, call tester.pumpWidget to mount the widget, interact with methods like tap and enterText, call pump or pumpAndSettle to advance frames, and assert with expect and Finders such as find.text. It runs quickly without a real device.
What is the purpose of a golden test?
Correct answer: B. To compare rendered pixels against a stored reference image
Why: Golden tests catch unintended visual changes through image comparison.
What is the difference between pump and pumpAndSettle?
Answer: pump schedules a single frame and advances the clock by an optional duration, which is useful for stepping through animations. pumpAndSettle repeatedly pumps until no frames are scheduled, so it waits for animations and transitions to finish before you assert.
What does the integration_test package add over widget tests?
Correct answer: B. It runs the full app on a real device or emulator to verify end-to-end flows
Why: Integration tests exercise the whole app, including platform behaviour.
How do you locate a widget in a test?
Correct answer: B. With a Finder such as find.text or find.byKey
Why: Finders like find.text and find.byType select widgets to assert on.
What are golden tests, and when are they useful?
Answer: Golden tests render a widget and compare the output pixels against a stored reference image, failing if they differ. They are useful for catching unintended visual regressions in components or themes, and you regenerate the goldens deliberately when a change is intended.
What does the integration_test package give you over widget tests?
Answer: integration_test runs the complete app on a real device or emulator, exercising real navigation, platform plugins, and performance, so it verifies end-to-end user journeys. Widget tests are faster and isolated, while integration tests catch issues that only appear in the assembled app.
Why use mocks or fakes in tests?
Correct answer: B. To replace real dependencies such as the network so tests are fast, deterministic, and isolated
Why: Mocks isolate the unit under test from slow or non-deterministic dependencies.
What shape does a healthy test pyramid have?
Correct answer: B. Many fast unit tests, fewer widget tests, and a few integration tests
Why: A broad base of unit tests with fewer slow end-to-end tests keeps suites fast.
How do you find and interact with widgets in a test?
Answer: You use Finders such as find.text, find.byType, and find.byKey to locate widgets, then act with tester.tap, tester.enterText, or tester.drag. After acting you pump a frame and assert the resulting state with expect.
Why and how do you use mocks in tests?
Answer: You replace real dependencies, such as a network client or repository, with mocks or fakes so tests are fast, deterministic, and focused on the unit under test. Packages like mocktail or mockito let you stub return values and verify that methods were called.
How do you assert that a widget appears exactly once?
Correct answer: B. expect(finder, findsOneWidget)
Why: findsOneWidget matches exactly one widget for the finder.
Where do Flutter tests live by default?
Correct answer: B. In the test/ directory
Why: Test files live under test/ and run with flutter test.
What is the testing pyramid, and why follow it?
Answer: The pyramid recommends many fast unit tests at the base, fewer widget tests in the middle, and a small number of slower integration tests at the top. Following it keeps the suite fast and reliable while still covering real user flows.
How do you test asynchronous code and streams?
Answer: For Futures you make the test async and await the result, or use expectLater with the completion matcher. For streams you use expectLater with emitsInOrder and related matchers. You can also use fakeAsync to control time deterministically.
Why test business logic separately from widgets?
Correct answer: B. Isolated logic tests are faster and more stable, which is easier when state is decoupled via BLoC or Riverpod
Why: Decoupled logic enables fast, UI-independent unit tests.
Where do test files live, and how do you run them?
Answer: Test files live in the test directory and usually end with _test.dart, and you run them with flutter test, optionally targeting a single file or a test by name. Integration tests live in the integration_test directory and run on a device or emulator.
How does decoupling logic from the UI improve testability?
Answer: When business logic lives in plain classes, BLoCs, or Riverpod notifiers rather than inside widgets, you can unit test it quickly without pumping widgets or using a device. The UI then becomes a thin layer covered by a few widget tests, keeping the suite fast and stable.
How do Flutter performance interview questions work?
Impeller, const constructors, RepaintBoundary, DevTools, and cutting rebuild cost. 11 MCQs · 11 short answers
Why does using const widgets improve performance?
Answer: A const widget is created once at compile time and is canonicalised, so Flutter can skip rebuilding and re-laying-out that subtree because the instance never changes. Marking stable widgets const reduces work and allocations on every frame.
Why mark widgets const where possible?
Correct answer: B. Const widgets are created once at compile time and skip rebuilds, reducing work
Why: Const subtrees are canonicalised and need no rebuild or re-layout.
What is Impeller?
Correct answer: B. Flutter's rendering engine that precompiles shaders to avoid first-run shader jank
Why: Impeller replaces the old runtime shader compilation that caused early-frame jank.
What is Impeller, and what problem does it solve?
Answer: Impeller is Flutter's modern rendering engine that precompiles shaders ahead of time instead of compiling them at runtime. This removes the shader-compilation jank that previously caused stutter the first time an animation or effect ran.
How does RepaintBoundary help performance?
Answer: RepaintBoundary puts a subtree on its own compositing layer, so when that subtree repaints, for example during an animation, the rest of the screen is not repainted with it. Placed around frequently repainting widgets, it cuts wasted paint work.
What does RepaintBoundary do?
Correct answer: B. Isolates a subtree onto its own layer so its repaints do not affect the rest
Why: It limits repaint work to the bounded subtree, for example around an animating widget.
How do you profile and fix jank in a Flutter app?
Answer: Run in profile mode and use Flutter DevTools' performance view and rebuild profiler to find slow frames and frequent rebuilds, plus the performance overlay to watch UI and raster thread times. Then narrow setState scope, add const, isolate repaints, and move heavy work off the UI isolate.
Why is ListView.builder important for long lists?
Answer: ListView.builder lazily builds only the items near the viewport rather than all of them up front, so memory use and build time stay bounded no matter how long the list is. This keeps scrolling smooth for large or infinite data sets.
Which tool helps you find expensive rebuilds and jank?
Correct answer: B. Flutter DevTools, including the performance and rebuild profilers
Why: DevTools shows frame timings, rebuild counts, and the timeline.
Why use ListView.builder for long lists?
Correct answer: B. It builds only on-screen items, avoiding the cost of building everything at once
Why: Lazy building keeps memory and build time bounded for long lists.
What is the frame budget for smooth scrolling?
Answer: For 60fps you have about 16 milliseconds per frame to handle build, layout, and paint, and even less on 90 or 120Hz screens. Work that overruns the budget drops frames and causes visible jank, so per-frame work must stay light.
How does narrowing setState scope reduce rebuild cost?
Answer: If you call setState on a small, dedicated widget that holds only the changing state, Flutter rebuilds just that widget instead of a large ancestor subtree. Splitting widgets and keeping state local therefore limits how much of the tree is rebuilt each change.
What is the frame budget for a smooth 60fps app?
Correct answer: B. About 16ms per frame
Why: 60fps leaves roughly 16ms per frame for build, layout, and paint.
How does narrowing setState scope help performance?
Correct answer: B. Only the smallest widget holding the changing state rebuilds, not a large subtree
Why: Smaller stateful widgets limit the rebuilt portion of the tree.
How do you reduce image-related memory and jank?
Answer: Decode images at the size you actually display using cacheWidth and cacheHeight, cache them with a package like cached_network_image, and avoid loading full-resolution assets into small widgets. This cuts decode time and memory pressure that otherwise cause jank.
What does cacheExtent on a scrollable affect?
Correct answer: B. How much off-screen content is kept built to smooth scrolling
Why: A larger cacheExtent pre-builds nearby off-screen items for smoother scrolling.
Why avoid expensive work inside the build method?
Answer: build can run many times per second, so heavy computation, allocation, or I/O there is repeated and wastes the frame budget. Move such work to initState, a provider, a memoised value, or another isolate, and keep build to describing the UI.
Why avoid Opacity for frequently changing transparency?
Correct answer: B. It can force an offscreen buffer; prefer AnimatedOpacity or fading at paint time
Why: Opacity can trigger costly layer compositing, so cheaper alternatives are preferred.
What does the Flutter performance overlay show?
Answer: The performance overlay draws two graphs, one for the UI thread and one for the raster thread, showing how long each frame takes. Bars that cross the threshold mark dropped frames, helping you see when and where the app janks.
How do you reduce startup jank from large images?
Correct answer: B. Resize and cache images, set cacheWidth/cacheHeight, and lazy-load where possible
Why: Decoding right-sized images cuts memory and decode time at startup.
When do you use RepaintBoundary versus splitting widgets to cut rebuilds?
Answer: Splitting widgets and localising state reduces rebuild work, which is build and layout. RepaintBoundary reduces repaint work by isolating a layer. They target different phases, so use widget splitting and const for rebuild-heavy cases and RepaintBoundary for paint-heavy, frequently animating areas.
What does the performance overlay show?
Correct answer: B. The UI and raster thread frame timings as graphs to spot jank
Why: The overlay graphs UI and raster thread times so slow frames are visible.
What architecture questions do senior Flutter interviews ask?
Feature-first structure, layering, MVVM/clean, and dependency injection choices. 9 MCQs · 9 short answers
What is a feature-first folder structure, and why use it?
Answer: Feature-first organises code into a folder per feature, each holding that feature's UI, state, and data, rather than grouping all widgets, models, and services separately. It keeps related code together, scales better as the app grows, and makes features easier to find and remove.
Describe a layered architecture for a Flutter app.
Answer: A common layering is presentation (widgets and view models or BLoCs), domain (entities and use cases or business rules), and data (repositories and data sources). Dependencies point inward toward the domain, so UI and data details can change without affecting the core logic, which also improves testability.
What is a feature-first project structure?
Correct answer: B. Organising code by feature folders rather than by technical layer
Why: Feature-first keeps related UI, state, and data for a feature together.
What problem does a layered presentation, domain, and data architecture solve?
Correct answer: B. It separates concerns so UI, business rules, and data sources can change independently
Why: Clear layers keep dependencies pointing inward and make code testable.
What does the MVVM pattern look like in Flutter?
Answer: In MVVM the view is the widget, the view model holds presentation state and exposes it, often via a ChangeNotifier or Riverpod notifier, and the model or repository supplies data. The view binds to the view model and rebuilds on changes, which keeps UI and logic separate.
In MVVM for Flutter, what is the view model responsible for?
Correct answer: B. Holding presentation state and exposing it to the view, separate from business logic
Why: The view model adapts domain data into state the view binds to.
What is dependency injection?
Correct answer: B. Supplying a class its dependencies from outside rather than creating them internally
Why: DI decouples classes from how their collaborators are built, which aids testing.
What is dependency injection, and why does it matter?
Answer: Dependency injection means giving a class its collaborators from outside instead of creating them internally. It decouples classes from concrete implementations, which makes code easier to test by substituting fakes and easier to reconfigure, such as swapping a real API for a mock.
Why depend on abstractions rather than concrete classes?
Correct answer: B. It lets you swap implementations, such as a fake repository in tests, without changing callers
Why: Programming to interfaces enables substitution and testability.
Why separate UI widgets from business logic?
Correct answer: B. So logic can be tested and reused independently of the UI
Why: Separation makes logic unit-testable and the UI simpler.
Why program to interfaces rather than concrete classes?
Answer: Depending on an abstraction lets you swap implementations without changing callers, for instance using a fake repository in tests or switching data sources. It reduces coupling and supports the dependency inversion principle, keeping high-level logic independent of low-level details.
Why separate business logic from widgets?
Answer: Keeping logic out of widgets makes it reusable and testable on its own and keeps widgets focused on presentation. It avoids bloated build methods, reduces duplication, and lets the logic and the UI change independently.
What is the role of a service or repository class?
Correct answer: B. To encapsulate access to a data source or capability behind a clean API
Why: It centralises data access so the rest of the app depends on one interface.
What belongs in a repository, and what does it depend on?
Answer: A repository encapsulates data access for a domain area behind a clean interface, coordinating data sources such as a remote API and a local cache. It returns domain models, depends on abstractions of its data sources, and shields the rest of the app from those details.
What is a common downside of over-engineering architecture early?
Correct answer: B. Excessive layers and boilerplate slow delivery before the app needs them
Why: Add abstraction when the codebase earns it, not pre-emptively.
What is the risk of over-engineering architecture too early?
Answer: Adding many layers, abstractions, and boilerplate before the app needs them slows development, makes simple changes harder, and can confuse new contributors. A pragmatic approach starts simple and introduces structure as complexity and team size genuinely demand it.
Which tools are commonly used for dependency injection in Flutter?
Answer: get_it provides a service locator, often combined with injectable for code generation, while Riverpod offers providers that double as DI plus state management, and Provider can inject dependencies down the tree. The choice depends on whether you prefer a locator or a provider-based approach.
Which packages are commonly used for service location or DI in Flutter?
Correct answer: B. get_it, injectable, or Riverpod providers
Why: get_it with injectable, and Riverpod, are common DI approaches in Flutter.
What do interviews ask about Flutter platform channels and releasing apps?
Platform channels, FFI, flavors, build modes, and shipping to the App Store and Play. 7 MCQs · 7 short answers
What is a platform channel, and when do you need one?
Answer: A platform channel is a typed message bridge between Dart and native code, Kotlin or Swift, used when a feature is only available through a platform API or an existing native SDK with no Flutter plugin. You send method calls and arguments across it and receive results asynchronously.
What is a platform channel used for?
Correct answer: B. Communicating between Dart and native Kotlin or Swift code via messages
Why: Platform channels pass method calls and data between Flutter and the host platform.
When would you use FFI instead of a platform channel?
Correct answer: B. To call C or native libraries directly and synchronously for performance
Why: dart:ffi binds native C libraries directly, avoiding channel overhead.
When would you use dart:ffi instead of a platform channel?
Answer: Use dart:ffi to call C or C++ libraries directly and synchronously, which avoids the asynchronous message overhead of a channel and suits performance-critical native code or reusing existing C libraries. Channels remain better for talking to platform-specific Java, Kotlin, or Swift APIs.
What is the difference between debug and release build modes?
Correct answer: B. Debug enables hot reload and assertions; release is AOT-compiled and optimised
Why: Release builds are AOT-compiled and stripped of debug tooling for speed.
What are build flavors used for?
Correct answer: B. Building variants such as dev, staging, and production with different config from one codebase
Why: Flavors produce separate app variants with their own config and identifiers.
What is the difference between debug, profile, and release builds?
Answer: Debug builds use JIT compilation, enable hot reload and assertions, and are slower. Profile builds are optimised but keep some tooling for performance analysis. Release builds are AOT-compiled, fully optimised, and stripped of debugging aids, and are what you ship to stores.
What are build flavors, and why use them?
Answer: Flavors let you build distinct variants of the app, such as dev, staging, and production, from one codebase, each with its own application id, name, icons, and configuration like API endpoints. This avoids separate projects and lets the variants coexist on a device.
Which artefact do you typically upload to the Google Play Store?
Correct answer: B. An Android App Bundle (.aab)
Why: Play prefers the .aab bundle, from which it generates optimised APKs.
What do you upload to the Play Store and the App Store?
Answer: For Google Play you upload an Android App Bundle, an .aab, from which Play generates optimised APKs per device. For the App Store you archive an iOS build in Xcode and upload it to App Store Connect, usually via Xcode or Transporter, then submit it for review.
Why build with --obfuscate and --split-debug-info?
Correct answer: B. To obfuscate Dart symbols while keeping symbol files that de-obfuscate crash traces
Why: Obfuscation protects code while split debug info lets you read stack traces.
What does the iOS release flow produce before App Store submission?
Correct answer: B. A build you archive and upload via Xcode or Transporter
Why: iOS release goes through an Xcode archive uploaded to App Store Connect.
How do you make release builds smaller and protect the code?
Answer: Build app bundles so stores ship device-specific APKs, enable resource shrinking and tree shaking, compress or right-size assets, and split native libraries per ABI. Use --obfuscate with --split-debug-info to obfuscate Dart symbols while keeping symbol files that de-obfuscate crash reports.
What basic steps prepare an app for store release?
Answer: Set the app name, application id or bundle id, version, and icons, switch to release signing with your keystore or certificates, remove debug code and test endpoints, build the release artefact, and test it on real devices before submitting through Play Console or App Store Connect.
Related Flutter guides on this site
- Flutter Development: The Complete 2026 Guide — the hub for everything below.
- Flutter State Management in 2026: Provider vs Riverpod vs BLoC
- Flutter App Architecture in 2026: A Practical Feature-First Guide
- Flutter Performance in 2026: Impeller, DevTools, and Rebuild Reduction
- Flutter Testing Strategy in 2026: Unit, Widget, Integration, and Goldens
- go_router in Flutter: Deep Linking, Nested Navigation, and Web URLs
For further reading, compare the answers with the official Flutter documentation, Dart language guides, Flutter's DevTools docs, build modes, platform channels, and Dart obfuscation guidance.