interviewDeck

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

Loading your questions…

All Questions

Filters & tools

Flutter Interview Questions and Answers

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

What is Flutter, and what are its advantages?

Flutter is Google's open-source UI toolkit for building natively compiled applications for Android, iOS, Web, Windows, macOS, and Linux from a single codebase using the Dart programming language.

Key advantages include Hot Reload, a single codebase for multiple platforms, high performance through native compilation, a rich widget library (Material & Cupertino), expressive UI, and excellent developer productivity.

What is the difference between StatelessWidget and StatefulWidget?

A StatelessWidget is immutable. Once built, its UI does not change unless its parent rebuilds it.

A StatefulWidget has mutable state managed by a separate State object. Calling setState() updates the state and rebuilds the widget.

import 'package:flutter/material.dart';

class CounterPage extends StatefulWidget {
  const CounterPage({super.key});

  @override
  State<CounterPage> createState() => _CounterPageState();
}

class _CounterPageState extends State<CounterPage> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Counter')),
      body: Center(
        child: Text('Count: $count'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            count++;
          });
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

What is Hot Reload, and how is it different from Hot Restart?

Hot Reload injects updated source code into the running application while preserving the current state. It is mainly used during UI development.

Hot Restart restarts the Dart application, resets all state, and rebuilds the widget tree from scratch.

What is the Widget Tree in Flutter?

Everything in Flutter is a Widget. The Widget Tree is a hierarchical structure that describes the application's UI. Parent widgets contain child widgets, and Flutter rebuilds only the affected parts of the tree when state changes, making rendering efficient.

Explain the lifecycle of a StatefulWidget.

The main lifecycle methods are: createState()initState()didChangeDependencies()build(). During updates, Flutter may call didUpdateWidget() and setState() triggers another build(). Before removal, dispose() is called to clean up resources like controllers, streams, and animations.

What are the main state management approaches in Flutter, and how do you choose between them?

Flutter offers several state management approaches: setState (local widget state), InheritedWidget/Provider (dependency injection + simple reactive state), Riverpod (compile-safe, testable Provider successor), BLoC/Cubit (event-driven, stream-based, great separation of business logic), and GetX/MobX (reactive, less boilerplate).

The choice depends on app size and team preference: small apps can use setState/Provider; large apps with complex business logic benefit from BLoC or Riverpod for testability and separation of concerns.

What are Keys in Flutter, and when do you need them?

Keys help Flutter identify widgets across rebuilds when the widget tree is reordered or widgets of the same type are inserted/removed (e.g., items in a list). Without a key, Flutter may reuse the wrong widget's state.

ValueKey/ObjectKey distinguish siblings by value/identity, while GlobalKey uniquely identifies a widget across the entire app and allows accessing its state or context from outside (e.g., GlobalKey<FormState>).

ListView(children: [
  for (final item in items)
    ListTile(key: ValueKey(item.id), title: Text(item.name)),
]);

What is InheritedWidget, and how does Provider build on it?

InheritedWidget lets data be efficiently passed down the widget tree without manual prop drilling. Descendants call context.dependOnInheritedWidgetOfExactType to read it and automatically rebuild when it notifies of a change.

Provider wraps InheritedWidget with a simpler API (Provider, ChangeNotifierProvider, context.watch/read/select) and adds lifecycle management like automatic disposal.

Explain the BLoC pattern and the difference between Bloc and Cubit.

BLoC (Business Logic Component) separates business logic from UI using Streams: the UI dispatches Events, the Bloc processes them and emits States, and widgets rebuild via BlocBuilder/BlocListener.

Cubit is a simplified Bloc without the event layer—you call methods directly that emit new states. Cubit is easier to learn; Bloc gives more traceability (every state change originates from an explicit event), useful for complex or auditable flows.

class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);
  void increment() => emit(state + 1);
}

What is the difference between Navigator 1.0 and Navigator 2.0 (Router API)?

Navigator 1.0 is imperative: you call Navigator.push/pop to manage an internal stack of routes.

Navigator 2.0 (the Router API) is declarative: the navigation stack is derived from application state, enabling deep linking, web URL sync, and browser back-button support. Packages like go_router and auto_route wrap Navigator 2.0's complexity with a simpler declarative API.

What is the difference between FutureBuilder and StreamBuilder?

FutureBuilder rebuilds once when a single Future completes (or errors)—ideal for one-off async operations like an API call.

StreamBuilder rebuilds every time a Stream emits a new value—ideal for continuous data like real-time updates, sockets, or Firebase snapshots. Both expose AsyncSnapshot with connectionState, data, and error.

StreamBuilder<int>(
  stream: counterStream,
  builder: (context, snapshot) {
    if (snapshot.hasError) return Text('Error');
    if (!snapshot.hasData) return CircularProgressIndicator();
    return Text('${snapshot.data}');
  },
);

How do you optimize performance in a Flutter application?

Key techniques: use const constructors so widgets skip unnecessary rebuilds; use ListView.builder/SliverList for lazy rendering of long lists; wrap expensive repaint areas with RepaintBoundary; avoid rebuilding large widget subtrees by narrowing setState scope or using Selector/context.select; use DevTools' performance/timeline view to find jank; and move heavy CPU work off the UI thread with compute()/isolates.

Why should widgets use const constructors whenever possible?

A const widget is built once at compile time and Flutter can skip rebuilding/reallocating it when its parent rebuilds, since the framework knows the instance can't change. This reduces widget tree diffing work and improves performance, especially for static UI like icons, text, or padding.

const Text('Hello');
const SizedBox(height: 16);

What are Flutter's three build modes, and how do they differ?

Debug mode enables hot reload, assertions, and debugging tools but is slower (JIT compiled). Profile mode is close to release performance but keeps some tracing for performance analysis. Release mode is fully AOT-compiled, optimized, and stripped of debug information—used for production builds.

What are Platform Channels, and when would you use them?

Platform Channels (MethodChannel, EventChannel, BasicMessageChannel) let Dart code communicate with native Android (Kotlin/Java) or iOS (Swift/Objective-C) code. They're used when a plugin doesn't exist for a native API you need (e.g., a custom SDK), sending messages asynchronously across the bridge.

static const platform = MethodChannel('com.example/battery');
final int level = await platform.invokeMethod('getBatteryLevel');

What are Isolates in Flutter, and when should you use compute()?

Dart is single-threaded per Isolate—each isolate has its own memory and event loop, so isolates don't share state and communicate via message passing. Flutter's UI runs on the main isolate; heavy CPU-bound work (JSON parsing large payloads, image processing) can block it and cause jank.

compute() spawns a new isolate to run a top-level function off the UI thread and returns the result via a Future, keeping the UI responsive.

final result = await compute(parseJson, responseBody);

What is the difference between implicit and explicit animations in Flutter?

Implicit animations (e.g., AnimatedContainer, AnimatedOpacity) automatically animate between old and new values whenever their properties change—no controller required.

Explicit animations use an AnimationController with a Tween and AnimatedBuilder/AnimatedWidget, giving full control over duration, curve, direction, and repeats—used for complex or coordinated animations.

Explain the difference between Expanded and Flexible, and how Stack differs from Row/Column.

Expanded forces a child to fill available space along the main axis (equivalent to Flexible with FlexFit.tight). Flexible lets a child take up to the available space but allows it to be smaller (FlexFit.loose).

Row/Column lay children out linearly (horizontally/vertically) without overlap. Stack overlays children on top of each other, positioned with Positioned or alignment—used for things like badges, overlays, or custom layered UI.

How do you build a responsive UI in Flutter?

Use MediaQuery.of(context).size to read screen dimensions, LayoutBuilder to adapt UI based on the parent's constraints (not just the screen), and OrientationBuilder for portrait/landscape changes. Combine with breakpoints to switch between mobile/tablet/desktop layouts, and use flexible widgets (Expanded, Wrap, FittedBox) instead of fixed sizes.

How does theming work in Flutter?

ThemeData defines app-wide colors, typography, and component styles, applied via MaterialApp(theme: ...). Widgets read it with Theme.of(context). Flutter also supports light/dark themes (theme and darkTheme with themeMode) and custom theme extensions via ThemeExtension for app-specific tokens.

How do you build and validate a form in Flutter?

Wrap fields in a Form widget with a GlobalKey<FormState>. Each TextFormField takes a validator that returns an error string or null. Calling formKey.currentState!.validate() runs all validators and returns whether the form is valid; save() triggers each field's onSaved callback.

final formKey = GlobalKey<FormState>();

Form(
  key: formKey,
  child: TextFormField(
    validator: (v) => (v == null || v.isEmpty) ? 'Required' : null,
  ),
);

if (formKey.currentState!.validate()) { /* submit */ }

How do you make network calls in Flutter, and how does Dio differ from the http package?

The http package is a lightweight, low-level client for basic GET/POST requests with manual JSON decoding. Dio is a more feature-rich HTTP client offering interceptors (for auth tokens/logging), request/response transformation, timeouts, file upload/download with progress, and easier error handling via DioException.

final dio = Dio();
dio.interceptors.add(InterceptorsWrapper(
  onRequest: (options, handler) {
    options.headers['Authorization'] = 'Bearer $token';
    handler.next(options);
  },
));

What are the common options for local data persistence in Flutter?

shared_preferences stores simple key-value pairs (settings, flags)—backed by NSUserDefaults/SharedPreferences natively. Hive is a fast, pure-Dart NoSQL key-value/object database, good for structured local data without native SQL. sqflite gives full SQL/relational storage for complex queries. Drift (formerly Moor) adds a type-safe query builder on top of sqflite.

How do you observe and respond to the app lifecycle in Flutter?

Mix in WidgetsBindingObserver and override didChangeAppLifecycleState to react to resumed, inactive, paused, and detached states—useful for pausing video/audio, refreshing data on resume, or saving state before backgrounding.

class _MyState extends State<MyWidget> with WidgetsBindingObserver {
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.paused) { /* save state */ }
  }
}

What types of testing does Flutter support?

Unit tests verify pure logic (functions, classes) using the test package. Widget tests (flutter_test) pump a widget into a test environment and verify UI/behavior without a real device, using WidgetTester to tap, scroll, and assert. Integration tests (integration_test package) run the full app on a real device/emulator to test end-to-end flows.

testWidgets('counter increments', (tester) async {
  await tester.pumpWidget(MyApp());
  await tester.tap(find.byIcon(Icons.add));
  await tester.pump();
  expect(find.text('1'), findsOneWidget);
});

What are Slivers, and why would you use CustomScrollView?

Slivers are scrollable areas that can be composed together inside a CustomScrollView, allowing complex scroll effects—like a collapsing app bar (SliverAppBar), mixed grids and lists, or sticky headers—that a plain ListView can't achieve, since all slivers share one scroll position.

What is the difference between MaterialApp and CupertinoApp?

MaterialApp configures an app to use Google's Material Design widgets, theming, and navigation conventions (Android-style). CupertinoApp configures an app to use iOS-style widgets and theming. Both provide a Navigator and a widget tree root, and you can mix Cupertino widgets inside a MaterialApp for a more iOS-native feel on iOS.

What is Scaffold, and what structural pieces does it provide?

Scaffold implements the basic Material Design visual layout: it provides slots for an appBar, body, floatingActionButton, drawer, bottomNavigationBar, and SnackBar display, wiring them together with correct z-ordering and insets automatically.

What is BuildContext, and what does it actually represent?

BuildContext is a handle to the location of a widget in the widget tree—concretely, it is the Element associated with that widget. It's used to look up inherited data (Theme.of(context)), find ancestor widgets, and navigate. It is not the widget itself, and using a stale context (e.g., after the widget is disposed) throws errors.

Explain the relationship between Widget, Element, and RenderObject.

Widget is an immutable configuration/blueprint. Element is the mutable instantiation of a widget in the tree—it persists across rebuilds and manages the widget's lifecycle, diffing old vs new widgets to decide whether to update, replace, or reuse a RenderObject. RenderObject handles the actual layout, painting, and hit-testing.

This three-tree architecture is why Flutter can be fast: Elements let cheap widget rebuilds be reconciled without necessarily re-laying-out or repainting.

What actually happens when you call setState()?

setState() marks the associated Element as 'dirty' and schedules a frame via markNeedsBuild(). On the next frame, Flutter calls build() again for that widget (and only that subtree, not the whole app), diffs the returned widget tree against the previous one, and updates only the RenderObjects that actually changed.

What is InheritedModel, and how does it improve on InheritedWidget?

InheritedModel extends InheritedWidget to let dependents subscribe to only a specific aspect of the data rather than the whole object, so a widget only rebuilds when the particular aspect it cares about changes—reducing unnecessary rebuilds compared to a plain InheritedWidget, where every dependent rebuilds on any change.

What is ValueNotifier and ValueListenableBuilder, and when would you use them over setState?

ValueNotifier<T> is a lightweight observable holding a single value that notifies listeners when it changes. ValueListenableBuilder subscribes to it and rebuilds only its own small subtree—avoiding a full-widget setState() rebuild when only one small part of the UI depends on that value.

final counter = ValueNotifier<int>(0);

ValueListenableBuilder<int>(
  valueListenable: counter,
  builder: (context, value, child) => Text('$value'),
);

What is CustomPainter, and when would you use it?

CustomPainter lets you draw directly on a Canvas (lines, arcs, paths, text, images) for graphics that standard widgets can't express, like custom charts, gauges, or signature pads. It's used with a CustomPaint widget, and shouldRepaint() controls whether it repaints on rebuild.

class LinePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawLine(Offset.zero, Offset(size.width, size.height), Paint()..color = Colors.blue);
  }
  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

What information does MediaQuery expose besides screen size?

Beyond size, MediaQuery.of(context) exposes padding (notches/status bar), viewInsets (space taken by the on-screen keyboard), devicePixelRatio, textScaleFactor, and platformBrightness (system light/dark mode).

What does the SafeArea widget do?

SafeArea automatically insets its child to avoid operating-system UI intrusions like the notch, status bar, and home indicator, using the device's MediaQuery padding so content isn't hidden behind system chrome.

What is Overlay/OverlayEntry used for?

Overlay manages a stack of entries painted above the rest of the app, independent of the normal widget tree layout—used for things like tooltips, custom dropdown menus, and in-app notifications that need to float above everything else regardless of where they were triggered from.

What is the difference between GestureDetector and InkWell?

GestureDetector detects taps, drags, and other gestures on any widget without providing visual feedback. InkWell does the same but adds a Material ripple effect on tap—it must be a descendant of a Material widget to render correctly.

What is a Hero animation in Flutter?

A Hero widget animates a shared element (like an image) smoothly flying from one screen to another during navigation, as long as both screens use a Hero with the same tag. Flutter automatically interpolates position and size during the page transition.

Hero(tag: 'profile-pic', child: Image.network(url));

How does the Dismissible widget work for swipe-to-delete?

Dismissible wraps a list item and detects horizontal (or vertical) swipe gestures, animating the item off-screen and calling onDismissed once the swipe passes a threshold. It requires a unique key per item so Flutter knows which item was removed.

Dismissible(
  key: ValueKey(item.id),
  onDismissed: (direction) => removeItem(item),
  child: ListTile(title: Text(item.name)),
);

What is the difference between Opacity, Visibility, and Offstage widgets?

Opacity keeps the widget in the layout and just fades it, but is relatively expensive since it composites a separate layer. Visibility can fully remove the child from the tree (avoiding build/layout/paint cost) while optionally keeping its size reserved via maintainSize. Offstage lays the child out (so its size is known) but doesn't paint or hit-test it.

How do you implement localization (i18n) in a Flutter app?

Add the flutter_localizations and intl packages, define .arb files per locale with translated strings, and configure localizationsDelegates and supportedLocales on MaterialApp. The generated AppLocalizations class then exposes translated strings via AppLocalizations.of(context)!.someKey.

What Firebase services are commonly integrated into Flutter apps?

Common ones: Firebase Auth (email/social login), Cloud Firestore/Realtime Database (NoSQL data sync), Cloud Messaging (FCM) (push notifications), Crashlytics (crash reporting), Remote Config (feature flags), and Analytics. All are wired up via the firebase_core plugin plus per-service packages.

How does deep linking work in Flutter?

Deep linking lets a URL (web link, custom scheme, or App/Universal Link) open the app directly to a specific screen. It's configured natively (Android intent-filter, iOS Associated Domains) and handled in Dart via Navigator 2.0/go_router parsing the incoming URI into a route, or a package like uni_links/app_links for legacy Navigator 1.0 apps.

What are Flutter flavors, and why are they used?

Flavors (build variants) let you build different versions of the same app—e.g. dev, staging, production—each with a different API endpoint, app icon, or bundle ID, from one codebase. Configured via native flavor setup (Gradle product flavors on Android, Xcode schemes on iOS) combined with --flavor and --dart-define flags.

flutter run --flavor dev -t lib/main_dev.dart

How do you reduce the size of a release Flutter app?

Use flutter build --split-per-abi to avoid shipping all CPU architectures in one APK, enable icon tree-shaking (automatic for Material Icons), remove unused assets/packages, use --obfuscate --split-debug-info to shrink and protect release builds, and analyze size with flutter build apk --analyze-size.

How do you globally catch errors in a Flutter app (including async errors)?

FlutterError.onError catches framework/build errors, but uncaught errors in async code (outside the current build) need runZonedGuarded to wrap runApp(), catching anything that escapes a Future/Stream. Together, this lets you funnel all errors to a crash reporter like Crashlytics or Sentry.

void main() {
  runZonedGuarded(() {
    FlutterError.onError = (details) => Crashlytics.recordFlutterError(details);
    runApp(MyApp());
  }, (error, stack) => Crashlytics.recordError(error, stack));
}

How do you make a Flutter app accessible?

Most Material/Cupertino widgets already expose reasonable semantics automatically. For custom widgets, wrap them in a Semantics widget with a label, mark decorative images as excludeSemantics, ensure sufficient touch target size (48x48), color contrast, and test with the device's screen reader (TalkBack/VoiceOver) or Flutter's Accessibility Inspector in DevTools.

What is ScrollPhysics, and what's the difference between BouncingScrollPhysics and ClampingScrollPhysics?

ScrollPhysics defines how a scrollable responds to user input at the boundaries and during flings. BouncingScrollPhysics (default on iOS) allows overscrolling with a bounce-back effect. ClampingScrollPhysics (default on Android) stops hard at the edges with a glow/stretch overscroll indicator instead.

What are the differences between showDialog, showModalBottomSheet, and SnackBar?

showDialog shows a modal Material dialog that blocks interaction until dismissed. showModalBottomSheet slides a panel up from the bottom, also modal, good for actions/menus. SnackBar (via ScaffoldMessenger) shows a brief, non-blocking message at the bottom that auto-dismisses.

How is Dependency Injection typically done in Flutter?

Beyond widget-tree-based DI (Provider/Riverpod), a service locator like get_it lets you register singletons/factories for services (API clients, repositories) once at startup and resolve them anywhere, including outside the widget tree (e.g. in a background isolate). injectable adds code generation on top of get_it to reduce manual registration boilerplate.

final getIt = GetIt.instance;
getIt.registerLazySingleton<ApiClient>(() => ApiClient());

final api = getIt<ApiClient>();

How does Clean Architecture typically map onto a Flutter app?

Flutter apps commonly split into layers: Presentation (widgets + state management/ViewModels), Domain (use cases and entities, framework-agnostic pure Dart), and Data (repositories, API/DB data sources, DTOs). Dependencies point inward—the domain layer knows nothing about Flutter or the data layer—making business logic independently testable.

What are the main provider types in Riverpod, and when do you use each?

Provider exposes a read-only computed value. StateProvider exposes simple mutable state. FutureProvider/StreamProvider expose async data with built-in loading/error handling. NotifierProvider/AsyncNotifierProvider expose more complex state with custom methods, similar to a Bloc/Cubit but compile-safe and testable without BuildContext.

What is the Freezed package used for?

Freezed generates immutable data classes with copyWith, value equality (==/hashCode), toString, and union/sealed-class-style state modeling from a compact annotated class—removing the boilerplate of writing all of that by hand for every model or state class.

@freezed
class UserState with _$UserState {
  const factory UserState({required String name, required int age}) = _UserState;
}

How do you handle JSON serialization in a Flutter app?

For small apps, write manual fromJson/toJson factory methods. For larger apps, json_serializable (with build_runner) generates this boilerplate from annotated model classes, reducing manual errors and keeping models in sync with the API contract.

What problem does the Equatable package solve?

By default, Dart objects use identity equality (== compares references), so two objects with identical field values are considered different. Equatable lets a class override equality by listing its props, so value-based comparisons work automatically—critical for Bloc states, since BlocBuilder skips rebuilds when the new state equals the old one.

class UserState extends Equatable {
  final String name;
  const UserState(this.name);
  @override
  List<Object?> get props => [name];
}

What is flutter_hooks, and what problem does it solve?

flutter_hooks (inspired by React Hooks) lets you reuse stateful logic (like an AnimationController or TextEditingController with automatic disposal) inside a HookWidget without needing a StatefulWidget's boilerplate. Hooks like useState and useEffect manage lifecycle automatically.

What do runApp() and WidgetsFlutterBinding.ensureInitialized() do?

runApp() takes a widget and attaches it as the root of the widget tree, inflating it and attaching it to the screen. WidgetsFlutterBinding.ensureInitialized() initializes the binding between the Flutter engine and the framework and must be called first if you need to use platform channels or async work (e.g. Firebase.initializeApp()) before runApp().

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

How do you set up crash reporting in a production Flutter app?

Integrate Firebase Crashlytics or Sentry: initialize the SDK at startup, route FlutterError.onError and PlatformDispatcher.instance.onError (or runZonedGuarded) to the SDK's error recorder, and optionally attach custom logs/breadcrumbs and user identifiers for better triage.

How do you add and use custom fonts in Flutter?

Add the font files under an assets folder and declare them under the fonts section of pubspec.yaml with a family name and font weight/style variants, then reference the family in a TextStyle(fontFamily: ...) or set it globally via ThemeData(fontFamily: ...).

flutter:
  fonts:
    - family: Poppins
      fonts:
        - asset: fonts/Poppins-Regular.ttf
        - asset: fonts/Poppins-Bold.ttf
          weight: 700

How do you configure a native splash screen in Flutter?

Flutter shows a native splash screen (defined in native Android/iOS launch resources) before the Dart engine even starts, since Dart isn't running yet. The flutter_native_splash package generates the correct native assets/configuration from a single image and color defined in pubspec.yaml, keeping Android/iOS in sync.

What is the difference between a Dart package and a Flutter plugin?

A Dart package contains pure Dart code with no native dependency (e.g. a utility/formatting library) and works on any platform including server-side Dart. A Flutter plugin additionally contains platform-specific native code (Kotlin/Swift) and uses platform channels to bridge Dart calls to native APIs (e.g. camera, GPS).

What is dart:ffi, and when would you use it?

dart:ffi (Foreign Function Interface) lets Dart call native C libraries directly, without going through the platform-channel message bridge—useful for CPU-intensive native code (image/audio processing, existing C/C++/Rust libraries) where the overhead of serializing platform channel messages would be too costly.

How do you embed a web page inside a Flutter app?

The webview_flutter plugin embeds a native platform WebView (Android WebView/WKWebView on iOS) inside the widget tree via a WebViewController, letting you load URLs/HTML, inject JavaScript, and listen for navigation events—commonly used for payment gateways or legacy web content.

How do you request runtime permissions (camera, location) in Flutter?

The permission_handler package provides a cross-platform API to check and request runtime permissions, wrapping Android's runtime permission model and iOS's Info.plist-declared usage descriptions. Always check the current status first and gracefully handle 'denied' and 'permanentlyDenied' (which requires sending the user to app settings).

final status = await Permission.camera.request();
if (status.isGranted) { /* open camera */ }

Describe Flutter's rendering pipeline from build to pixels on screen.

Each frame goes through: Build (widgets describe UI by calling build()), Layout (each RenderObject is given constraints by its parent and reports back a size—a single-pass, parent-to-child-to-parent walk), Paint (RenderObjects record painting instructions into layers), and Compositing/Rasterization (layers are composited and rasterized by the engine, using Skia or Impeller, into actual pixels on the GPU).

What is Impeller, and how does it differ from Skia in Flutter?

Skia was Flutter's original general-purpose 2D rendering engine, which compiled shaders at runtime, sometimes causing visible jank ('shader compilation jank') the first time a new visual effect appeared. Impeller is Flutter's newer rendering engine that precompiles shaders ahead of time, eliminating that jank, and is now the default on iOS (and rolling out on Android).

What are the three main architectural layers of Flutter?

Framework (Dart): the widgets, rendering, and animation libraries you write against. Engine (C++): implements Flutter's core—Skia/Impeller graphics, Dart runtime, text layout—and exposes it to the framework via dart:ui. Embedder (platform-specific): the entry point per platform (Android/iOS/desktop) that provides the surface, input, and native plugin access.

What can you do with Flutter DevTools?

DevTools offers: the Widget Inspector (visualize/select widget tree, see layout constraints), the Performance/Timeline view (find jank, see frame build/raster times), the Memory view (track allocations, find leaks), the Network view (inspect HTTP calls), and Logging (structured log stream).

What are common causes of memory leaks in a Flutter app?

Common causes: forgetting to call dispose() on AnimationController, TextEditingController, or ScrollController; not cancelling StreamSubscriptions; holding a reference to a disposed BuildContext; and registering listeners (e.g. WidgetsBindingObserver) without removing them in dispose.

What are the downsides of overusing GlobalKey?

GlobalKeys are relatively expensive (they require a global registry lookup), can only be used by one widget at a time in the tree (duplicating one throws an assertion error), and break encapsulation by letting external code reach into a widget's internal state—making the widget harder to reason about and reuse.

How do you set up CI/CD for a Flutter app?

A typical pipeline (GitHub Actions, Codemagic, Bitrise) runs flutter analyze and flutter test on every PR, then on merge/tag builds signed release artifacts (flutter build appbundle/ipa) and uses Fastlane to automate versioning, code signing, and uploading to the Play Store/TestFlight.

What does flutter analyze do, and how do custom lint rules help?

flutter analyze statically checks Dart/Flutter code for errors, type issues, and lint-rule violations defined in analysis_options.yaml. Enabling stricter rule sets (like flutter_lints or very_good_analysis) catches issues like missing const constructors, unused imports, or unsafe null checks before code review.

How do you mock dependencies when unit testing Flutter/Dart code?

mocktail (null-safety friendly, no code generation) or mockito (with build_runner code generation) let you create fake implementations of interfaces/abstract classes, stub method return values, and verify interactions—so tests for a ViewModel/Bloc can run without hitting a real API or database.

class MockApi extends Mock implements ApiClient {}

test('returns users', () async {
  final api = MockApi();
  when(() => api.fetchUsers()).thenAnswer((_) async => [User('John')]);
  expect(await api.fetchUsers(), [User('John')]);
});

What are Golden Tests in Flutter?

Golden tests render a widget and compare the output pixel-for-pixel against a previously saved reference image ('golden'). They catch unintended visual regressions that logic-only widget tests would miss, at the cost of being sensitive to font/platform rendering differences across machines.

testWidgets('button matches golden', (tester) async {
  await tester.pumpWidget(MyButton());
  await expectLater(find.byType(MyButton), matchesGoldenFile('button.png'));
});

How does the integration_test package differ from widget tests, and how do you run it?

Widget tests run in a simulated environment (no real device/engine rendering). integration_test runs the full app on a real device or emulator, driving it exactly like a user (tap, scroll, wait for network), which is essential for verifying true end-to-end flows like login, checkout, or native plugin behavior.

flutter test integration_test/app_test.dart

How do TabBar and PageView work together?

A TabController synchronizes a TabBar (the tab headers) with a TabBarView or PageView (the swipeable content)—tapping a tab animates the view to that page, and swiping the view updates the selected tab, since both listen to the same controller.

How do you implement drag-and-drop in Flutter?

Draggable<T> wraps a widget that can be dragged, carrying a typed data payload and a feedback widget shown while dragging. DragTarget<T> wraps a drop zone, exposing onWillAccept/onAccept callbacks that fire when a matching Draggable is dropped on it.

How do you implement pull-to-refresh in Flutter?

Wrap a scrollable (ListView/CustomScrollView) in a RefreshIndicator, providing an onRefresh callback that returns a Future. The indicator shows a spinner while the Future is pending and hides automatically once it completes.

RefreshIndicator(
  onRefresh: () => viewModel.loadData(),
  child: ListView(children: items),
);

What problem does NestedScrollView solve?

NestedScrollView coordinates an outer scrollable (e.g. a collapsing SliverAppBar/header) with an inner scrollable (e.g. a TabBarView of lists), so scrolling the inner content also drives the outer header's collapse—something that plain nested ListViews can't do correctly since they'd compete for scroll gestures.

What are ClipRRect, ClipOval, and ClipPath used for?

These widgets clip a child to a shape: ClipRRect clips to rounded rectangle corners, ClipOval clips to an ellipse/circle (common for circular avatars), and ClipPath clips to an arbitrary custom-drawn path via a CustomClipper—useful for non-rectangular banners or shaped containers.

What do FittedBox and AspectRatio do?

FittedBox scales and positions its child to fit the available space according to a BoxFit (contain, cover, fill...), similar to how CSS object-fit works for images. AspectRatio tries to size its child to a given width/height ratio within the constraints given by its parent.

What does context.select do in Provider, and why use it over context.watch?

context.watch rebuilds a widget whenever any part of the provided object changes. context.select lets you subscribe to only a specific field/computed value, so the widget rebuilds only when that particular piece of data actually changes—avoiding unnecessary rebuilds for large models with many fields.

final name = context.select<UserModel, String>((m) => m.name);

What is the difference between BlocBuilder, BlocListener, and BlocConsumer?

BlocBuilder rebuilds UI in response to new states. BlocListener runs one-off side effects (navigation, showing a SnackBar) without rebuilding UI. BlocConsumer combines both, exposing a builder and a listener in one widget when you need both behaviors for the same Bloc.

What are the key technical differences between Riverpod and Provider?

Riverpod providers are declared as global final variables outside the widget tree, so they don't need a BuildContext to read/test and can't throw ProviderNotFoundException at runtime (a common Provider footgun). Riverpod also supports provider composition/overriding (great for testing) and code-generation via riverpod_generator, whereas Provider relies purely on InheritedWidget lookups through context.

What is NotificationListener, and how is it used with scroll events?

NotificationListener<T> lets an ancestor widget listen for notifications bubbling up from descendants without a direct reference to them—commonly used with ScrollNotification to detect scroll position/direction (e.g. to hide an AppBar on scroll-down) without attaching a ScrollController.

NotificationListener<ScrollNotification>(
  onNotification: (notification) {
    if (notification is ScrollEndNotification) loadMore();
    return false;
  },
  child: ListView(...),
);

How do you lock screen orientation or control system UI overlays in Flutter?

SystemChrome.setPreferredOrientations restricts the app to specific orientations (e.g. portrait-only). SystemChrome.setEnabledSystemUIMode controls status bar/navigation bar visibility (e.g. immersive full-screen mode), and SystemChrome.setSystemUIOverlayStyle controls status bar icon brightness/color.

SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);

How do you configure app icons across platforms in Flutter?

The flutter_launcher_icons package generates all required icon sizes for Android (adaptive icons) and iOS from a single source image, based on configuration in pubspec.yaml, avoiding manually exporting dozens of resolution variants by hand.

What is the difference between flutter pub get and flutter pub upgrade, and what is pubspec.lock for?

pub get installs dependencies matching the version constraints in pubspec.yaml, respecting the existing pubspec.lock if present. pub upgrade ignores the lock file and fetches the newest versions allowed by the constraints, then rewrites the lock file. pubspec.lock pins exact resolved versions so builds are reproducible across machines/CI.

What are the main sections of a pubspec.yaml file?

Key sections: name/description/version (package metadata), environment (SDK version constraints), dependencies (runtime packages), dev_dependencies (test/build-only tools), and flutter (assets, fonts, and the uses-material-design flag).

What does flutter create generate, and what's in each folder?

lib/ holds your Dart source (entry point main.dart). android/ and ios/ hold native platform projects for building/signing. test/ holds unit/widget tests. pubspec.yaml declares dependencies and assets. Optional web/, windows/, macos/, linux/ folders exist if those platforms are enabled.

How do you pass environment-specific configuration into a Flutter build?

--dart-define=KEY=value passes compile-time constants readable via String.fromEnvironment/bool.fromEnvironment, letting a single codebase build against different API URLs or feature flags per environment without hardcoding values or committing secrets to source control. --dart-define-from-file reads many values from a JSON file at once.

flutter build apk --dart-define=API_URL=https://api.prod.com

What does --obfuscate do in a Flutter release build, and why use it?

--obfuscate --split-debug-info=<dir> renames Dart symbols to meaningless names in the compiled release binary, making reverse-engineering harder, while saving a debug symbol map separately so crash stack traces can still be de-obfuscated later using that saved map.

flutter build apk --obfuscate --split-debug-info=build/debug-info

What special considerations apply when targeting Flutter Web?

Flutter Web renders via CanvasKit (Skia compiled to WASM, pixel-perfect but heavier download) or the HTML renderer (lighter, uses DOM/CSS, less consistent visuals)—choose based on whether visual fidelity or initial load size matters more. Also consider SEO limitations (content is canvas-rendered, not real DOM text, hurting crawlability), URL-based routing, and that some plugins (camera, native storage) aren't available or behave differently on web.

What special considerations apply when targeting Flutter Desktop (Windows/macOS/Linux)?

Desktop apps need to handle window resizing/multiple window sizes gracefully (no fixed mobile-sized layouts), support mouse-specific interactions (hover states, right-click context menus, scroll wheel) in addition to touch, and often need keyboard shortcuts/focus traversal that mobile apps don't require.

What does migrating a legacy Flutter codebase to null safety involve?

Migration means auditing every variable/parameter to decide if it can truly be null, adding ? to genuinely nullable types, adding required or default values to non-nullable named parameters, and using late for fields initialized after declaration. The dart migrate tool can suggest an initial pass, but the result usually needs manual review since it tends to over-use ! non-null assertions.

How do you run background tasks in a Flutter app (e.g. periodic sync)?

The workmanager package (Android WorkManager / iOS BGTaskScheduler under the hood) schedules deferred or periodic background work that survives app termination and respects OS battery/scheduling constraints. For simple in-app-lifetime timers, a plain Timer.periodic is enough, but it stops once the app is fully killed.

How do you capture or pick an image in Flutter?

The image_picker plugin provides a simple API to pick an image/video from the gallery or capture one with the camera, returning an XFile. For a fully custom camera UI (live preview, manual capture controls), the lower-level camera plugin gives direct access to the device's camera stream.

final picker = ImagePicker();
final image = await picker.pickImage(source: ImageSource.camera);

How does Flutter cache network images, and when would you use cached_network_image?

Image.network caches decoded images only in memory for the current session via Flutter's ImageCache—it re-downloads on a fresh app launch. cached_network_image adds a persistent disk cache, placeholder/error widgets, and fade-in transitions, avoiding repeated downloads across app restarts.

How do you style part of a text string differently within the same line?

Use RichText (or Text.rich) with a tree of TextSpans, where each span can have its own TextStyle and even its own tap gesture recognizer—letting you mix bold, colored, or tappable segments within a single paragraph.

Text.rich(TextSpan(
  text: 'Hello ',
  children: [TextSpan(text: 'World', style: TextStyle(fontWeight: FontWeight.bold))],
));