Migration to Flutter Guide
Discover our battle-tested 21-step framework for a smooth and successful migration to Flutter!
Home
Glossary

initState in Flutter

What is initState in Flutter?

initState is a Flutter method on State that runs exactly once, immediately after a StatefulWidget's State object is created, used for one-time setup like creating controllers or starting a subscription.

It's part of the broader Widget Lifecycle in Flutter; this page focuses specifically on initState itself, including the async question that comes up constantly: initState's signature is void initState(), so it can never be marked async directly.

Key facts at a glance

  • initState in Flutter runs exactly once per State object, before the first build().
  • Its signature is void initState() – it cannot return a Future, so it can never be marked async.
  • An override must call super.initState()
  • BuildContext.dependOnInheritedWidgetOfExactType should not be called from initState. If a dependency on an inherited widget is needed, didChangeDependencies() should be used instead, as it is called immediately after initState().
  • Calling setState in initState is usually unnecessary, because the framework will call build() after initState() completes.

Can I use async or await in initState in Flutter?

Not directly on initState itself. A common approach is to call a separate async method without awaiting it inside initState.

import 'package:flutter/material.dart';

class ProfileScreen extends StatefulWidget {
  const ProfileScreen({super.key});

  @override
  State<ProfileScreen> createState() => _ProfileScreenState();
}

class _ProfileScreenState extends State<ProfileScreen> {
  String? _name;

  @override
  void initState() {
    super.initState();
    _loadProfile();
  }

  Future<void> _loadProfile() async {
    final name = await fetchUserName();
    if (!mounted) return;
    setState(() => _name = name);
  }

  @override
  Widget build(BuildContext context) {
    return Text(_name ?? 'Loading…');
  }
}

_loadProfile() is called without await from initState(). The method starts executing synchronously and continues asynchronously after reaching await. Once the operation completes, it checks mounted before calling setState.

It’s tempting to write void initState() async { … } directly and await inside it - and Dart's compiler won’t stop you, since a void function is allowed to be async. Flutter’s framework does, though: it checks whether initState() returns a Future and throws the error "State.initState() must be a void method without an async keyword" the moment the widget builds. That check only runs when assertions are enabled, so don't rely on it as a safety net in every build mode - treat "never mark initState async" as the rule regardless.

Best practices

  1. Extract async work into its own method and call it, unawaited, from initState – never try to mark initState itself async.
  2. Check mounted before calling setState after an await – the State may be disposed while the asynchronous operation is in progress.
  3. Perform inherited-widget dependency lookups, such as Theme.of(context), in didChangeDependencies rather than initState – BuildContext.dependOnInheritedWidgetOfExactType should not be called from initState.
  4. Keep initState synchronous and fast – create controllers, initialize fields, and start asynchronous work, but don't wait for its results there.
  5. Initialize state that depends on the widget's configuration in initState – values derived from the widget's initial properties can be assigned once here, while values that need to react to later changes should be handled through the appropriate lifecycle methods.

Common mistakes

  • Declaring initState as async. Flutter's framework detects the returned Future when the widget first builds and throws a FlutterError - asynchronous work should be moved to a separate async method instead.
  • Trying to await an async call directly inside initState. Since initState should not be declared async, asynchronous work started there cannot be awaited. Instead, call a separate async method without awaiting it.
  • Calling setState after an await without checking mounted. The State may have been disposed before the asynchronous operation completes, making the subsequent setState invalid.
  • Doing Theme.of(context) or similar inherited-widget lookups in initState. BuildContext.dependOnInheritedWidgetOfExactType should not be called from initState. Such dependencies should be obtained in didChangeDependencies, which is called immediately after initState.

Learn more

Design system Flutter

Building a Design System in a Large Flutter App

Flutter is known for its seamless UI/UX features and robust design elements. It allowed us to deliver a unique customer experience in the “CA24 Mobile” banking app. This article shares some of the learnings and pain points behind implementing it in this large project.

12 Flutter & Dart Code Hacks
by LeanCode

24 Flutter & Dart Code Hacks & Best Practices – How to Write Better Code?

In this article, we’re sharing LeanCode’s 24 practical Flutter and Dart patterns that help you write less boilerplate, make your code cleaner, and catch mistakes earlier. Apply these patterns and you'll find yourself coding faster, communicating more clearly with your teammates, and spending less time debugging issues that the compiler could have caught.

Is Flutter good for app development?

Flutter Pros and Cons: Why Choose Flutter in 2025?

Flutter is loved by many for its simple and fast development of high-quality cross-platform apps. Let’s take a closer look if Flutter is a suitable solution in every case, i.e., when developing mobile, web, and desktop applications.