Home› Mobile› Complete Guide
Complete Guide

Complete Mobile Tutorial

This complete guide covers all 20 Mobile tutorials on TheCodeForge, organised by topic.

Learning Roadmap
Beginner → Build a strong foundation
Intermediate → Deepen your understanding with practical topics
Advanced → Master advanced concepts and real-world applications
20
Topics
10
Beginner
10
Intermediate
0
Advanced
Jump to section
Flutter (7)Kotlin (5)Android (3)Swift (3)Xcode (2)

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.

The four moments where mobile code breaks

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.

MomentWhat the platform is doingErrors it produces
Before first buildFields declared but not yet assigned; dependency graph not wiredLateInitializationError, Kotlin lateinit not initialized, Default FirebaseApp is not initialized
During build / renderLayout constraints resolved; a parent hands a child a boxRenderFlex overflowed, Angular-style binding errors, index-out-of-range on a list still loading
After teardownWidget or view controller destroyed, but async work is still in flight and holds a referencesetState() called after dispose(), Kotlin JobCancellationException, SwiftUI publishing from a background thread
At build / toolchain timeGradle, CocoaPods or SPM resolving versions and modulesGradle 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.

Null safety is a contract, and the platform boundary breaks it

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.

kotlin
// Java: @Nullable is absent, so Kotlin sees String! (platform type)
//   public String getDisplayName() { return null; }

val name: String = user.displayName   // compiles. NPE at runtime.

// Make the boundary explicit and the compiler starts helping again:
val name: String? = user.displayName        // honest type
val shown: String = name ?: "Anonymous"     // decision at the edge

// Or reject bad data once, loudly, where it enters:
val name = requireNotNull(user.displayName) { "displayName missing for ${user.id}" }
In practiceAnnotate the Java side with @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.

Layout errors are arithmetic, not taste

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.

IntentWidgetBehaviour
Text should shrink to fit the rowExpanded / FlexibleChild is given the remaining main-axis space and wraps or ellipsises inside it
Items should flow onto a second lineWrapLays out children in runs, starting a new run when the current one is full
Content is legitimately longer than the screenSingleChildScrollView / ListViewGives the axis unbounded extent and scrolls it
Only part of a long label mattersText(overflow: TextOverflow.ellipsis)Truncates visibly, so the user knows text was cut

Build-system failures: read the resolver, not the error

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.

bash
# Android: what does the plugin/Gradle/JDK triangle look like right now?
./gradlew --version
./gradlew :app:dependencies --configuration debugRuntimeClasspath

# Why is this version of a library here at all?
./gradlew :app:dependencyInsight --configuration debugRuntimeClasspath \
  --dependency okhttp

# Flutter: the doctor names toolchain breakage before the build does
flutter doctor -v
flutter pub deps

# iOS: resolve and report, rather than re-running the failing build
pod install --repo-update
xcodebuild -resolvePackageDependencies -showBuildSettings | grep -i swift_version
In practiceA version conflict that you fix by pinning a number you found in a forum post will come back the next time anyone touches the dependency block. A version conflict you fix after reading dependencyInsight stays fixed, because you changed the constraint that produced it.

Frequently Asked Questions

Why does my Flutter app crash with setState after dispose only sometimes?
Because it is a race. The widget schedules async work — a network call, a timer, a stream subscription — and calls 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.
Should I use lateinit in Kotlin or a nullable field?
Use 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.
What does 'Default FirebaseApp is not initialized' actually mean?
That some Firebase API was called before initialization completed. The usual causes are a missing 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.
Why does SwiftUI crash with 'Publishing changes from background threads'?
Because SwiftUI's view state must be mutated on the main actor. A network completion handler or a background queue that assigns to an @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.
Is Flutter hot reload supposed to pick up every change?
No, and knowing the boundary saves a lot of confusion. Hot reload re-runs 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.
Which of these should I learn first if I am coming from backend work?
Start with the lifecycle of one platform, not the syntax of three. The language differences between Dart, Kotlin and Swift are shallow; the thing that will actually break your code is not knowing when your objects are created and destroyed. Pick Flutter or native Android, read the teardown errors in this track first, and the rest of the track will read like variations on a theme you already know.

Flutter

Kotlin

Android

Swift

Xcode

Also Explore
Java 257 tutorials → JavaScript 185 tutorials → System Design 145 tutorials → Interview 77 tutorials → Frontend 13 tutorials → Testing 12 tutorials →
Start from the beginning

Every tutorial starts with a plain-English analogy — then real code, then interview questions.

Browse Mobile Tutorials →