This complete guide covers all 20 Mobile tutorials on TheCodeForge, organised by topic.
Mobile development punishes a habit that server-side work tolerates: assuming the thing you need is already there. A backend process starts, loads its config, and runs. A mobile screen is built, torn down, rebuilt on rotation, suspended when a call comes in, and restored from a snapshot hours later — and every one of those transitions can hand your code a widget that no longer exists, a field that was never assigned, or a value the platform swears is non-null but isn't.
That is why almost every error in this track is a lifecycle error wearing a framework's costume. Flutter calls it setState called after dispose. Kotlin calls it lateinit property has not been initialized. Swift calls it unexpectedly found nil while unwrapping an Optional. Three languages, three stack traces, one root cause: code ran at a moment the author did not picture. Learn to read the moment and the fixes stop feeling like guesswork.
Before reaching for a fix, place the failure on the lifecycle. Nearly every crash in this track lands in one of four windows, and the window tells you which fix is the correct one rather than the one that merely silences the log.
| Moment | What the platform is doing | Errors it produces |
|---|---|---|
| Before first build | Fields declared but not yet assigned; dependency graph not wired | LateInitializationError, Kotlin lateinit not initialized, Default FirebaseApp is not initialized |
| During build / render | Layout constraints resolved; a parent hands a child a box | RenderFlex overflowed, Angular-style binding errors, index-out-of-range on a list still loading |
| After teardown | Widget or view controller destroyed, but async work is still in flight and holds a reference | setState() called after dispose(), Kotlin JobCancellationException, SwiftUI publishing from a background thread |
| At build / toolchain time | Gradle, CocoaPods or SPM resolving versions and modules | Gradle version mismatch, CocoaPods not installed, unresolved reference, no provisioning profile |
The pattern is worth internalising because it generalises. A crash in the third window is never fixed by a null check — a null check there just converts a loud crash into a silent no-op while the leak that caused it stays. The fix is to cancel the work when the owner dies.
Kotlin and Swift both have real null safety, and both punch a hole in it at exactly one place: the boundary with code the compiler cannot see. In Kotlin that boundary is Java. A Java method returning String becomes a platform type, written String!, which Kotlin will happily assign to a non-null String with no complaint at all — and then throw at runtime when the value turns out to be null.
This is the single most surprising thing about Kotlin for engineers arriving from Java, because the crash looks impossible: a NullPointerException on a type the compiler declared could not be null. The compiler was not wrong. It was never asked.
@Nullable / @NonNull and Kotlin's platform types collapse into real nullable and non-null types. One annotation pass on a legacy module buys you compile-time errors instead of production crashes — the highest-leverage hour you can spend on a mixed codebase.Flutter's yellow-and-black overflow stripe reads like a styling complaint. It is not. It is Flutter telling you that a child asked for more space than its parent offered, and that the difference is exactly N pixels. Layout in Flutter is a single pass: constraints travel down the tree, sizes travel back up, and a Row hands its children unbounded width in the main axis. A Text that never wraps plus a sibling icon can therefore exceed a phone's width with no warning until it renders.
So the fix is chosen by intent, not by trial. If the content should shrink, constrain it. If it should wrap, let it. If it should scroll, make the axis scrollable. Wrapping the whole thing in something that merely hides the overflow removes the stripe and keeps the bug.
| Intent | Widget | Behaviour |
|---|---|---|
| Text should shrink to fit the row | Expanded / Flexible | Child is given the remaining main-axis space and wraps or ellipsises inside it |
| Items should flow onto a second line | Wrap | Lays out children in runs, starting a new run when the current one is full |
| Content is legitimately longer than the screen | SingleChildScrollView / ListView | Gives the axis unbounded extent and scrolls it |
| Only part of a long label matters | Text(overflow: TextOverflow.ellipsis) | Truncates visibly, so the user knows text was cut |
Gradle, CocoaPods and Swift Package Manager all fail with messages that name a symptom several layers from the cause. Minimum supported Gradle version is X is not really about Gradle: an Android Gradle Plugin version pins a Gradle version, which pins a JDK, and a plugin you added last week moved one of the three. Unresolved reference after adding a dependency usually means the dependency resolved for one source set and not the one you are editing.
The productive move with all three tools is the same: ask the resolver what it actually decided, instead of reading the failure and guessing.
dependencyInsight stays fixed, because you changed the constraint that produced it.setState in the callback. If the user leaves the screen before the callback fires, the State object is already disposed and the call throws. It looks intermittent because it depends on whether the user is faster than the network. Cancel the subscription or timer in dispose(), or guard with if (!mounted) return; before touching state — and prefer cancellation, because the guard stops the crash but not the wasted work.lateinit only when the value is genuinely assigned before any read and the lifecycle guarantees it — a view binding assigned in onCreate, a dependency injected at construction. If there is any path where the field may legitimately be absent, a nullable type is the honest declaration and the compiler will force you to handle it. lateinit trades a compile-time check for a runtime exception; that is only a good trade when the runtime guarantee is real.google-services.json, the Google Services Gradle plugin not applied, or a call from Application.onCreate that runs before FirebaseApp.initializeApp(). It is a startup ordering problem, not a credentials problem, which is why re-downloading the config file so often changes nothing.@Published property is publishing from the wrong thread, and SwiftUI traps it. Mark the owning type @MainActor, or hop explicitly with await MainActor.run { ... }. Dispatching to DispatchQueue.main works too, but the actor annotation makes the requirement part of the type rather than something each caller must remember.build with your new code but keeps existing state, so it cannot apply changes to main(), global or static initialisers, enum and generic type declarations, or native code. Those need a hot restart or a full rebuild. If a change appears to be ignored, check whether it lives in one of those places before assuming the tool is broken.Every tutorial starts with a plain-English analogy — then real code, then interview questions.
Browse Mobile Tutorials →