Toolmingo
Guides21 min read

50 Flutter Interview Questions (With Answers)

Top Flutter interview questions with clear answers and code examples — covering Dart, widgets, state management, architecture, animations, and performance.

Flutter is one of the fastest-growing cross-platform frameworks, and Flutter developer interviews cover everything from Dart fundamentals to widget trees, state management, and performance tuning. This guide covers 50 common Flutter interview questions with detailed answers and code examples.


Table of Contents

  1. Dart Fundamentals
  2. Widgets & UI
  3. State Management
  4. Navigation & Routing
  5. Async & Streams
  6. Architecture Patterns
  7. Animations
  8. Platform Integration
  9. Testing
  10. Performance & Advanced

Dart Fundamentals

1. What is Dart and why does Flutter use it?

Dart is a statically typed, object-oriented language developed by Google. Flutter uses Dart because:

  • AOT compilation — compiles to native ARM code for fast startup and runtime
  • JIT compilation — enables hot reload during development
  • Single language — one language for UI, business logic, and platform channels
  • Sound null safety — catches null errors at compile time
// Null safety in Dart
String name = "Flutter";    // non-nullable, must have value
String? nickname;           // nullable, can be null

// Safe access
int length = nickname?.length ?? 0;   // Elvis operator
String upper = nickname!.toUpperCase(); // force unwrap (throws if null)

2. What is the difference between var, final, and const?

Keyword Type inferred Assignable Evaluated
var Yes Multiple times Runtime
final Yes Once Runtime
const Yes Once Compile-time
var name = "Alice";         // type inferred as String, reassignable
name = "Bob";               // OK

final now = DateTime.now(); // set once, at runtime
// now = DateTime.now();    // ERROR: final variable

const pi = 3.14159;         // compile-time constant
const list = [1, 2, 3];     // deeply constant
// list.add(4);             // ERROR: Unsupported operation

// const constructors
const Text("Hello");        // same instance reused — no rebuild

Rule of thumb: prefer constfinalvar.


3. Explain Dart mixins.

Mixins allow adding behaviour to a class without inheritance. They enable code reuse across unrelated class hierarchies.

mixin Flyable {
  String fly() => "I can fly!";
}

mixin Swimmable {
  String swim() => "I can swim!";
}

class Duck extends Animal with Flyable, Swimmable {
  // Duck now has fly() and swim() methods
}

// Restrict mixin usage with `on`
mixin Logger on Widget {
  void log(String msg) => print("[${runtimeType}] $msg");
}

Key points:

  • Mixins cannot be instantiated directly
  • Use on to restrict which types can use the mixin
  • Dart uses C3 linearization for mixin conflict resolution

4. What is the difference between == and identical()?

// == checks value equality (calls operator==)
// identical() checks reference equality (same object in memory)

const a = "hello";
const b = "hello";
print(a == b);          // true (value equal)
print(identical(a, b)); // true (const canonicalization — same object)

var x = "hello";
var y = "hello";
print(x == y);          // true (value equal)
print(identical(x, y)); // false (different objects at runtime)

// With custom classes
class Point {
  final int x, y;
  Point(this.x, this.y);
  
  @override
  bool operator ==(Object other) =>
      other is Point && other.x == x && other.y == y;
  
  @override
  int get hashCode => Object.hash(x, y);
}

5. What are Dart extensions?

Extensions add methods to existing types without subclassing or modifying the original class.

extension StringUtils on String {
  String get capitalize =>
      isEmpty ? this : "${this[0].toUpperCase()}${substring(1)}";
  
  bool get isEmail => contains("@") && contains(".");
  
  String repeat(int times) => List.filled(times, this).join();
}

// Usage
"hello".capitalize;       // "Hello"
"user@example.com".isEmail; // true
"ab".repeat(3);           // "ababab"

// Extensions on generic types
extension ListUtils<T> on List<T> {
  T? firstOrNull() => isEmpty ? null : first;
}

Widgets & UI

6. What is the difference between StatelessWidget and StatefulWidget?

StatelessWidget StatefulWidget
State Immutable Mutable via State<T>
Rebuild trigger Parent rebuild setState() or parent
Performance Slightly lighter Slightly heavier
Use case Display-only UI Interactive / animated UI
// StatelessWidget — no internal state
class Greeting extends StatelessWidget {
  final String name;
  const Greeting({required this.name, super.key});

  @override
  Widget build(BuildContext context) {
    return Text("Hello, $name!");
  }
}

// StatefulWidget — has mutable state
class Counter extends StatefulWidget {
  const Counter({super.key});
  @override
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return Column(children: [
      Text("$_count"),
      ElevatedButton(
        onPressed: () => setState(() => _count++),
        child: const Text("Increment"),
      ),
    ]);
  }
}

7. Explain the Flutter widget tree, element tree, and render tree.

Flutter has three parallel trees:

Tree Purpose Examples
Widget tree Configuration / immutable blueprints Text, Column, Container
Element tree Mutable lifecycle instances ComponentElement, RenderObjectElement
Render tree Layout and painting RenderBox, RenderFlex

Flow: Widget describes → Element manages lifecycle → RenderObject paints.

When setState() is called:

  1. Widget subtree is rebuilt (cheap — just config objects)
  2. Flutter diffs old vs new widget tree
  3. Only changed RenderObjects are repainted

8. What is the difference between Column/Row and Flex?

Column and Row are convenience widgets built on Flex:

// These are equivalent:
Column(children: [...])
Flex(direction: Axis.vertical, children: [...])

Row(children: [...])
Flex(direction: Axis.horizontal, children: [...])

Key Flex layout properties:

Column(
  mainAxisAlignment: MainAxisAlignment.spaceBetween, // along main axis
  crossAxisAlignment: CrossAxisAlignment.start,      // perpendicular axis
  mainAxisSize: MainAxisSize.min,                    // shrink-wrap
  children: [
    Expanded(flex: 2, child: WidgetA()),  // takes 2/3 of space
    Expanded(flex: 1, child: WidgetB()),  // takes 1/3 of space
    Flexible(child: WidgetC()),           // can shrink below ideal size
  ],
)

9. What is const constructor in Flutter and why does it matter?

const constructors create compile-time constant widget instances that Flutter can skip rebuilding:

// Without const — new instance created on every build
Text("Hello")           // rebuilt unnecessarily

// With const — same instance reused
const Text("Hello")     // Flutter skips rebuild, O(1) check

// Deep const subtree
const Column(children: [
  Text("Static A"),
  Icon(Icons.star),
])
// The entire subtree is skipped during rebuild

Performance impact: Use const wherever the widget doesn't depend on runtime data. Flutter's lint rule prefer_const_constructors enforces this.


10. What is the Key in Flutter and when should you use it?

Keys help Flutter identify widgets across rebuilds, especially in lists:

// Without keys — Flutter matches by position, can swap state
ListView(children: [
  MyWidget(color: Colors.red),
  MyWidget(color: Colors.blue),
])

// With keys — Flutter matches by key, preserves state correctly
ListView(children: [
  MyWidget(key: ValueKey("red"), color: Colors.red),
  MyWidget(key: ValueKey("blue"), color: Colors.blue),
])
Key type Use case
ValueKey(value) Unique value (ID, string)
ObjectKey(object) Object reference
UniqueKey() Force new state each rebuild
GlobalKey Access widget state from outside its subtree

Rule: Use keys when reordering stateful widgets in a list. Avoid UniqueKey() in build() — creates new widget every rebuild.


State Management

11. Explain the different state management approaches in Flutter.

Approach Complexity Best for
setState Low Local, simple widget state
InheritedWidget Medium Propagate state down tree
Provider Low-Medium Small-medium apps
Riverpod Medium Testable, compile-safe Provider
Bloc/Cubit Medium-High Event-driven, large apps
GetX Low Rapid development (opinionated)
MobX Medium Reactive with code-gen
Redux High Predictable global state

12. How does Provider work?

Provider uses InheritedWidget under the hood to propagate state:

// 1. Model
class CounterModel extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners(); // triggers rebuild of listening widgets
  }
}

// 2. Provide at root
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => CounterModel(),
      child: const MyApp(),
    ),
  );
}

// 3. Consume anywhere in the tree
class CounterDisplay extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final count = context.watch<CounterModel>().count; // rebuild on change
    return Text("$count");
  }
}

class IncrementButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      // context.read — no rebuild
      onPressed: () => context.read<CounterModel>().increment(),
      child: const Text("Increment"),
    );
  }
}

context.watch → subscribes, rebuilds on change
context.read → reads once, no rebuild
context.select → rebuilds only when selected value changes


13. What is Riverpod and how does it differ from Provider?

Riverpod is a rewrite of Provider that is compile-safe and doesn't rely on BuildContext:

// Riverpod provider definition (outside widget tree)
final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
  return CounterNotifier();
});

class CounterNotifier extends StateNotifier<int> {
  CounterNotifier() : super(0);
  void increment() => state++;
}

// Consuming in a widget
class CounterWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Column(children: [
      Text("$count"),
      ElevatedButton(
        onPressed: () => ref.read(counterProvider.notifier).increment(),
        child: const Text("+"),
      ),
    ]);
  }
}
Feature Provider Riverpod
Context required Yes No (WidgetRef)
Compile-time safety Partial Full
Testability Good Excellent
Provider override Difficult Easy
Auto-dispose Manual Built-in

14. Explain BLoC pattern in Flutter.

BLoC (Business Logic Component) separates UI from business logic via events and states:

// Events
abstract class CounterEvent {}
class Increment extends CounterEvent {}
class Decrement extends CounterEvent {}

// States
class CounterState {
  final int count;
  const CounterState(this.count);
}

// BLoC
class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterBloc() : super(const CounterState(0)) {
    on<Increment>((event, emit) => emit(CounterState(state.count + 1)));
    on<Decrement>((event, emit) => emit(CounterState(state.count - 1)));
  }
}

// UI
class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => CounterBloc(),
      child: BlocBuilder<CounterBloc, CounterState>(
        builder: (context, state) {
          return Column(children: [
            Text("${state.count}"),
            ElevatedButton(
              onPressed: () => context.read<CounterBloc>().add(Increment()),
              child: const Text("+"),
            ),
          ]);
        },
      ),
    );
  }
}

Cubit is a simpler BLoC variant — no events, just methods that emit states.


Navigation & Routing

15. What is the difference between Navigator 1.0 and Navigator 2.0?

Navigator 1.0 Navigator 2.0
API Imperative Declarative
Deep links Difficult First-class
URL sync Manual Automatic
Back button Automatic Manual control
Complexity Low High
// Navigator 1.0 — imperative push/pop
Navigator.push(context, MaterialPageRoute(builder: (_) => DetailPage()));
Navigator.pop(context, result);

// Navigator 2.0 — declarative
Router(
  routerDelegate: MyRouterDelegate(),
  routeInformationParser: MyRouteInformationParser(),
)

Most teams use GoRouter (official package) which wraps Navigator 2.0 with a simple API:

final router = GoRouter(routes: [
  GoRoute(path: "/", builder: (_, __) => const HomePage()),
  GoRoute(
    path: "/detail/:id",
    builder: (context, state) => DetailPage(id: state.pathParameters["id"]!),
  ),
]);

// Navigation
context.go("/detail/42");
context.push("/detail/42"); // adds to stack
context.pop();

16. How do you pass data between screens?

// 1. Constructor arguments (simple)
Navigator.push(context, MaterialPageRoute(
  builder: (_) => DetailPage(item: selectedItem),
));

// 2. Named routes with arguments
Navigator.pushNamed(context, "/detail", arguments: selectedItem);
// In target widget:
final item = ModalRoute.of(context)!.settings.arguments as Item;

// 3. GoRouter path/query parameters
context.go("/product/42?ref=home");
// state.pathParameters["id"] = "42"
// state.uri.queryParameters["ref"] = "home"

// 4. Return data on pop
final result = await Navigator.push<String>(
  context,
  MaterialPageRoute(builder: (_) => PickerPage()),
);
if (result != null) setState(() => _selected = result);

Async & Streams

17. Explain Future, async, and await in Dart.

// Future represents a value available in the future
Future<String> fetchUser(int id) async {
  final response = await http.get(Uri.parse("/users/$id")); // non-blocking wait
  if (response.statusCode != 200) throw Exception("Not found");
  return jsonDecode(response.body)["name"] as String;
}

// Consuming a Future
void loadUser() async {
  try {
    final name = await fetchUser(1);
    setState(() => _userName = name);
  } catch (e) {
    setState(() => _error = e.toString());
  }
}

// FutureBuilder widget
FutureBuilder<String>(
  future: fetchUser(1),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const CircularProgressIndicator();
    }
    if (snapshot.hasError) return Text("Error: ${snapshot.error}");
    return Text("Hello, ${snapshot.data}!");
  },
)

18. What is a Stream in Dart?

A Stream is a sequence of asynchronous events over time:

// Single-subscription stream (like a file read)
Stream<int> countUp(int max) async* {
  for (int i = 0; i <= max; i++) {
    await Future.delayed(const Duration(seconds: 1));
    yield i;
  }
}

// Broadcast stream (multiple listeners, like UI events)
final controller = StreamController<String>.broadcast();
controller.sink.add("event1");
controller.stream.listen((event) => print(event));

// StreamBuilder widget
StreamBuilder<int>(
  stream: countUp(10),
  builder: (context, snapshot) {
    if (!snapshot.hasData) return const CircularProgressIndicator();
    return Text("Count: ${snapshot.data}");
  },
)
Future Stream
Values One Many over time
Completion Once Can be infinite
Listeners Multiple Single or broadcast
Error One Per-event

19. What is the difference between Stream and StreamController?

// StreamController creates and controls a stream
class LocationService {
  final _controller = StreamController<Position>.broadcast();
  
  Stream<Position> get locationStream => _controller.stream;
  
  void _onPositionUpdate(Position pos) => _controller.sink.add(pos);
  
  void dispose() => _controller.close(); // ALWAYS close to prevent memory leaks
}

// Common mistake: not closing StreamController
// Use StreamSubscription to cancel
class _MyWidgetState extends State<MyWidget> {
  late StreamSubscription<Position> _sub;
  
  @override
  void initState() {
    super.initState();
    _sub = locationService.locationStream.listen(_onLocation);
  }
  
  @override
  void dispose() {
    _sub.cancel(); // prevent memory leak
    super.dispose();
  }
}

Architecture Patterns

20. What is clean architecture in Flutter?

Clean architecture separates code into layers with one-way dependency:

Presentation (Widgets, BLoC/Cubit)
    ↓ depends on
Domain (Entities, Use Cases, Repository interfaces)
    ↓ depends on
Data (Repository implementations, Data Sources, DTOs)
// Domain layer — pure Dart, no Flutter imports
class User {
  final String id;
  final String name;
  const User({required this.id, required this.name});
}

abstract class UserRepository {
  Future<User> getUser(String id);
}

class GetUserUseCase {
  final UserRepository repository;
  GetUserUseCase(this.repository);
  
  Future<User> call(String id) => repository.getUser(id);
}

// Data layer
class UserRepositoryImpl implements UserRepository {
  final ApiClient apiClient;
  UserRepositoryImpl(this.apiClient);
  
  @override
  Future<User> getUser(String id) async {
    final dto = await apiClient.fetchUser(id);
    return User(id: dto.id, name: dto.name);
  }
}

// Presentation layer
class UserCubit extends Cubit<UserState> {
  final GetUserUseCase getUserUseCase;
  UserCubit(this.getUserUseCase) : super(UserInitial());
  
  Future<void> loadUser(String id) async {
    emit(UserLoading());
    try {
      final user = await getUserUseCase(id);
      emit(UserLoaded(user));
    } catch (e) {
      emit(UserError(e.toString()));
    }
  }
}

21. What is dependency injection in Flutter?

DI provides dependencies to a class rather than creating them inside. Popular options:

// 1. get_it service locator
final sl = GetIt.instance;

void setupDependencies() {
  sl.registerLazySingleton<ApiClient>(() => ApiClient());
  sl.registerLazySingleton<UserRepository>(() => UserRepositoryImpl(sl()));
  sl.registerFactory<UserCubit>(() => UserCubit(sl()));
}

// Usage
final cubit = sl<UserCubit>();

// 2. injectable (code-gen on top of get_it)
@injectable
class UserCubit extends Cubit<UserState> {
  UserCubit(@injectable UserRepository repo) : super(UserInitial());
}

// 3. Riverpod providers (built-in DI)
final userRepoProvider = Provider<UserRepository>(
  (ref) => UserRepositoryImpl(ref.read(apiClientProvider)),
);

Animations

22. What are the main animation types in Flutter?

Type API Use case
Implicit AnimatedContainer, AnimatedOpacity Simple value changes
Explicit AnimationController + Tween Full control
Hero Hero widget Shared element transitions
Rive Rive package Complex designer animations
Lottie Lottie package After Effects animations
// Implicit — easiest
AnimatedContainer(
  duration: const Duration(milliseconds: 300),
  curve: Curves.easeInOut,
  width: _expanded ? 200 : 100,
  color: _expanded ? Colors.blue : Colors.red,
)

// Explicit — full control
class _FadeInState extends State<FadeIn>
    with SingleTickerProviderStateMixin {
  late AnimationController _ctrl;
  late Animation<double> _opacity;

  @override
  void initState() {
    super.initState();
    _ctrl = AnimationController(
      duration: const Duration(milliseconds: 500),
      vsync: this,
    );
    _opacity = Tween(begin: 0.0, end: 1.0).animate(
      CurvedAnimation(parent: _ctrl, curve: Curves.easeIn),
    );
    _ctrl.forward();
  }

  @override
  void dispose() {
    _ctrl.dispose(); // ALWAYS dispose AnimationController
    super.dispose();
  }

  @override
  Widget build(BuildContext context) =>
      FadeTransition(opacity: _opacity, child: widget.child);
}

23. What is TickerProviderStateMixin vs SingleTickerProviderStateMixin?

// SingleTickerProviderStateMixin — one AnimationController
class _MyState extends State<MyWidget>
    with SingleTickerProviderStateMixin {
  late AnimationController _ctrl;
  // _ctrl = AnimationController(vsync: this, ...)
}

// TickerProviderStateMixin — multiple AnimationControllers
class _MyTabState extends State<MyTabWidget>
    with TickerProviderStateMixin {
  late AnimationController _ctrl1;
  late AnimationController _ctrl2;
  // Both use vsync: this
}

Using SingleTickerProviderStateMixin with multiple controllers throws a runtime error. Always use TickerProviderStateMixin when you need more than one controller.


Platform Integration

24. What is a Platform Channel?

Platform channels allow Flutter to communicate with native Android/iOS code:

// Flutter (Dart) side
class BatteryService {
  static const _channel = MethodChannel("com.example/battery");

  Future<int> getBatteryLevel() async {
    try {
      return await _channel.invokeMethod<int>("getBatteryLevel") ?? -1;
    } on PlatformException catch (e) {
      throw Exception("Failed to get battery: ${e.message}");
    }
  }
}

// Android (Kotlin) side
class MainActivity : FlutterActivity() {
  private val CHANNEL = "com.example/battery"

  override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
      .setMethodCallHandler { call, result ->
        if (call.method == "getBatteryLevel") {
          val level = getBatteryLevel()
          if (level != -1) result.success(level)
          else result.error("UNAVAILABLE", "Battery unavailable", null)
        } else result.notImplemented()
      }
  }
}
Channel type Direction Use case
MethodChannel Bidirectional One-off method calls
EventChannel Native → Flutter Continuous streams (sensors, BLE)
BasicMessageChannel Bidirectional Custom message codecs

25. How do you handle permissions in Flutter?

// Using permission_handler package
import "package:permission_handler/permission_handler.dart";

Future<void> requestCamera() async {
  final status = await Permission.camera.status;

  if (status.isGranted) {
    _openCamera();
  } else if (status.isDenied) {
    final result = await Permission.camera.request();
    if (result.isGranted) _openCamera();
  } else if (status.isPermanentlyDenied) {
    // User denied with "Don't ask again" — open app settings
    await openAppSettings();
  }
}

// Multiple permissions at once
Future<void> requestMultiple() async {
  final statuses = await [
    Permission.camera,
    Permission.microphone,
  ].request();

  if (statuses[Permission.camera]!.isGranted &&
      statuses[Permission.microphone]!.isGranted) {
    _startVideoCall();
  }
}

Also configure AndroidManifest.xml (Android) and Info.plist (iOS) with required permission strings.


Testing

26. What are the three types of tests in Flutter?

Type Speed Scope Command
Unit tests Fast Functions, classes, BLoC flutter test
Widget tests Medium Single widget rendering flutter test
Integration tests Slow Full app on device/emulator flutter test integration_test/
// Unit test
test("counter increments", () {
  final cubit = CounterCubit();
  cubit.increment();
  expect(cubit.state, 1);
});

// Widget test
testWidgets("shows user name", (tester) async {
  await tester.pumpWidget(
    const MaterialApp(home: UserCard(name: "Alice")),
  );
  expect(find.text("Alice"), findsOneWidget);
  
  await tester.tap(find.byType(ElevatedButton));
  await tester.pump(); // rebuild after state change
  expect(find.text("Tapped"), findsOneWidget);
});

// Bloc test (bloc_test package)
blocTest<CounterBloc, CounterState>(
  "emits [1] when Increment is added",
  build: () => CounterBloc(),
  act: (bloc) => bloc.add(Increment()),
  expect: () => [const CounterState(1)],
);

27. How do you mock dependencies in Flutter tests?

// Using mockito
@GenerateMocks([UserRepository])
void main() {
  late MockUserRepository mockRepo;
  late UserCubit cubit;

  setUp(() {
    mockRepo = MockUserRepository();
    cubit = UserCubit(GetUserUseCase(mockRepo));
  });

  tearDown(() => cubit.close());

  test("loads user successfully", () async {
    when(mockRepo.getUser("1"))
        .thenAnswer((_) async => const User(id: "1", name: "Alice"));

    await cubit.loadUser("1");

    expect(cubit.state, isA<UserLoaded>());
    expect((cubit.state as UserLoaded).user.name, "Alice");
  });

  test("emits error on failure", () async {
    when(mockRepo.getUser("999"))
        .thenThrow(Exception("Not found"));

    await cubit.loadUser("999");

    expect(cubit.state, isA<UserError>());
  });
}

Performance & Advanced

28. What causes jank in Flutter and how do you fix it?

Jank occurs when frames take longer than 16.67ms (60fps) or 8.33ms (120fps):

Common causes and fixes:

Cause Fix
Heavy build() method Extract widgets, use const
Rebuilding too much RepaintBoundary, context.select
Expensive operations on main isolate Move to Isolate or compute()
Loading images synchronously cached_network_image, lazy loading
Missing ListView.builder Use .builder for long lists
Opacity on large widgets Offstage or Visibility instead
// Bad — rebuilds entire list on any change
Column(children: items.map((e) => ItemWidget(e)).toList())

// Good — only builds visible items
ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) => ItemWidget(items[index]),
)

// Move CPU work off main thread
final result = await compute(heavyComputation, data);

// RepaintBoundary — isolates repaint
RepaintBoundary(
  child: ExpensiveAnimatedWidget(), // won't trigger parent repaint
)

29. What is Isolate in Dart?

Isolates are independent workers with their own memory heap — true parallelism in Dart:

// compute() — simple isolate for one-shot tasks
List<int> sortNumbers(List<int> numbers) {
  return [...numbers]..sort();
}

final sorted = await compute(sortNumbers, [5, 2, 8, 1, 9]);

// Isolate.spawn — full control
void heavyTask(SendPort sendPort) {
  // runs in separate isolate
  final result = performExpensiveCalc();
  sendPort.send(result);
}

final receivePort = ReceivePort();
await Isolate.spawn(heavyTask, receivePort.sendPort);
final result = await receivePort.first;

// Dart 2.15+: Isolate.run() (simpler API)
final sorted = await Isolate.run(() => [5, 2, 8, 1]..sort());

Important: Isolates cannot share memory. Communication is via SendPort/ReceivePort with message passing (objects are copied).


30. What is InheritedWidget and when would you use it directly?

InheritedWidget is the foundation for Provider, Theme, MediaQuery, and all Flutter state propagation:

class AppThemeData extends InheritedWidget {
  final Color primaryColor;

  const AppThemeData({
    required this.primaryColor,
    required super.child,
    super.key,
  });

  static AppThemeData of(BuildContext context) {
    final result = context.dependOnInheritedWidgetOfExactType<AppThemeData>();
    assert(result != null, "No AppThemeData found in context");
    return result!;
  }

  @override
  bool updateShouldNotify(AppThemeData oldWidget) =>
      primaryColor != oldWidget.primaryColor;
}

// Usage
final color = AppThemeData.of(context).primaryColor;

updateShouldNotify controls whether dependent widgets rebuild when the InheritedWidget changes — return false to suppress unnecessary rebuilds.


31. How does Flutter handle hot reload vs hot restart?

Hot Reload Hot Restart
Speed ~1 second ~3-5 seconds
State preserved Yes No
What updates Widget tree Full app restart
When to use UI changes Logic/state structure changes
What won't update initState, main(), global vars Nothing

Hot reload works by injecting updated source code into the Dart VM and rebuilding the widget tree while preserving the current state. Changes to initState(), constructors, or global variables require hot restart.


32. What is MediaQuery and LayoutBuilder?

// MediaQuery — device-level info
class ResponsiveWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
    final padding = MediaQuery.of(context).padding; // safe area
    final isTablet = size.width > 600;

    return isTablet ? TabletLayout() : MobileLayout();
  }
}

// LayoutBuilder — parent constraint-based
class AdaptiveCard extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth > 400) {
          return WideCard();
        }
        return NarrowCard();
      },
    );
  }
}

Prefer LayoutBuilder when adapting to the parent widget's size. Use MediaQuery for device-level info (screen size, orientation, safe area).


33. What is GlobalKey and what are its risks?

// GlobalKey allows accessing widget state from outside its subtree
final _formKey = GlobalKey<FormState>();

Form(
  key: _formKey,
  child: Column(children: [
    TextFormField(validator: (v) => v!.isEmpty ? "Required" : null),
    ElevatedButton(
      onPressed: () {
        if (_formKey.currentState!.validate()) {
          _formKey.currentState!.save();
        }
      },
      child: const Text("Submit"),
    ),
  ]),
)

Risks:

  • GlobalKey forces a new element — can't be used in const widgets
  • Moving a widget with GlobalKey across the tree is expensive (triggers full subtree rebuild)
  • Overuse leads to tight coupling and hard-to-test code

Use GlobalKey sparingly — mainly for Form, ScaffoldMessenger, or Navigator.


34. Explain the Flutter build modes.

Mode Command Optimizations Use case
Debug flutter run JIT, asserts, DevTools Development
Profile flutter run --profile AOT, minimal debuggability Performance profiling
Release flutter build AOT, tree shaking, minification Production
// Conditional code by mode
import "package:flutter/foundation.dart";

if (kDebugMode) {
  print("Debug only logging");
}

if (kReleaseMode) {
  // Initialize production crash reporting
  FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
}

if (kProfileMode) {
  // Profile-specific setup
}

35. What is tree shaking in Flutter?

Tree shaking removes unused code from the final build. Flutter's AOT compiler:

  1. Starts from main() entry point
  2. Traces all reachable code
  3. Eliminates everything unreachable
// This entire class is removed if never used
class UnusedUtility {
  static String format(String s) => s.toUpperCase();
}

// Icons are tree-shaken
// Only icons you actually use are included in the binary
Icons.home     // included
Icons.settings // included only if referenced
// All other 1000+ icons — excluded

Use flutter build apk --analyze-size to inspect what's included in your binary.


Common Anti-Patterns

Anti-pattern Problem Fix
setState in initState Throws error on first frame Use addPostFrameCallback
Not disposing AnimationController Memory leak dispose() in State.dispose()
context after async gap May be unmounted Check mounted before using context
Large build() methods Poor readability, hard to optimize Extract private methods or widgets
GlobalKey everywhere Performance + tight coupling Use callbacks or state management
ListView without .builder All items built upfront Use ListView.builder
Blocking main isolate UI jank Use compute() or Isolate.run()
Ignoring const lint Unnecessary rebuilds Add const wherever possible
// Dangerous: using context after async gap
void _save() async {
  await saveToDatabase();
  // Widget may have been unmounted!
  Navigator.pop(context); // may throw
}

// Safe: check mounted
void _save() async {
  await saveToDatabase();
  if (!mounted) return; // guard
  Navigator.pop(context);
}

Flutter vs React Native vs Native

Feature Flutter React Native Native (iOS/Android)
Language Dart JavaScript/TypeScript Swift/Kotlin
UI engine Own (Skia/Impeller) Native components Native
Performance Near-native Good (JSI bridge) Best
Code sharing ~95% ~85% 0%
Hot reload Yes Yes Partial
Bundle size Larger (~5MB) Smaller N/A
Animations Excellent Good Excellent
Desktop/Web Yes (experimental) Limited No

Frequently Asked Questions

Q: Is Flutter production-ready?
Yes. Flutter powers apps at Google, BMW, Alibaba, eBay, and thousands of startups. Version 3.x has stable support for Android, iOS, Web, and Desktop.

Q: How does Flutter differ from React Native architecturally?
Flutter renders everything using its own Skia/Impeller graphics engine — no native UI components. React Native bridges to native components (UIKit/Views). Flutter's approach gives pixel-perfect consistency across platforms; React Native's gives native look and feel automatically.

Q: What's the best state management solution?
It depends on app size: setState for local state, Provider/Riverpod for small-medium apps, BLoC for large apps needing strict separation and testability.

Q: Can Flutter access native device APIs?
Yes, via Platform Channels (direct) or the extensive pub.dev package ecosystem. Most common APIs (camera, location, Bluetooth, push notifications, SQLite) have well-maintained packages.

Q: How do you handle background tasks in Flutter?
Use flutter_background_service or workmanager for background Dart code. For platform-native background processing, use Android WorkManager or iOS BGTaskScheduler via Platform Channels.

Q: What is pubspec.yaml?
The project manifest file for Flutter/Dart. It declares the app name, version, dependencies (dependencies:), dev dependencies (dev_dependencies:), assets, and fonts. Run flutter pub get after changes.

Related tools

Keep reading

All Toolmingotools are free & run in your browser

No sign-up, no upload, no watermark. Your files never leave your device.

Browse all tools