← Back to list

StatelessWidget vs StatefulWidget in Flutter: A Complete Guide from Basics to Advanced

Understand the differences between StatelessWidget and StatefulWidget, when to use each one, how Flutter rebuilds widgets, and the best…

nisarg ratani · 2026-07-13 05:34 · 2 claps · 5.0 min read
#flutter #flutter-ui #mobile-app-development #flutter-stateless-widget #flutter-stateful-widget
Open on Medium ↗
Wiki topics: 📱 · Mobile Development

StatelessWidget vs StatefulWidget in Flutter: A Complete Guide from Basics to Advanced

Understand the differences between StatelessWidget and StatefulWidget, when to use each one, how Flutter rebuilds widgets, and the best practices for building maintainable Flutter applications.

Introduction

One of the very first questions every Flutter developer encounters is:

Should I use StatelessWidget or StatefulWidget?

At first glance, they seem similar because both display UI. However, they serve very different purposes.

Choosing the right widget affects:

  • Performance
  • Code readability
  • Maintainability
  • State management
  • Application architecture

In this article, we’ll explore both widgets in depth — from the basics to advanced concepts — with practical examples and real-world scenarios.

What is a Widget?

In Flutter, everything is a widget.

Buttons, text, images, layouts, pages, and even the application itself are widgets.

Example:

Text("Hello Flutter")

This Text widget is immutable. If you want to display different text, Flutter creates a new widget configuration rather than modifying the existing one.

There are two primary types of widgets you’ll use:

  • StatelessWidget
  • StatefulWidget

Understanding StatelessWidget

A StatelessWidget is a widget whose UI does not change after it is built.

In other words:

  • No internal mutable state
  • UI depends only on constructor parameters
  • The widget rebuilds only when its parent provides new data

Think of it as a printed photograph.

Unless someone replaces the photograph, it always looks the same.

Creating a StatelessWidget

class WelcomeText extends StatelessWidget {
  const WelcomeText({super.key});
  @override
  Widget build(BuildContext context) {
    return const Text(
      "Welcome to Flutter",
    );
  }
}

The widget simply describes the UI.

When Should You Use StatelessWidget?

Use a StatelessWidget whenever the UI doesn't need to change because of internal state.

Examples include:

  • Text labels
  • Icons
  • Static images
  • Logos
  • Dividers
  • Simple cards
  • Reusable UI components
  • Buttons whose behavior is handled externally

Example:

class UserAvatar extends StatelessWidget {
  final String imageUrl;
  const UserAvatar({
    super.key,
    required this.imageUrl,
  });
  @override
  Widget build(BuildContext context) {
    return CircleAvatar(
      backgroundImage: NetworkImage(imageUrl),
    );
  }
}

The widget displays whatever image is passed to it but doesn’t change the image by itself.

Understanding StatefulWidget

A StatefulWidget is a widget whose UI can change over time.

It stores mutable data inside a separate State object.

Whenever the state changes, Flutter rebuilds the widget.

Think of it as a digital clock.

Every second, the displayed time changes.

Creating a StatefulWidget

class CounterPage extends StatefulWidget {
  const CounterPage({super.key});
  @override
  State<CounterPage> createState() {
    return _CounterPageState();
  }
}

class _CounterPageState extends State<CounterPage> {
  int counter = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text("$counter"),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            counter++;
          });
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

Every time setState() is called, Flutter schedules a rebuild for this widget.

How StatefulWidget Works

Unlike a StatelessWidget, a StatefulWidget consists of two separate classes.

StatefulWidget
      │
      ▼
State Object
      │
      ▼
Build Method

The widget itself is immutable.

The mutable data lives inside the State object.

This separation allows Flutter to preserve state across rebuilds.

Comparing StatelessWidget and StatefulWidget

FeatureStatelessWidgetStatefulWidgetMutable state❌ No✅ YesCan update UI internally❌ No✅ YesUses setState()❌ No✅ YesLifecycle methodsLimitedRich lifecycleStores mutable data❌ No✅ YesSuitable forStatic UIDynamic UI

Real-World Examples

Example 1: Company Logo

Image.asset("assets/logo.png")

The logo never changes.

Use:

StatelessWidget

Example 2: Counter

0
↓
1
↓
2
↓
3

The value changes.

Use:

StatefulWidget

Example 3: Login Screen

The screen contains:

  • TextFields
  • Loading indicator
  • Error messages
  • Password visibility toggle

These values change.

A login screen is commonly implemented as a StatefulWidget or, in larger applications, a StatelessWidget connected to an external state management solution such as Riverpod, Bloc, or Provider.

What Happens When setState() is Called?

Consider this code:

setState(() {
  counter++;
});

Flutter performs these steps:

setState()
↓
Mark widget dirty
↓
Schedule rebuild
↓
Call build()
↓
Compare old and new widget trees
↓
Update only necessary UI

Flutter does not redraw the entire application.

Only the affected portion of the widget tree is rebuilt.

Understanding Rebuilds

Suppose your widget tree looks like this:

Scaffold
│
├── AppBar
├── Counter
├── Button
└── Image

When the counter changes:

Scaffold
│
├── AppBar
├── Counter   ← Rebuilt
├── Button
└── Image

The image and app bar can often be reused because they haven’t changed.

Flutter is highly optimized for this process.

StatelessWidget Can Still Rebuild

A common misconception is:

Stateless widgets never rebuild.

This is incorrect.

A StatelessWidget rebuilds whenever:

  • Its parent rebuilds.
  • Its constructor parameters change.
  • An inherited dependency (such as Theme or MediaQuery) changes.

Example:

class Greeting extends StatelessWidget {
  final String name;
  const Greeting({
    super.key,
    required this.name,
  });
  @override
  Widget build(BuildContext context) {
    return Text("Hello $name");
  }
}

If the parent passes a different name, Flutter rebuilds the widget.

StatefulWidget Lifecycle

A StatefulWidget has several lifecycle methods.

createState()
↓
initState()
↓
didChangeDependencies()
↓
build()
↓
didUpdateWidget()
↓
deactivate()
↓
dispose()

The most commonly used methods are:

initState()

Called once when the state is created.

Useful for:

  • API calls
  • Animation controllers
  • Stream subscriptions
  • Initialising variables

didChangeDependencies()

Called when an inherited dependency changes.

Useful when your widget depends on objects like Theme, MediaQuery, or localization.

build()

Builds the UI.

Should be kept fast and free of heavy business logic.

dispose()

Called when the widget is permanently removed.

Use it to clean up resources.

Example:

@override
void dispose() {
  controller.dispose();
  super.dispose();
}

Common Stateful Widgets

Examples include:

  • Checkbox
  • Switch
  • Radio button
  • Slider
  • TextField
  • PageView
  • TabBar
  • Animation widgets
  • Video player
  • Audio player

These widgets change their appearance or behavior based on state.

Why Keep Widgets Small?

Instead of writing:

class HomePage extends StatefulWidget {
  ...
}

with hundreds of lines inside one build() method, split the UI into reusable widgets.

Example:

Scaffold(
  appBar: const HomeAppBar(),
  body: const HomeBody(),
  bottomNavigationBar: const HomeNavigationBar(),
)

Benefits include:

  • Better readability
  • Easier testing
  • Improved code reuse
  • Reduced rebuild scope

Modern Flutter Approach

Years ago, many Flutter screens were implemented as StatefulWidgets.

Today, it’s common to use:

  • Riverpod
  • Bloc
  • Provider
  • Cubit

These libraries manage state outside the widget.

Example with Riverpod:

class HomeScreen extends ConsumerWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final counter = ref.watch(counterProvider);
    return Text("$counter");
  }
}

Even though the UI updates dynamically, the widget itself remains stateless because the state is managed externally.

This approach improves testability and separates UI from business logic.

Performance Considerations

Many developers assume:

StatelessWidget is always faster.

The reality is more nuanced.

Flutter efficiently rebuilds widgets regardless of whether they are stateless or stateful. The primary performance considerations are:

  • Avoid unnecessary rebuilds.
  • Keep widgets small.
  • Use const constructors where possible.
  • Separate frequently changing UI from static UI.
  • Profile performance before optimizing.

Choosing the correct widget type based on your requirements is more important than trying to optimize prematurely.

Common Mistakes

❌ Using StatefulWidget for every screen.

❌ Storing business logic inside build().

❌ Calling setState() for unrelated UI changes.

❌ Forgetting to call dispose() for controllers.

❌ Performing API calls inside build().

❌ Creating large widgets with hundreds of lines of code.

Best Practices

  • Prefer StatelessWidget when the widget has no internal mutable state.
  • Use StatefulWidget only when local state is genuinely needed.
  • Keep the build() method focused on describing the UI.
  • Dispose of controllers, animations, and streams properly.
  • Split large widgets into smaller reusable components.
  • Use external state management for complex applications.
  • Mark widgets as const whenever possible.

When Should You Choose Each?

ScenarioRecommended WidgetStatic textStatelessWidgetCompany logoStatelessWidgetProduct cardStatelessWidgetCounterStatefulWidgetTimerStatefulWidgetForm with validationStatefulWidget (or external state management)Loading indicatorStatefulWidget (or external state management)API-driven screenStatelessWidget + Riverpod/Bloc/ProviderEnterprise applicationMostly StatelessWidgets with external state management

Summary

StatelessWidget and StatefulWidget are the building blocks of every Flutter application.

A StatelessWidget is ideal for UI that depends only on input parameters and doesn't manage its own mutable state. A StatefulWidget is designed for UI that changes over time because of local state.

As Flutter applications grow, you’ll often find that many screens can remain stateless by moving state management into dedicated solutions like Riverpod or Bloc. This keeps widgets focused on presentation while business logic lives elsewhere.

Understanding when and why to use each widget type is a fundamental step toward writing clean, scalable, and maintainable Flutter applications.


메타데이터
post_id
bb8e2d4b0402
slug
statelesswidget-vs-statefulwidget-in-flutter-a-complete-guide-from-basics-to-advanced-bb8e2d4b0402
url
https://medium.com/@nisargratani/statelesswidget-vs-statefulwidget-in-flutter-a-complete-guide-from-basics-to-advanced-bb8e2d4b0402
canonical_url
https://medium.com/@nisargratani/statelesswidget-vs-statefulwidget-in-flutter-a-complete-guide-from-basics-to-advanced-bb8e2d4b0402
author_url
https://medium.com/@nisargratani
status
ok
fetched_at
2026-08-21 22:58:37