Bird
Raised Fist0
Fluttermobile~20 mins

Riverpod overview in Flutter - Mini App: Build & Ship

Choose your learning style10 modes available
Build: Counter with Riverpod
A simple counter app that uses Riverpod to manage state. The app shows a number and a button to increase it.
Target UI
┌─────────────────────────┐
│       Counter App       │
├─────────────────────────┤
│                         │
│           0             │
│                         │
│      [+] Increment       │
│                         │
└─────────────────────────┘
Use Riverpod to create a provider for an integer counter state.
Display the current counter value in the center of the screen.
Add a button labeled '+' that increments the counter when tapped.
Use ConsumerWidget to listen to the provider and rebuild UI on changes.
Starter Code
Flutter
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

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

void main() {
  runApp(const ProviderScope(child: MyApp()));
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: CounterScreen(),
    );
  }
}

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

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // TODO: Read counterProvider and build UI
    return Scaffold(
      appBar: AppBar(title: const Text('Counter App')),
      body: const Center(child: Text('0')),
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        child: const Icon(Icons.add),
      ),
    );
  }
}
Task 1
Task 2
Task 3
Solution
Flutter
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

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

void main() {
  runApp(const ProviderScope(child: MyApp()));
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: CounterScreen(),
    );
  }
}

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

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final counter = ref.watch(counterProvider);

    return Scaffold(
      appBar: AppBar(title: const Text('Counter App')),
      body: Center(
        child: Text(
          '$counter',
          style: const TextStyle(fontSize: 48),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          ref.read(counterProvider.notifier).state++;
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

This app uses Riverpod to manage the counter state simply and clearly.

We create a StateProvider<int> called counterProvider that holds the current count.

Inside CounterScreen, which is a ConsumerWidget, we use ref.watch(counterProvider) to get the current count and rebuild the UI when it changes.

The counter value is shown in a large text in the center.

The floating action button increments the counter by accessing the provider's notifier and increasing its state.

This shows how Riverpod helps keep state management clean and reactive.

Final Result
Completed Screen
┌─────────────────────────┐
│       Counter App       │
├─────────────────────────┤
│                         │
│           0             │
│                         │
│      [+] Increment       │
│                         │
└─────────────────────────┘
Tapping the [+] button increases the number in the center by 1 each time.
The number updates immediately without needing to restart the app.
Stretch Goal
Add a reset button in the app bar to set the counter back to zero.
💡 Hint
Add an IconButton in the AppBar actions that calls ref.read(counterProvider.notifier).state = 0 when pressed.

Practice

(1/5)
1. What is the main purpose of Riverpod in Flutter apps?
easy
A. To manage app data and state clearly and safely
B. To create animations easily
C. To handle user input events
D. To design app layouts

Solution

  1. Step 1: Understand Riverpod's role

    Riverpod is a tool to manage state and data in Flutter apps.
  2. Step 2: Compare options

    Only To manage app data and state clearly and safely describes managing app data and state clearly and safely, which is Riverpod's main purpose.
  3. Final Answer:

    To manage app data and state clearly and safely -> Option A
  4. Quick Check:

    Riverpod = state management [OK]
Hint: Remember Riverpod is about state and data, not UI design [OK]
Common Mistakes:
  • Confusing Riverpod with UI or animation tools
  • Thinking Riverpod handles layouts or gestures
2. Which of the following is the correct way to declare a simple Riverpod provider for an integer value 0?
easy
A. final counterProvider = Provider((ref) => '0');
B. final counterProvider = Provider<int>((ref) => 0);
C. final counterProvider = Provider<int>((ref) => '0');
D. final counterProvider = Provider<int>((ref) => null);

Solution

  1. Step 1: Check provider type and value

    The provider is typed as int and returns 0, which matches.
  2. Step 2: Validate other options

    final counterProvider = Provider((ref) => '0'); returns a string '0' but no type specified; final counterProvider = Provider<int>((ref) => '0'); returns string '0' but typed int; final counterProvider = Provider<int>((ref) => null); returns null which is invalid for int.
  3. Final Answer:

    final counterProvider = Provider<int>((ref) => 0); -> Option B
  4. Quick Check:

    Provider<int> must return int value [OK]
Hint: Match provider type with returned value type exactly [OK]
Common Mistakes:
  • Returning wrong data type for the provider
  • Omitting type when needed
  • Returning null for non-nullable types
3. Given this Riverpod code snippet, what will be printed?
final greetingProvider = Provider((ref) => 'Hello');

class GreetingWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final greeting = ref.watch(greetingProvider);
    print(greeting);
    return Text(greeting);
  }
}
medium
A. Empty string
B. null
C. Error: greetingProvider not found
D. Hello

Solution

  1. Step 1: Understand provider value

    greetingProvider returns the string 'Hello'.
  2. Step 2: Check what ref.watch returns

    ref.watch(greetingProvider) gets 'Hello', so print outputs 'Hello'.
  3. Final Answer:

    Hello -> Option D
  4. Quick Check:

    ref.watch returns provider value [OK]
Hint: ref.watch returns the provider's current value [OK]
Common Mistakes:
  • Assuming ref.watch returns null or error
  • Confusing provider names
  • Forgetting provider returns a value
4. What is wrong with this Riverpod provider declaration?
final numberProvider = Provider<int>((ref) {
  return '42';
});
medium
A. The provider is missing a required parameter
B. The provider should be a StateProvider instead
C. The provider returns a string instead of an int
D. The provider declaration syntax is invalid

Solution

  1. Step 1: Check return type vs provider type

    The provider is typed as int but returns a string '42'.
  2. Step 2: Identify the mismatch error

    This type mismatch causes a compile-time error.
  3. Final Answer:

    The provider returns a string instead of an int -> Option C
  4. Quick Check:

    Provider<int> must return int, not string [OK]
Hint: Return type must match provider's declared type exactly [OK]
Common Mistakes:
  • Returning wrong data type
  • Ignoring type errors
  • Confusing Provider and StateProvider roles
5. You want to create a counter app using Riverpod where the UI updates when the counter changes. Which provider type and widget combination is best to achieve this?
hard
A. Use StateProvider<int> for counter and ConsumerWidget to rebuild UI
B. Use Provider<int> for counter and StatelessWidget to rebuild UI
C. Use FutureProvider<int> for counter and StatefulWidget to rebuild UI
D. Use StreamProvider<int> for counter and ConsumerWidget to rebuild UI

Solution

  1. Step 1: Choose provider type for mutable state

    StateProvider<int> allows changing the counter value and notifying listeners.
  2. Step 2: Choose widget that listens to provider changes

    ConsumerWidget rebuilds UI automatically when provider data changes.
  3. Final Answer:

    Use StateProvider<int> for counter and ConsumerWidget to rebuild UI -> Option A
  4. Quick Check:

    StateProvider + ConsumerWidget = reactive counter [OK]
Hint: Use StateProvider for changeable state and ConsumerWidget for UI updates [OK]
Common Mistakes:
  • Using Provider instead of StateProvider for mutable state
  • Using StatelessWidget which doesn't rebuild on changes
  • Confusing FutureProvider or StreamProvider for simple counters
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