In initState function in StatefulWidget in Flutter, should super.initState() be called before any function or after any function?

#initstate#lifecycle#statefulwidget#best-practices

Answer

Quick Answer

text
super.initState()
must be called FIRST — before any other code in
text
initState()
.

dart

void initState() {
  super.initState();  // ✅ FIRST - Always call this first

  // Then your initialization code
  _controller = AnimationController(vsync: this);
  _fetchData();
}

Why Call super.initState() First?

Reason 1: Framework Initialization

The

text
super.initState()
call:

  • Initializes the widget's internal state
  • Sets up the widget's relationship with the widget tree
  • Prepares the
    text
    BuildContext
    for use
  • Registers the widget with the framework

If you don't call it first:

dart

void initState() {
  _fetchData(); // ❌ Might fail - widget not fully initialized
  super.initState(); // ❌ Too late!
}

Reason 2: BuildContext Availability

dart

void initState() {
  super.initState(); // ✅ First - initializes context

  // Now context is safe to use
  final theme = Theme.of(context); // ✅ Works
  final mediaQuery = MediaQuery.of(context); // ✅ Works
}

Wrong order:

dart

void initState() {
  final theme = Theme.of(context); // ❌ Context not ready
  super.initState(); // ❌ Too late
}

Official Documentation

From Flutter's StatefulWidget documentation:

"If you override this method, make sure to call

text
super.initState()
as the first line."

This is a hard requirement, not a suggestion.


Correct Pattern

✅ Correct Example

dart
class MyWidget extends StatefulWidget {
  
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  late AnimationController _controller;
  late Future<List<User>> _usersFuture;
  Timer? _timer;

  
  void initState() {
    super.initState();  // ✅ FIRST LINE

    // Initialize controllers
    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 2),
    );

    // Fetch data
    _usersFuture = _fetchUsers();

    // Start timer
    _timer = Timer.periodic(Duration(seconds: 1), (timer) {
      print('Tick ${timer.tick}');
    });

    // Add listeners
    _controller.addListener(() {
      print('Animation: ${_controller.value}');
    });
  }

  Future<List<User>> _fetchUsers() async {
    // API call
    return [];
  }

  
  void dispose() {
    _controller.dispose();
    _timer?.cancel();
    super.dispose(); // Dispose is opposite - call last
  }

  
  Widget build(BuildContext context) {
    return Container();
  }
}

❌ Wrong Example

dart

void initState() {
  // ❌ WRONG - initialization before super.initState()
  _controller = AnimationController(vsync: this);
  _fetchData();

  super.initState(); // ❌ Too late!
}

Why it's wrong:

  • Widget not fully initialized yet
  • text
    BuildContext
    might not be ready
  • Framework state not set up
  • Can cause runtime errors or crashes

Common Use Cases

1. Initializing Controllers

dart

void initState() {
  super.initState(); // ✅ First

  // Initialize animation controller
  _animationController = AnimationController(
    vsync: this,
    duration: Duration(milliseconds: 500),
  );

  // Initialize text controller
  _textController = TextEditingController(text: 'Initial value');

  // Initialize scroll controller
  _scrollController = ScrollController();
}

2. Fetching Data

dart

void initState() {
  super.initState(); // ✅ First

  // Fetch data when widget loads
  _fetchUserData();
}

Future<void> _fetchUserData() async {
  final response = await http.get(Uri.parse('https://api.example.com/user'));
  setState(() {
    _userData = jsonDecode(response.body);
  });
}

3. Setting Up Listeners

dart

void initState() {
  super.initState(); // ✅ First

  // Add listener to animation controller
  _controller.addListener(() {
    setState(() {
      _progress = _controller.value;
    });
  });

  // Add listener to text field
  _textController.addListener(() {
    print('Text changed: ${_textController.text}');
  });
}

4. Starting Timers

dart

void initState() {
  super.initState(); // ✅ First

  // Start periodic timer
  _timer = Timer.periodic(Duration(seconds: 5), (timer) {
    _refreshData();
  });

  // One-time delayed action
  Future.delayed(Duration(seconds: 2), () {
    setState(() {
      _showWelcome = true;
    });
  });
}

5. Accessing Widget Properties

dart
class UserProfile extends StatefulWidget {
  final String userId;
  final bool autoRefresh;

  UserProfile({required this.userId, this.autoRefresh = false});

  
  _UserProfileState createState() => _UserProfileState();
}

class _UserProfileState extends State<UserProfile> {
  
  void initState() {
    super.initState(); // ✅ First

    // Access widget properties via widget.propertyName
    print('User ID: ${widget.userId}');

    // Fetch user data
    _fetchUser(widget.userId);

    // Conditional initialization
    if (widget.autoRefresh) {
      _startAutoRefresh();
    }
  }

  void _fetchUser(String userId) {
    // Fetch user data
  }

  void _startAutoRefresh() {
    Timer.periodic(Duration(seconds: 30), (timer) {
      _fetchUser(widget.userId);
    });
  }

  
  Widget build(BuildContext context) {
    return Container();
  }
}

What Happens If You Don't Call super.initState()?

Analyzer Warning

dart

void initState() {
  // Missing super.initState()
  _controller = AnimationController(vsync: this);
}

// ⚠️ Analyzer warning:
// "Missing call to 'super.initState()'"

Runtime Issues

Potential problems:

  • Widget not properly initialized
  • BuildContext not fully set up
  • Framework state inconsistencies
  • Unexpected crashes or errors
  • Memory leaks

initState vs dispose Order

Key difference:

Methodsuper Call PlacementReason
text
initState()
FIRST (before your code)Framework must initialize first
text
dispose()
LAST (after your code)Clean up your resources before framework cleanup

Correct Pattern

dart
class _MyWidgetState extends State<MyWidget> {
  late AnimationController _controller;
  late Timer _timer;

  
  void initState() {
    super.initState();  // ✅ FIRST - Framework initializes

    _controller = AnimationController(vsync: this);
    _timer = Timer.periodic(Duration(seconds: 1), (_) {});
  }

  
  void dispose() {
    // ✅ Clean up your resources first
    _controller.dispose();
    _timer.cancel();

    super.dispose();  // ✅ LAST - Framework cleans up
  }

  
  Widget build(BuildContext context) {
    return Container();
  }
}

Best Practices

✅ DO

dart

void initState() {
  super.initState(); // ✅ First line always

  // Initialize in logical order
  _initializeControllers();
  _fetchData();
  _setupListeners();
}

❌ DON'T

dart

void initState() {
  _setupEverything(); // ❌ Don't call functions before super
  super.initState(); // ❌ Too late
}
dart

void initState() {
  // ❌ Don't forget super.initState() entirely
  _controller = AnimationController(vsync: this);
}
dart

void initState() {
  super.initState();
  setState(() {}); // ❌ Don't call setState in initState
  // Use addPostFrameCallback instead
}

When You Need to Call setState in initState

Problem: Can't call

text
setState()
directly in
text
initState()
.

Solution: Use

text
addPostFrameCallback
:

dart

void initState() {
  super.initState();

  WidgetsBinding.instance.addPostFrameCallback((_) {
    // This runs after the first frame is rendered
    setState(() {
      _isReady = true;
    });
  });
}

Summary

Rule: Always call

text
super.initState()
as the first line in your
text
initState()
method.

Why: The framework needs to initialize the widget before you can safely use it.

Opposite: In

text
dispose()
, call
text
super.dispose()
as the last line (after cleaning up your resources).

Required: Not optional — Flutter will show an analyzer warning if you forget.


Resources