Flutter hiring has one specific problem: React Native and React web developers who installed the Flutter CLI and shipped one demo project now list "Flutter" on their profiles. The interview question "Have you used Flutter?" is useless as a filter — it reveals only that they opened the documentation. The useful filter is Dart's type system, Flutter's widget rendering model, and state management patterns. These are not transferable from React or React Native and take real time to learn.
Why Flutter Is Different from React Native
Dart compiles to native ARM64 machine code
A Flutter application ships as a compiled Dart binary. The Dart compiler performs ahead-of-time (AOT) compilation: before the app is distributed, the Dart code is compiled to native ARM64 machine instructions for iOS and Android. At runtime, there is no Dart VM, no JavaScript engine, no interpreter running the application code. The binary executes native machine instructions directly on the device CPU.
This is categorically different from React Native, which runs JavaScript in the Hermes runtime (on Android) or JavaScriptCore (on iOS) and communicates with native platform code through JSI. A developer accustomed to React Native's architecture — debugging Hermes stack traces, understanding the JSI C++ bridge, managing the JS thread vs UI thread distinction — is working with different mental models than Flutter's Dart binary.
Hot reload works by preserving the Element tree
Flutter's hot reload is instant because it replaces widget configurations without discarding the persistent state layer. The Widget→Element→RenderObject tree has three distinct responsibilities. Widgets are immutable configuration objects — they describe what to build. Elements are the persistent, stateful layer — they maintain state, hold lifecycle (mount/unmount), and keep a reference to their corresponding RenderObject. RenderObjects do layout (computing size given parent constraints) and painting (recording drawing commands).
When you save a file during development, hot reload injects the updated code into the Dart VM (which runs in development for hot reload purposes; release builds use AOT), rebuilds the widget tree from the root, and reconciles it against the existing Element tree. Elements whose widget type has not changed update their configuration; their state survives. Only Elements whose widget type changes are recreated. This is why StatefulWidget state persists across hot reloads — the Element holding the State object is reused, not recreated.
A developer who explains hot reload as "it just saves automatically" has not understood the element tree model.
Null safety is enforced at the Dart type system level
Dart's null safety (introduced in Dart 2.12, required in current Flutter) means that variables cannot hold null unless their type is explicitly marked nullable with ?. String name cannot be null; String? name can be. The Dart analyser enforces this at compile time — a function that returns String and has a code path that returns null is a compile error, not a runtime exception.
This is different from TypeScript's null safety, which is erased at runtime. Dart's null safety is enforced by the compiled binary — there is no runtime where a non-nullable String variable holds null. A developer who is accustomed to TypeScript's compile-time-only type system needs to internalise that Dart's type contracts are enforced at execution.
Flutter-Specific Skills That Reveal Real Depth
State management architecture
Flutter does not have a built-in global state solution — it provides setState() (local component state), InheritedWidget (passed-down state), and the framework hooks for reactive updates. Production Flutter apps use a state management package: Bloc (events → state via stream, explicit unidirectional data flow), Riverpod (providers that can depend on other providers, compile-safe, testable), or Provider (lighter than Bloc, ViewModel-style).
The choice matters because it affects how the team reasons about state transitions, how state is tested, and how widgets subscribe to the state they need. A developer who uses setState() for everything in a multi-screen app has not worked with Flutter at production scale. Ask: "How do you manage state across multiple screens in Flutter? Which package do you use and why?"
Widget composition and custom painting
Flutter's rendering model allows widgets to be composed from smaller primitives, and CustomPainter allows drawing arbitrary shapes directly to the canvas. A developer who has built a complex chart, a custom progress indicator, or a gesture-driven animated component using CustomPainter understands how Flutter's rendering pipeline works at a level below the widget layer.
The test: share a custom UI component (a non-standard progress bar, a multi-point gesture recognition interaction) and ask how they'd implement it. A shallow Flutter developer says "I'd find a package." A deep Flutter developer describes the CustomPainter/CustomPainter approach, the coordinate system, and the relationship to the animation controller.
Platform channel integration
Flutter's platform channel mechanism allows Dart code to call native Android (Kotlin/Java) or iOS (Swift/Objective-C) code. The channel is identified by a string name; the Dart side sends a message (a method call or event stream); the native side receives it, executes native code, and returns a result. The serialisation is handled by the codec (default: StandardMessageCodec, which handles primitive types and maps/lists).
A developer who has written a platform channel — not just consumed an existing plugin — has produced native Kotlin and Swift code. This is the capability gap that matters when a required Flutter plugin doesn't exist or doesn't support a needed API.
Five Interview Questions That Filter Effectively
"Explain the difference between a StatelessWidget and a StatefulWidget. When does a StatefulWidget rebuild?"
Expected: StatelessWidget has no mutable state — its build() method is called when the parent rebuilds. StatefulWidget has an associated State object that persists across rebuilds. Calling setState() on the State object marks the widget as dirty and schedules a rebuild of the build() method. The State object's lifecycle (initState, didUpdateWidget, dispose) is separate from the widget's — the State survives when the widget is reconfigured but not when the Element is removed from the tree.
"What is Dart's null safety and how does it change how you write code?"
Expected: non-nullable by default — String name cannot hold null; the compiler enforces this. The ! operator asserts non-null (throws at runtime if wrong); the ?. operator allows method calls on nullable values without throwing. late declares a non-nullable variable that will be initialised before use (shifts the safety from compile-time to runtime, so use sparingly). Code changes: explicit null checks everywhere that cross the nullable/non-nullable boundary, no more NullPointerException surprises on non-nullable types.
"How do you handle an API call and display loading/error/success states in Flutter?"
Expected: FutureBuilder for one-time async operations (shows loading spinner while future is pending, error widget if rejected, data widget if resolved), or Bloc/Riverpod with explicit state classes (LoadingState, ErrorState(message), SuccessState(data)). The complete answer handles all three states — a developer who describes only the success path has not built production UIs.
"You see janky animations in a Flutter release build. How do you diagnose this?"
Expected: use Flutter DevTools Performance tab to identify which widgets are rebuilding on each frame; check whether the build() method of frequently-rebuilding widgets is doing expensive work (network calls, list creation, complex computations — move these to initState or compute with compute() for isolate execution); verify animations use the Impeller renderer in release mode; check if RepaintBoundary is needed to isolate animated widgets from the rest of the tree.
"Describe how you'd implement a drag-and-drop list in Flutter."
Expected: ReorderableListView for standard reorderable lists (built-in, handles drag state); for custom drag behaviour, LongPressDraggable + DragTarget widgets. LongPressDraggable captures the child widget's feedback (what renders during drag), data (what's being dragged), and provides drag lifecycle callbacks. DragTarget specifies which data types it accepts and what happens on drop. The answer should mention that drag gestures in Flutter run via the gesture arena — only one gesture recogniser wins per gesture sequence.
Red Flags in Flutter Portfolios
No Dart null safety. Apps written before Dart 2.12 without migrating to null safety are a signal that the developer has not updated their knowledge. Null safety is required in current Flutter (SDK 3.0+).
setState() everywhere across screens. If a portfolio app's state management is entirely local setState() calls without a global state solution, the developer has not built an app with shared state across multiple routes. For small demo apps this is acceptable; for anything with a user account, real-time data, or multi-screen state — it's a gap.
Only one platform in the portfolio. A Flutter developer who has only deployed to one platform (usually iOS, because Mac is required for iOS development) may not have dealt with Android-specific layout issues, Play Store configuration, or Android-specific permission handling (which differs from iOS in meaningful ways).
Rates for Flutter Developers in 2026
| Region | Mid (3–5 yrs) | Senior (6+ yrs) |
|---|---|---|
| India | $22–40/hr | $38–60/hr |
| Eastern Europe | $42–68/hr | $65–95/hr |
| Latin America | $28–50/hr | $45–72/hr |
| UK / Western Europe | $68–108/hr | $92–138/hr |
| USA / Canada | $92–135/hr | $115–168/hr |
Flutter developers command a 5–10% premium over React Native developers at the same experience level due to lower supply and the steeper learning curve from web development backgrounds. See Flutter vs React Native for the hiring supply comparison.
Frequently Asked Questions
Can a React Native developer make the switch to Flutter? Yes, but the ramp time is real — 6–10 weeks to become productive in Dart, Flutter's widget model, and the state management ecosystem. The concepts that do not transfer: Dart's null safety model, the Widget/Element/RenderObject distinction, Dart's AOT compilation model, and platform channel implementation. React Native experience does help with the mobile platform concepts (push notifications, navigation patterns, store submission).
Is Flutter harder to learn than React Native?
Dart is a simpler language than JavaScript (no prototypal inheritance, no this binding surprises, no callback hell before async/await). Flutter's widget composition model is more explicit than React Native's component model, which some developers find easier and some find more verbose. The harder part of Flutter for web developers is Dart itself and the loss of the npm ecosystem — Flutter's pub.dev package ecosystem is smaller.
Does Flutter work for web as well as mobile? Yes. Flutter compiles to web (Canvas-based rendering — Flutter draws pixels in a browser canvas rather than using HTML elements), Windows, macOS, and Linux in addition to iOS and Android. Web support works for internal tools and apps where SEO and accessibility are not critical. For public-facing web content where search engine indexing matters, a web-native framework (Next.js, SvelteKit) is the right choice — Flutter's Canvas rendering is not indexed the same way as HTML content.