# Flutter and Android Lifecycle Explained: initState, didChangeAppLifecycleState and More


If you've come to Flutter from native Android, one of the first things you'll notice is that the lifecycle methods you relied on — `onCreate`, `onResume`, `onPause`, `onStop`, `onDestroy` — don't exist in the same form. Flutter manages lifecycle differently, and understanding the mapping between the two systems is essential for building apps that handle background/foreground transitions, resource cleanup, and state restoration correctly.

---

## Android's Activity Lifecycle

Android apps are built around the `Activity` class, and the OS communicates state changes through a set of well-defined callbacks:

| Method | When it fires |
|--------|--------------|
| `onCreate()` | Activity is first created |
| `onStart()` | Activity becomes visible |
| `onResume()` | User can interact with the activity |
| `onPause()` | Activity is partially obscured or losing focus |
| `onStop()` | Activity is no longer visible |
| `onDestroy()` | Activity is being destroyed |

These fire in sequence as the user navigates into and out of the activity. The most commonly used pair is `onResume` and `onPause` — when the app comes to the foreground and when it's about to leave it.

---

## Flutter's Widget Lifecycle

Flutter replaces the activity-centric model with a widget-centric one. A `StatefulWidget` goes through its own lifecycle:

**`initState()`** — called once when the widget is inserted into the tree. This is the equivalent of `onCreate`. Initialize controllers, set up listeners, and fetch initial data here.

```dart
@override
void initState() {
  super.initState();
  _controller = AnimationController(vsync: this);
  _loadData();
}
```

**`didChangeDependencies()`** — called immediately after `initState` and again whenever the widget's inherited dependencies change. Use this when setup depends on `InheritedWidget` values like `Theme` or `MediaQuery`.

**`build()`** — called whenever Flutter needs to render the widget. This can be called many times, so keep it free of side effects.

**`didUpdateWidget()`** — called when the parent rebuilds and passes new configuration to this widget. Useful for reacting to external prop changes.

**`dispose()`** — called when the widget is permanently removed from the tree. This is where you release resources — cancel timers, close streams, dispose controllers. The equivalent of `onDestroy`.

```dart
@override
void dispose() {
  _controller.dispose();
  _subscription.cancel();
  super.dispose();
}
```

---

## Observing App-Level Lifecycle in Flutter

Widget lifecycle handles what happens to a specific screen. But for app-level events — the user pressing home, switching apps, receiving a phone call — Flutter uses `WidgetsBindingObserver`.

Add the mixin to your `StatefulWidget`'s state class, register it in `initState`, and override `didChangeAppLifecycleState`:

```dart
class _HomeState extends State<HomeScreen> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.resumed:
        // App is in foreground and interactive
        break;
      case AppLifecycleState.inactive:
        // App is losing focus (e.g., phone call, app switcher)
        break;
      case AppLifecycleState.paused:
        // App is in background and not visible
        break;
      case AppLifecycleState.detached:
        // Engine is running but no views attached
        break;
      case AppLifecycleState.hidden:
        // All views are hidden (added in newer Flutter versions)
        break;
    }
  }
}
```

Always call `removeObserver` in `dispose`. Failing to do so causes memory leaks — the observer will continue receiving callbacks even after the widget is gone.

---

## Mapping Android to Flutter

| Android | Flutter equivalent | When |
|---------|-------------------|------|
| `onCreate` | `initState` | Widget first created |
| `onStart` / `onResume` | `AppLifecycleState.resumed` | App comes to foreground |
| `onPause` | `AppLifecycleState.inactive` | App losing focus |
| `onStop` | `AppLifecycleState.paused` | App goes to background |
| `onDestroy` | `dispose` | Widget permanently removed |

One important nuance: Flutter's state names don't map one-to-one to Android's names. When Android calls `Activity.onPause`, Flutter enters `inactive` — not `paused`. When Android calls `Activity.onStop`, Flutter enters `paused`. This mismatch trips up developers coming from native Android, so it's worth keeping this table close.

---

## Practical Use Cases

**Pause video playback when the app goes to the background:**

```dart
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
  if (state == AppLifecycleState.paused) {
    _videoController.pause();
  } else if (state == AppLifecycleState.resumed) {
    _videoController.play();
  }
}
```

**Release the camera when the app is not visible:**

```dart
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
  if (state == AppLifecycleState.inactive) {
    _cameraController.dispose();
  } else if (state == AppLifecycleState.resumed) {
    _initCamera();
  }
}
```

**Refresh stale data when returning to the foreground:**

```dart
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
  if (state == AppLifecycleState.resumed) {
    _refreshUserSession();
  }
}
```

---

## Using the Newer AppLifecycleListener

Flutter also provides `AppLifecycleListener`, a newer class that offers named callbacks for each transition rather than a single switch statement. For new code, this tends to be cleaner:

```dart
late final AppLifecycleListener _listener;

@override
void initState() {
  super.initState();
  _listener = AppLifecycleListener(
    onResume: () => _onResumed(),
    onPause: () => _onPaused(),
    onInactive: () => _onInactive(),
    onDetach: () => _onDetached(),
  );
}

@override
void dispose() {
  _listener.dispose();
  super.dispose();
}
```

The behavior is the same as `WidgetsBindingObserver`, but the API is more explicit and easier to read at a glance.

---

## Common Mistakes

**Forgetting to remove the observer.** If you add an observer in `initState` but don't remove it in `dispose`, the widget keeps receiving callbacks after it's been removed from the tree. This can cause exceptions or unexpected behavior.

**Doing heavy work in `build`.** Unlike `onCreate`, `build` can be called many times. Database queries, network calls, and expensive computations don't belong there — they belong in `initState` or triggered by state changes.

**Treating `inactive` like `paused`.** `inactive` fires for brief interruptions — phone calls, the notification shade, the app switcher — where the app is still partially visible. `paused` is when the app has genuinely moved to the background. The right action (pause media vs. release camera) depends on which state you're actually in.

---

## Summary

Flutter's lifecycle is widget-first rather than activity-first, but all the same events exist — they're just expressed differently. `initState` and `dispose` bookend a widget's life. `WidgetsBindingObserver` and `AppLifecycleListener` handle app-level foreground/background transitions. Keep the Android-to-Flutter mapping in mind, always clean up in `dispose`, and use the named callbacks in `AppLifecycleListener` for clarity in new code.

