Skip to content

Flutter Developer Interview Questions & Answers

40 questions for Flutter Developer interviews — 5 easy, 30 medium, 5 hard.

About the Flutter Developer role

A Flutter developer builds cross-platform apps in Dart, owning features from UI to state management and API integration. They use Riverpod or Bloc, go_router, Firebase and local storage, write widget and unit tests, and handle platform setup for permissions and push notifications. Flutter roles are common in Indian startups, fintech and service companies that want one team to deliver Android and iOS apps.

Dart and widgets Riverpod or Bloc go_router navigation Firebase integration Widget testing
Solved for this roleStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
3 questions free Unlock the rest in seconds
01

How do you implement OTP login with a phone number?

Save to ▾
Show answer
The user enters a phone number, the backend or an auth service sends an OTP via SMS and returns a verification ID. The user enters the code, which is verified to issue session tokens. On Android, use the SMS Retriever or User Consent API to auto-read the code without SMS permissions; on iOS, set the text content type to one-time code for autofill. Rate-limit requests, expire codes quickly and protect against abuse.
02

Write a Dart sealed class for API results and handle it with an exhaustive switch.

Save to ▾
Show answer
sealed class Result<T> {} class Success<T> extends Result<T> { Success(this.data); final T data; } class Failure<T> extends Result<T> { Failure(this.message); final String message; } Widget view(Result<List<String>> r) => switch (r) { Success(:final data) => ListView(children: [for (final s in data) Text(s)]), Failure(:final message) => Center(child: Text(message)), };
03

Explain the StatefulWidget lifecycle methods.

Save to ▾
Show answer
createState creates the State. initState runs once for setup like controllers and subscriptions; do not use inherited widgets there. didChangeDependencies runs after initState and when inherited data changes. build runs on every rebuild. didUpdateWidget runs when the parent rebuilds with new configuration. dispose runs when removed permanently, where you cancel streams, dispose controllers and timers to avoid memory leaks.
04

Write a Cubit that loads products and emits loading, success and error states.

Save to ▾
Show answer
sealed class ProductState {} class ProductLoading extends ProductState {} class ProductLoaded extends ProductState { ProductLoaded(this.items); final List<Product> items; } class ProductError extends ProductState { ProductError(this.message); final String message; } class ProductCubit extends Cubit<ProductState> { ProductCubit(this._repo) : super(ProductLoading()); final ProductRepository _repo; Future<void> load() async { emit(ProductLoading()); try { emit(ProductLoaded(await _repo.fetchProducts())); } catch (_) { emit(ProductError('Could not load products')); } } }
05

What is BuildContext?

Save to ▾
Show answer
BuildContext is a handle to a widget's location in the element tree. It is used to look up inherited data like Theme.of(context), MediaQuery and Navigator, and providers. Each widget's build method gets its own context. Common mistakes are using a context from above a widget that provides data, or using a context after an async gap when the widget may be unmounted; check context.mounted before using it after await.
06

What is Maestro and why do mobile teams use it?

Save to ▾
Show answer
Maestro is an open-source mobile UI testing framework where flows are written in simple YAML, such as launchApp, tapOn "Login" and assertVisible "Welcome". It works with native, Flutter and React Native apps on Android and iOS, handles waiting automatically, and reduces flakiness compared to many older tools. Teams use it for smoke tests of critical journeys in CI because tests are quick to write and readable by QA.
07

Parsing a 10 MB JSON file freezes the Flutter UI for two seconds. How do you fix it?

Save to ▾
Show answer
JSON decoding is CPU-bound and runs on the main isolate, blocking frames. Move it to a background isolate with Isolate.run(() => parseProducts(jsonString)) or compute. Also consider whether the API can paginate or return less data, and parse lazily. Test in profile mode with DevTools performance view to confirm the frame time improvement, since debug mode exaggerates slowness.
08

Explain the Bloc pattern and Cubit.

Save to ▾
Show answer
In Bloc, the UI adds events, the Bloc handles them with on<Event> handlers and emits new immutable states, and widgets rebuild with BlocBuilder or react with BlocListener. A Cubit is a simpler version where the UI calls methods directly and the Cubit emits states, without event classes. Use Cubit for straightforward logic and full Bloc when you need event transformations like debounce or detailed traceability.
09

Where should you keep API keys and secrets in a mobile app?

Save to ▾
Show answer
Anything bundled in a mobile app can be extracted by decompiling, so never ship true secrets like payment secret keys or admin credentials. Keep them on your backend and let the app call your server. Public client keys, such as maps or Firebase config, are acceptable but should be restricted by app package name, bundle ID and certificate fingerprint. Use environment configuration in CI rather than committing keys to the repository.
10

Describe a time you had to deliver a feature with incomplete requirements.

Save to ▾
Show answer
Situation: product asked for a referral feature with only a one-line description. Task: deliver in a sprint. Action: I listed open questions, met the product manager for 20 minutes to agree on core rules, built the agreed version with configurable reward values from the backend, and documented assumptions in the ticket. Result: we shipped on time, and later changes needed only config updates instead of a new release.
11

After an API call finishes, the app throws an error about using BuildContext across async gaps or setState after dispose. How do you fix it?

Save to ▾
Show answer
The user left the screen before the Future completed, so the widget was unmounted. Before using context or calling setState after await, check if (!context.mounted) return; or if (!mounted) return; in State. Better still, move async logic out of widgets into a state management layer that the UI listens to, and cancel subscriptions or timers in dispose so no callbacks run on a dead widget.
12

The cart total shows stale values on the checkout screen after items are removed on the cart screen. How do you fix it?

Save to ▾
Show answer
Each screen probably holds its own copy of cart data. Move cart state into a single shared state object, such as a Riverpod notifier or a Cubit provided above both screens, and have both screens watch it. All mutations go through that object, which emits a new immutable state. Derived values like total should be computed from the state, not stored separately, so they can never become inconsistent.
13

Write a go_router configuration with an auth redirect.

Save to ▾
Show answer
final router = GoRouter( initialLocation: '/', refreshListenable: authNotifier, redirect: (context, state) { final loggedIn = authNotifier.isLoggedIn; final goingToLogin = state.matchedLocation == '/login'; if (!loggedIn && !goingToLogin) return '/login'; if (loggedIn && goingToLogin) return '/'; return null; }, routes: [ GoRoute(path: '/', builder: (_, __) => const HomePage()), GoRoute(path: '/login', builder: (_, __) => const LoginPage()), GoRoute( path: '/product/:id', builder: (_, state) => ProductPage(id: state.pathParameters['id']!), ), ], );
14

What are useMemo, useCallback and React.memo used for?

Save to ▾
Show answer
React.memo skips re-rendering a component when its props are shallowly equal. useCallback keeps the same function reference between renders, and useMemo caches a computed value, both only changing when dependencies change. They help when passing callbacks to memoised list items or computing expensive values. Overusing them adds complexity with little gain; the React Compiler can now apply such memoisation automatically in supported projects.
15

How do you secure Cloud Firestore data accessed directly from a mobile app?

Save to ▾
Show answer
Because the app talks to the database directly, security rules are the backend. Write rules that check request.auth for authentication and that users can read or write only their own documents, for example allow read, write: if request.auth.uid == userId. Validate fields and types in rules, never leave test mode rules open, and move sensitive operations like payments or role changes to Cloud Functions. Test rules with the emulator suite.
16

Write a Dart HTTP client using dio with an interceptor that adds an auth token.

Save to ▾
Show answer
final dio = Dio(BaseOptions( baseUrl: 'https://api.example.com', connectTimeout: const Duration(seconds: 10), receiveTimeout: const Duration(seconds: 15), )); dio.interceptors.add(InterceptorsWrapper( onRequest: (options, handler) async { final token = await secureStorage.read(key: 'access_token'); if (token != null) options.headers['Authorization'] = 'Bearer $token'; handler.next(options); }, onError: (e, handler) { if (e.response?.statusCode == 401) authNotifier.logout(); handler.next(e); }, ));
17

Write a Riverpod provider that fetches data asynchronously and display it in a widget.

Save to ▾
Show answer
final productsProvider = FutureProvider.autoDispose<List<Product>>((ref) { final repo = ref.watch(productRepositoryProvider); return repo.fetchProducts(); }); class ProductsView extends ConsumerWidget { const ProductsView({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final products = ref.watch(productsProvider); return products.when( loading: () => const Center(child: CircularProgressIndicator()), error: (e, _) => const Center(child: Text('Failed to load')), data: (items) => ListView(children: [for (final p in items) ListTile(title: Text(p.name))]), ); } }
18

Write a Flutter screen that fetches a list from an API using FutureBuilder.

Save to ▾
Show answer
class UsersPage extends StatefulWidget { const UsersPage({super.key}); @override State<UsersPage> createState() => _UsersPageState(); } class _UsersPageState extends State<UsersPage> { late final Future<List<User>> _future = api.fetchUsers(); @override Widget build(BuildContext context) => FutureBuilder<List<User>>( future: _future, builder: (context, snap) { if (snap.connectionState != ConnectionState.done) { return const Center(child: CircularProgressIndicator()); } if (snap.hasError) return const Center(child: Text('Something went wrong')); final users = snap.data!; return ListView.builder( itemCount: users.length, itemBuilder: (_, i) => ListTile(title: Text(users[i].name)), ); }, ); }
19

How does Flutter's layout rule of constraints go down, sizes go up work?

Save to ▾
Show answer
A parent passes constraints, minimum and maximum width and height, to each child. The child chooses its size within those constraints and reports it up, then the parent positions the child. A widget cannot choose a size outside its constraints, which explains why a Container with width 100 may still fill the screen if forced by tight constraints. Understanding this rule solves most overflow and unbounded constraint errors.
20

Write an EAS configuration for development, preview and production builds.

Save to ▾
Show answer
{ "cli": { "version": ">= 12.0.0" }, "build": { "development": { "developmentClient": true, "distribution": "internal" }, "preview": { "distribution": "internal", "channel": "preview" }, "production": { "autoIncrement": true, "channel": "production" } }, "submit": { "production": {} } }
21

How do you handle pressure when a production issue affects many users?

Save to ▾
Show answer
I stay calm and focus on reducing impact first: confirm the issue, communicate status to the team, and look for the fastest safe mitigation such as a remote config switch, server-side fix or pausing a rollout. Then I find the root cause, ship a proper fix and write a short postmortem. Clear, regular updates to stakeholders reduce panic and let others plan around the issue.
22

The app needs a bottom navigation bar where each tab keeps its own navigation history. How do you implement it?

Save to ▾
Show answer
Use go_router's StatefulShellRoute.indexedStack, which creates a separate navigator branch for each tab and keeps their stacks alive with an IndexedStack. The scaffold shows a NavigationBar and switches branches with navigationShell.goBranch(index). This preserves scroll positions and nested pages when switching tabs, and deep links can open a specific tab and page. Handle tapping the active tab to pop to its root.
23

What is the difference between REST and GraphQL for mobile clients?

Save to ▾
Show answer
REST exposes resources at fixed endpoints, which can lead to over-fetching or many round trips for one screen. GraphQL exposes one endpoint where the client asks exactly for the fields it needs, reducing payload size and requests, which helps on slow mobile networks. GraphQL adds complexity in caching, error handling and server performance. Clients like Apollo or graphql_flutter provide normalised caching and code generation.
24

What is the freezed package used for?

Save to ▾
Show answer
freezed generates immutable data classes with copyWith, equality, toString and union types or sealed classes, and integrates with json_serializable for fromJson and toJson. It removes boilerplate when modelling state such as loading, data and error variants. You annotate a class, run build_runner, and get generated code in a part file. With Dart 3 sealed classes and records, some teams need it less, but it is still widely used.
25

Deep links open the app but land on the home screen instead of the product page. How do you debug it?

Save to ▾
Show answer
Check that the link configuration maps the URL path to the right screen name, including nested navigators, and that path parameters match. Verify verified link setup on both platforms: assetlinks.json for Android and the associated domains entitlement plus the site association file for iOS. Test with adb shell am start and xcrun simctl openurl. Also handle links when the app is cold started versus already running.
26

What is a Stream in Dart and how is it used in Flutter?

Save to ▾
Show answer
A Stream delivers a sequence of asynchronous events, such as real-time database updates, location changes or WebSocket messages. You listen with listen() or await for, and transform with map, where and debounce from packages. Single-subscription streams allow one listener; broadcast streams allow many. In Flutter, StreamBuilder rebuilds UI on each event. Always cancel subscriptions in dispose to prevent leaks.
27

What is the difference between StatelessWidget and StatefulWidget?

Save to ▾
Show answer
A StatelessWidget has no mutable state; its build output depends only on constructor parameters and inherited data. A StatefulWidget creates a State object that persists across rebuilds and holds mutable data; calling setState marks it dirty so build runs again. Use stateful widgets for local UI state like animations, text controllers or toggles, and lift shared or business state into a state management solution.
28

What are keys in Flutter and when do you need them?

Save to ▾
Show answer
Keys help Flutter match widgets to existing elements and state when the widget list changes. Without keys, Flutter matches by type and position, so reordering or removing items in a list of stateful widgets can attach state to the wrong item. Use ValueKey with a unique ID for list items, UniqueKey to force recreation, and GlobalKey rarely, for accessing state across the tree such as a Form.
29

What is responsive design in mobile apps and how do you handle different screen sizes?

Save to ▾
Show answer
Responsive design means layouts adapt to screen width, orientation, foldables and tablets instead of using fixed sizes. Use flexible layouts, percentages or flex, and breakpoints for compact, medium and expanded widths. In Flutter use LayoutBuilder and MediaQuery.sizeOf; in React Native use flexbox and useWindowDimensions. Respect safe areas for notches and system bars, support text scaling for accessibility, and test on small budget phones.
30

What are isolates and when should you use them?

Save to ▾
Show answer
Isolates are Dart's units of concurrency, each with its own memory and event loop, communicating by messages. Heavy CPU work like parsing a large JSON file, image processing or encryption on the main isolate causes jank. Use Isolate.run or compute to run such work in a separate isolate and return the result. For network or file I/O, async-await is enough because that work is not CPU-bound.
31

Tell me about a time you had to learn a new framework under a deadline.

Save to ▾
Show answer
Situation: my team decided to build a client app in Flutter, and I had only React Native experience. Task: deliver the login and dashboard screens in three weeks. Action: I completed the official codelabs in the first week, mapped concepts I knew like components and hooks to widgets and state, and asked for early code reviews. Result: the screens shipped on time, and I later wrote a short guide for other React Native developers moving to Flutter.
32

How do you explain your architecture choices to a new team member?

Save to ▾
Show answer
I start with the problem each layer solves, using one real feature from our app to trace a user tap through widget, state notifier, repository and API, and back to the UI. I share a short diagram and the team's architecture document, then give them a small task touching every layer. I review their first pull request closely and invite questions so they understand the reasons, not only the rules.
33

The API responses are slow on mobile networks. What can the mobile team ask the backend team to change?

Save to ▾
Show answer
Ask for pagination with sensible page sizes, gzip or brotli compression, removing unused fields or providing screen-specific endpoints, and aggregated endpoints to avoid multiple sequential calls. Request proper cache headers and ETags so the client can reuse responses. Ask for resized images through a CDN and HTTP/2 or HTTP/3 support. Share measured timings from real devices to justify the requests.
34

Users lose their place in a long Flutter form when they get a phone call and the app is killed in background. How do you fix it?

Save to ▾
Show answer
The operating system may kill the app process in background, and in-memory state is lost. Save draft form data to local storage, such as shared_preferences, Hive or a SQLite table, as the user types with a short debounce, and restore it when the screen opens. Flutter's RestorationMixin and restorationScopeId can restore simple UI state like scroll position and text fields. Clear the draft after successful submission.
35

Your Flutter codebase mixes setState, Provider and get_it randomly across screens. How would you clean it up?

Save to ▾
Show answer
Agree on one approach with the team, for example Riverpod for app state and setState only for local widget state, and document examples. Migrate gradually, starting with the most buggy or most changed features, and write tests before moving logic. Add lint rules and pull request checklists so new code follows the standard. Avoid a big-bang rewrite that blocks feature work for weeks.
36

What is clean architecture in a Flutter app?

Save to ▾
Show answer
Clean architecture separates code into presentation (widgets and state notifiers or blocs), domain (entities, use cases and repository interfaces) and data (repository implementations, API clients, local storage and DTOs). Dependencies point inward, so the domain knows nothing about Flutter or HTTP. It improves testability and lets you swap data sources. For small apps it may be heavy, so many teams keep presentation and data layers and add domain only when logic grows.
37

How do Future, async and await work in Dart?

Save to ▾
Show answer
A Future represents a value that will be available later, like an HTTP response. Marking a function async lets it use await, which pauses that function until the Future completes without blocking the event loop. Errors are handled with try-catch. Dart runs on a single-threaded event loop per isolate, so awaiting network calls keeps the UI responsive. Use Future.wait to run independent futures concurrently.
38

Write a React Native function that uploads an image with progress using Expo FileSystem.

Save to ▾
Show answer
import * as FileSystem from 'expo-file-system'; export async function uploadImage(uri, token, onProgress) { const task = FileSystem.createUploadTask( 'https://api.example.com/upload', uri, { httpMethod: 'POST', uploadType: FileSystem.FileSystemUploadType.MULTIPART, fieldName: 'file', headers: { Authorization: `Bearer ${token}` }, }, ({ totalBytesSent, totalBytesExpectedToSend }) => onProgress(totalBytesSent / totalBytesExpectedToSend) ); const res = await task.uploadAsync(); if (res.status !== 200) throw new Error('Upload failed'); return JSON.parse(res.body); }
39

What is the difference between a BaaS and a custom backend for a mobile startup?

Save to ▾
Show answer
A Backend-as-a-Service gives ready authentication, database, storage and functions, so a small team launches faster without managing servers, and costs stay low at small scale. A custom backend gives full control over business logic, data model, integrations and cost at large scale, but needs more engineering effort. Many startups begin with BaaS for the MVP and gradually move complex or expensive parts to custom services.
40

Two users editing the same shared list offline create conflicting changes. How do you resolve conflicts?

Save to ▾
Show answer
Choose a strategy based on data. Last write wins, using server timestamps, is simple but can lose edits. Field-level merging keeps non-conflicting changes. For lists, treat each item as a separate record with its own version so edits rarely clash. For collaborative content, CRDT-based approaches merge automatically. Always let the server decide final order, and show users when their change was overwritten.

Frequently asked questions

What does a Flutter Developer do?

A Flutter developer builds cross-platform apps in Dart, owning features from UI to state management and API integration. They use Riverpod or Bloc, go_router, Firebase and local storage, write widget and unit tests, and handle platform setup for permissions and push notifications. Flutter roles are common in Indian startups, fintech and service companies that want one team to deliver Android and iOS apps.

Which skills are tested in a Flutter Developer interview?

Expect questions on Dart and widgets, Riverpod or Bloc, go_router navigation, Firebase integration, Widget testing. Most rounds also ask you to explain a project and solve one practical problem live.

How do I prepare for a Flutter Developer interview?

Revise Dart and Flutter Basics, Flutter State Management and Architecture, Mobile Backend, APIs and Firebase first using the topic sheets, practise the questions on this page aloud, then do one full mock interview to practise timing.