← Back to blog Khalil Drissi

Getting started with Flutter for Android and iOS

Listen to article
0:00

I came to Flutter after years of maintaining separate Android and iOS codebases that drifted apart no matter how disciplined the team was. One framework, one language, two stores. That promise sounded too good, so I shipped a real app with it before deciding. It held up. Here is how I get a project running and what I wish someone had told me on day one.

Installing the toolchain

The install is heavier than people admit. You need the Flutter SDK, but you also need a full Android Studio install for the Android SDK and an emulator, and on a Mac you need Xcode plus the command line tools for iOS. Skip any of those and you only get half the platform. After installing, run the doctor command and fix every warning before writing a line of code.

flutter doctor -v
flutter create my_app
cd my_app
flutter run -d all

The doctor command is the most useful thing in the SDK. It checks your Android licenses, your Xcode setup, your CocoaPods version, and whether a device is connected. I run it whenever something behaves strangely, because nine times out of ten the problem is environmental rather than in my code.

Understanding the widget tree

Everything in Flutter is a widget. Padding is a widget. Alignment is a widget. This feels absurd for the first week and then it clicks. Instead of setting properties on a view, you wrap widgets in other widgets, and the nesting describes your layout. The framework rebuilds parts of the tree when state changes, and it is fast because it diffs against the previous tree rather than touching the native views directly.

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Hello')),
        body: const Center(child: Text('Running on both platforms')),
      ),
    );
  }
}

StatelessWidget is for things that never change after they are built. The moment you need a counter, a toggle, or any value that updates, you reach for StatefulWidget or a proper state solution. I cover that in detail in my post on Flutter state management, because picking the right approach early saves a painful refactor later.

Hot reload changes how you work

Hot reload is the feature that sold me. You save a file and the running app updates in under a second while keeping its current state. Tweaking a color, adjusting padding, fixing a layout bug becomes a tight loop with no rebuild. There is a difference between hot reload, which preserves state, and hot restart, which throws state away and reruns from scratch. When the UI looks wrong after a reload, a hot restart usually clears it.

Handling both platforms honestly

One codebase does not mean you can ignore the platforms. iOS users expect a back swipe and a certain feel for scrolling. Android users expect material ripples and a hardware back button. Flutter gives you both design languages, Material and Cupertino, and you can branch on the platform when it matters. I keep this branching small and centralised so it does not spread through the whole app.

import 'dart:io' show Platform;
import 'package:flutter/material.dart';

Widget adaptiveSpinner() {
  if (Platform.isIOS) {
    return const CupertinoActivityIndicator();
  }
  return const CircularProgressIndicator();
}

Project structure I settle on

The default template dumps everything in one file. That is fine for a demo and terrible for an app you maintain. I split code into folders by feature rather than by type, so a feature owns its screens, its models, and its logic in one place. This pays off the day you delete a feature and want it gone cleanly.

Once you have the structure, the next things to worry about are how the app talks to native APIs and how it stays smooth under load. I dig into native bridges in Flutter platform channels, and into keeping frame times low in Flutter performance optimization. Get the foundation right first, then layer those concerns on top.

What to build first

Do not start with your dream app. Build something small that touches a network call, a list, a detail screen, and local storage. That covers most of what a real app needs and exposes the rough edges of your setup while the stakes are low. By the time you have done that twice, the framework stops fighting you and starts disappearing into the background, which is exactly where a good tool belongs.

Comments
Leave a comment