Bird
Raised Fist0
Fluttermobile~20 mins

State management comparison in Flutter - Mini App: Build & Ship

Choose your learning style10 modes available
Build: Counter State Management Comparison
This screen shows a simple counter app implemented with three different state management methods: setState, Provider, and Riverpod. It helps beginners see how each method updates the UI when the button is pressed.
Target UI
-----------------------------------
| Counter State Management         |
|---------------------------------|
| setState Counter: 0              |
| [Increment]                     |
|                                 |
| Provider Counter: 0              |
| [Increment]                     |
|                                 |
| Riverpod Counter: 0              |
| [Increment]                     |
-----------------------------------
Display three counters labeled 'setState Counter', 'Provider Counter', and 'Riverpod Counter'.
Each counter starts at 0.
Each counter has its own Increment button below it.
Pressing a button increases only its own counter by 1 and updates the UI.
Use setState for the first counter.
Use Provider package for the second counter.
Use Riverpod package for the third counter.
Layout the counters vertically with some spacing.
Starter Code
Flutter
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(
    ProviderScope(
      child: MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (_) => ProviderCounter()),
        ],
        child: const MyApp(),
      ),
    ),
  );
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'State Management Comparison',
      home: const CounterScreen(),
    );
  }
}

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

  @override
  State<CounterScreen> createState() => _CounterScreenState();
}

class _CounterScreenState extends State<CounterScreen> {
  int setStateCounter = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Counter State Management Comparison')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // TODO: Add setState counter UI here
            // TODO: Add Provider counter UI here
            // TODO: Add Riverpod counter UI here
          ],
        ),
      ),
    );
  }
}

// TODO: Add ProviderCounter class here

// TODO: Add Riverpod counter provider here
Task 1
Task 2
Task 3
Task 4
Task 5
Solution
Flutter
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(
    ProviderScope(
      child: MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (_) => ProviderCounter()),
        ],
        child: const MyApp(),
      ),
    ),
  );
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'State Management Comparison',
      home: const CounterScreen(),
    );
  }
}

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

  @override
  State<CounterScreen> createState() => _CounterScreenState();
}

class _CounterScreenState extends State<CounterScreen> {
  int setStateCounter = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Counter State Management Comparison')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('setState Counter: $setStateCounter', style: const TextStyle(fontSize: 18)),
            ElevatedButton(
              onPressed: () => setState(() => setStateCounter++),
              child: const Text('Increment'),
            ),
            const SizedBox(height: 24),
            Consumer(builder: (context, watch, _) {
              final providerCounter = context.watch<ProviderCounter>();
              return Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text('Provider Counter: ${providerCounter.count}', style: const TextStyle(fontSize: 18)),
                  ElevatedButton(
                    onPressed: providerCounter.increment,
                    child: const Text('Increment'),
                  ),
                ],
              );
            }),
            const SizedBox(height: 24),
            Consumer(
              builder: (context, ref, _) {
                final riverpodCounter = ref.watch(riverpodCounterProvider);
                return Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text('Riverpod Counter: $riverpodCounter', style: const TextStyle(fontSize: 18)),
                    ElevatedButton(
                      onPressed: () {
                        ref.read(riverpodCounterProvider.notifier).state++;
                      },
                      child: const Text('Increment'),
                    ),
                  ],
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

class ProviderCounter extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

final riverpodCounterProvider = StateProvider<int>((ref) => 0);

This app shows three counters using different state management methods in Flutter.

1. setState: The simplest way. The counter is a local variable in the stateful widget. When the button is pressed, setState updates the UI.

2. Provider: Uses a ChangeNotifier class ProviderCounter to hold the count. The UI listens to changes and rebuilds when notifyListeners() is called.

3. Riverpod: Uses a StateProvider to hold the count. The UI watches the provider and updates automatically when the state changes.

This comparison helps beginners see how different methods manage and update state in Flutter apps.

Final Result
Completed Screen
-----------------------------------
| Counter State Management         |
|---------------------------------|
| setState Counter: 0              |
| [Increment]                     |
|                                 |
| Provider Counter: 0              |
| [Increment]                     |
|                                 |
| Riverpod Counter: 0              |
| [Increment]                     |
-----------------------------------
Tapping 'Increment' under 'setState Counter' increases only that counter by 1 and updates its number.
Tapping 'Increment' under 'Provider Counter' increases only that counter by 1 and updates its number.
Tapping 'Increment' under 'Riverpod Counter' increases only that counter by 1 and updates its number.
Stretch Goal
Add a Reset All button at the bottom that resets all three counters to zero.
💡 Hint
Use setState to reset the setState counter, call a reset method on ProviderCounter, and set the Riverpod provider state to 0.

Practice

(1/5)
1.

Which Flutter state management method is best for simple, local UI updates within a single widget?

easy
A. Using Bloc for complex event handling
B. Using the Provider package for global state
C. Using setState() inside a StatefulWidget
D. Using Riverpod for dependency injection

Solution

  1. Step 1: Understand the scope of setState()

    setState() is designed for small, local changes inside a StatefulWidget.
  2. Step 2: Compare with other methods

    Provider, Bloc, and Riverpod are better for shared or complex state across multiple widgets.
  3. Final Answer:

    Using setState() inside a StatefulWidget -> Option C
  4. Quick Check:

    Local UI updates = setState() [OK]
Hint: Use setState() for small widget-only changes [OK]
Common Mistakes:
  • Thinking Provider is simpler for local state
  • Using Bloc for tiny UI updates
  • Confusing Riverpod with setState()
2.

Which of the following is the correct syntax to update state using setState() in Flutter?

???
easy
A. setState(counter++);
B. setState { counter++; }
C. setState(=> counter++);
D. setState(() { counter++; });

Solution

  1. Step 1: Recall the setState() method signature

    setState() takes a function with no parameters that updates the state inside its body.
  2. Step 2: Check each option's syntax

    setState(() { counter++; }); correctly uses a function block. setState { counter++; } misses parentheses. setState(=> counter++); misses parentheses after setState. setState(counter++); passes a value, not a function.
  3. Final Answer:

    setState(() { counter++; }); -> Option D
  4. Quick Check:

    setState requires a function block [OK]
Hint: setState needs a function with braces () { } [OK]
Common Mistakes:
  • Omitting parentheses after setState
  • Passing expressions instead of functions
  • Using arrow syntax incorrectly
3.

Given this Flutter code snippet using Provider:

class Counter with ChangeNotifier {
  int value = 0;
  void increment() {
    value++;
    notifyListeners();
  }
}

// In widget:
final counter = Provider.of<Counter>(context);
counter.increment();
print(counter.value);

What will be printed after calling increment()?

medium
A. 1
B. An error occurs
C. null
D. 0

Solution

  1. Step 1: Understand the increment method

    The method increases value by 1 and calls notifyListeners() to update UI.
  2. Step 2: Check the printed value after increment

    Since value starts at 0 and increments once, the printed value is 1.
  3. Final Answer:

    1 -> Option A
  4. Quick Check:

    Provider state increments value to 1 [OK]
Hint: Increment then print shows updated value [OK]
Common Mistakes:
  • Forgetting notifyListeners() updates UI
  • Assuming value stays 0 after increment
  • Confusing Provider with setState()
4.

Identify the error in this Flutter Bloc usage snippet:

class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);
  void increment() {
    state++;
    emit(state);
  }
}
medium
A. Missing call to super.increment()
B. Cannot mutate state directly; it is immutable
C. emit() should not be called inside increment()
D. Cubit must extend Bloc, not Cubit

Solution

  1. Step 1: Understand Cubit state immutability

    The state property is immutable; you cannot change it directly.
  2. Step 2: Identify correct way to update state

    You must emit a new value, e.g., emit(state + 1);, instead of modifying state directly.
  3. Final Answer:

    Cannot mutate state directly; it is immutable -> Option B
  4. Quick Check:

    Cubit state is immutable, use emit(newValue) [OK]
Hint: Cubit state is immutable; never change state directly [OK]
Common Mistakes:
  • Trying to increment state variable directly
  • Calling super.increment() which doesn't exist
  • Confusing Cubit with Bloc inheritance
5.

You have a Flutter app with multiple widgets needing access to user login status and theme settings. Which state management approach is best and why?

hard
A. Use Provider or Riverpod to share state across widgets
B. Use setState() in each widget separately
C. Use local variables inside widgets without state management
D. Use Bloc only for UI animations

Solution

  1. Step 1: Analyze the need for shared state

    Login status and theme settings are shared data needed by multiple widgets.
  2. Step 2: Choose appropriate state management

    setState() is local and not suitable for sharing. Provider or Riverpod efficiently share and update state across widgets.
  3. Step 3: Exclude incorrect options

    Local variables don't update UI globally. Bloc is more complex and not just for animations.
  4. Final Answer:

    Use Provider or Riverpod to share state across widgets -> Option A
  5. Quick Check:

    Shared app state = Provider or Riverpod [OK]
Hint: Use Provider/Riverpod for shared app-wide state [OK]
Common Mistakes:
  • Using setState() for global shared data
  • Ignoring state management for shared info
  • Misusing Bloc only for animations
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard