33. Swapped Flutter tiles keep colors
Two StatefulWidget tiles in a Row store a random color in their State. When you swap their order in the list, the colors do not move with them. Why?
- A.Flutter matches widgets to existing elements by type and position, so without keys the State objects stay in place; give each tile a key such as ValueKey(tile.id)
- B.setState was not called after the swap
- C.Row caches the first children it was given and keeps painting them, so a reordered list is ignored until the screen is pushed again
- D.StatefulWidgets cannot be reordered in Flutter
Show answer
Answer: A. Flutter matches widgets to existing elements by type and position, so without keys the State objects stay in place; give each tile a key such as ValueKey(tile.id)
When rebuilding, Flutter compares the new widget list with the existing elements, and same-type widgets in the same slot reuse the old element and its State. After the swap, each slot still has the same type, so the States stay where they were. Keys let Flutter match by identity, so the State moves with its widget.
34. Dart microtask and event order
In what order does this Dart program print its letters?
import 'dart:async';
void main() {
print('A');
Future(() => print('B'));
scheduleMicrotask(() => print('C'));
Future.value().then((_) => print('D'));
print('E');
}- A.A B C D E
- B.A E B C D
- C.A E C D B
- D.A C D E B
Show answer
Answer: C. A E C D B
Synchronous code runs first, printing A and E. Then the microtask queue is drained before the next event: C was queued first, then the completion of Future.value() runs the then callback, printing D. Future(() => ...) schedules a timer event, so B prints last.
35. Widget, element and render trees
Flutter keeps three trees: widgets, elements and render objects. Which statement about them is correct?
- A.Widgets are long-lived and mutable; elements are rebuilt from scratch on every frame
- B.Render objects are created again for every build, while elements are never reused
- C.Only the widget tree exists at runtime; the other two are debug-only views
- D.Widgets are cheap immutable configs; elements are long-lived and link widgets to render objects
Show answer
Answer: D. Widgets are cheap immutable configs; elements are long-lived and link widgets to render objects
Widgets are lightweight, immutable descriptions that are rebuilt often. Elements are the long-lived instances in the tree that hold State and decide whether to update or replace a render object. Render objects do layout and painting and are reused when the widget type and key match.
36. Overriding == without hashCode
A Product class overrides `operator ==` to compare ids but does not override `hashCode`. Two products with the same id are added to a Set. What is likely?
- A.The Set throws a runtime error because hashCode is missing
- B.The Set always keeps only one product, since == is checked first
- C.The Set may keep both, because equal objects have different hash codes
- D.The analyzer refuses to compile the Set literal with these objects
Show answer
Answer: C. The Set may keep both, because equal objects have different hash codes
Hash-based collections first use hashCode to pick a bucket and only then call ==. If equal objects can have different hash codes, they land in different buckets and the duplicate is never found. Always override both together, or use a package such as equatable or freezed.
37. Why teams use Pigeon
A team has dozens of MethodChannel calls with string method names and untyped maps, and bugs keep coming from typos. What does Pigeon offer?
- A.A faster rendering engine that replaces platform channels
- B.Automatic conversion of Kotlin code into Dart
- C.A way to share memory between Dart and native threads
- D.Generated, type-safe Dart, Kotlin and Swift code for the channel API
Show answer
Answer: D. Generated, type-safe Dart, Kotlin and Swift code for the channel API
Pigeon reads an interface definition written in Dart and generates matching host and Dart code for the messages. Method names and argument types are checked by each compiler, so mismatches show up at build time instead of as runtime MissingPluginException or cast errors.
38. Calling a C library from Dart
You need to call a fast C image-processing library synchronously from Dart on mobile. Which approach avoids platform-channel message overhead?
- A.A MethodChannel with a binary codec
- B.dart:ffi bindings to the compiled C library
- C.An EventChannel that streams pixels
- D.A WebView running the C code through WebAssembly
Show answer
Answer: B. dart:ffi bindings to the compiled C library
dart:ffi lets Dart call native C functions directly in the same process, without serialising messages to the platform side. Tools such as ffigen generate bindings from C headers. Long-running calls should still run on a background isolate so the UI does not freeze.
39. Reading the performance overlay
The Flutter performance overlay shows the raster (GPU) graph spiking red while the UI graph stays green. Where is the problem most likely?
- A.Slow Dart code running inside build methods
- B.Costly painting such as saveLayer, heavy clips or big blurs
- C.Too many network calls made from the UI isolate
- D.Slow JSON parsing inside a FutureBuilder callback
Show answer
Answer: B. Costly painting such as saveLayer, heavy clips or big blurs
The UI thread runs your Dart build, layout and paint recording. The raster thread turns the layer tree into GPU commands. When only raster time is high, the cost is in what is drawn, often opacity layers, clipping, shadows, blurs or oversized images, not in Dart logic.
40. shrinkWrap lists inside a scroll view
A screen nests five `ListView(shrinkWrap: true)` sections of 200 items each inside a SingleChildScrollView. Scrolling is slow. Why?
- A.shrinkWrap forces each of the lists to run on its own isolate
- B.SingleChildScrollView repaints only the first list it contains
- C.shrinkWrap lays out every item to measure the list, so nothing is lazy
- D.Nested lists disable hardware acceleration for the whole screen
Show answer
Answer: C. shrinkWrap lays out every item to measure the list, so nothing is lazy
With shrinkWrap the list must size itself to its content, so it builds and lays out all 1,000 items. Using a CustomScrollView with SliverList sections keeps one scroll viewport and builds only visible items.
41. Obfuscating a Flutter release
You build with `flutter build apk --obfuscate --split-debug-info=build/symbols`. What must you keep to read crash stack traces later?
- A.Nothing; obfuscated traces are readable by default
- B.The symbol files written to build/symbols for that exact build
- C.Only the AndroidManifest.xml of the build
- D.The debug APK built from the same commit
Show answer
Answer: B. The symbol files written to build/symbols for that exact build
Obfuscation renames Dart symbols, and split-debug-info moves the symbol tables out of the binary, which also reduces size. Stack traces then contain only addresses or obscured names. Running flutter symbolize with the matching symbols, or uploading them to your crash tool, turns them back into readable frames.
42. pumpAndSettle times out
A widget test calls `await tester.pumpAndSettle()` on a screen with a looping CircularProgressIndicator and fails with a timeout. What is the fix?
- A.Increase the default test timeout to ten full minutes
- B.Use tester.pump(duration), since the animation never settles
- C.Run the same widget test in release mode instead
- D.Call setState inside the test body to stop the frames
Show answer
Answer: B. Use tester.pump(duration), since the animation never settles
pumpAndSettle keeps pumping frames until no more are scheduled. An indeterminate progress indicator schedules frames forever, so it never settles and the call times out. Pump a fixed duration instead, or replace the loading state with a fake that has finished.
43. List<dynamic> is not a List<String>
Decoding `{"tags": ["a", "b"]}` and writing `List<String> tags = json['tags'];` throws a type error at runtime. What fixes it?
- A.Build a typed list with List<String>.from(json['tags'])
- B.Declare the variable as final instead of using a type
- C.Call jsonDecode twice on the same input string
- D.Wrap the assignment in a try block and ignore it
Show answer
Answer: A. Build a typed list with List<String>.from(json['tags'])
jsonDecode builds a List<dynamic>, and Dart generics are reified, so a List<dynamic> is not a List<String> even if every element is a string. Creating a new typed list with List<String>.from or a lazy view with cast<String>() gives the right type. Code generators such as json_serializable handle this for you.
44. Flutter's layout rule
Which sentence correctly describes Flutter's layout protocol?
- A.Sizes go down, constraints go up, and the child sets its position
- B.Each widget picks any size and the screen scrolls to fit
- C.Constraints go down, sizes go up, and the parent sets the position
- D.The root widget measures every leaf before any layout
Show answer
Answer: C. Constraints go down, sizes go up, and the parent sets the position
A parent passes min and max width and height to each child. The child picks a size within those limits and reports it back, and the parent then decides where to place it. A child cannot be bigger than its constraints allow, which explains many surprising layouts.
45. Container ignores its width
Why does this Container fill the whole screen instead of being 100 × 100?
void main() => runApp(
Container(width: 100, height: 100, color: Colors.red),
);
- A.Container ignores width and height unless a color is set
- B.MaterialApp scales every child up to the screen size
- C.The width is in centimetres, not logical pixels
- D.The screen gives tight constraints, forcing the child to be full size
Show answer
Answer: D. The screen gives tight constraints, forcing the child to be full size
The root view passes tight constraints equal to the screen size, so its child must be exactly that size and width: 100 is ignored. Wrapping the Container in Center or Align loosens the constraints, and then 100 × 100 is honoured.
46. Blocking back with unsaved changes
On Android 14 and later, predictive back gestures need to know in advance whether a route may pop. Which Flutter widget handles an "unsaved changes" guard correctly?
- A.WillPopScope returning a Future from onWillPop
- B.PopScope with canPop set from the form state
- C.GestureDetector listening for a left swipe
- D.A WillPopScope inside a SafeArea widget
Show answer
Answer: B. PopScope with canPop set from the form state
Predictive back shows a preview of the previous screen before the gesture completes, so the system must know ahead of time whether popping is allowed. PopScope declares this with canPop and reports attempts through onPopInvokedWithResult, where you can show a dialog. WillPopScope is deprecated because its async callback cannot support this.
47. Turning a callback API into a Future
An old SDK reports results through onSuccess and onError callbacks. What does this Dart code use to expose it as a Future?
Future<String> scan() {
final c = Completer<String>();
sdk.scan(onSuccess: c.complete, onError: c.completeError);
return c.future;
}- A.A StreamController that is never closed
- B.A Timer that polls the SDK every second
- C.An Isolate that blocks until a result arrives
- D.A Completer, completed from the callbacks
Show answer
Answer: D. A Completer, completed from the callbacks
A Completer creates a Future you can finish later with complete or completeError. Wrapping callback-style APIs this way lets callers use await and try/catch. Make sure every path completes exactly once, or callers will hang or hit a "Future already completed" error.