<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Youssef Mohamed</title>
    <description>The latest articles on DEV Community by Youssef Mohamed (@youssef_mohamed_e636291be).</description>
    <link>https://dev.to/youssef_mohamed_e636291be</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3592308%2F676f1e4d-9e13-4ec1-8096-5d9f7ac62307.png</url>
      <title>DEV Community: Youssef Mohamed</title>
      <link>https://dev.to/youssef_mohamed_e636291be</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/youssef_mohamed_e636291be"/>
    <language>en</language>
    <item>
      <title>State Management in Flutter with Riverpod</title>
      <dc:creator>Youssef Mohamed</dc:creator>
      <pubDate>Sat, 01 Nov 2025 11:15:40 +0000</pubDate>
      <link>https://dev.to/youssef_mohamed_e636291be/state-management-in-flutter-with-riverpod-1po7</link>
      <guid>https://dev.to/youssef_mohamed_e636291be/state-management-in-flutter-with-riverpod-1po7</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Managing state in Flutter apps can be tricky — especially when the app starts growing.&lt;br&gt;
If you’ve tried setState or even Provider, you might have faced unnecessary rebuilds or limitations in accessing data outside widgets.&lt;/p&gt;

&lt;p&gt;That’s where Riverpod comes in.&lt;br&gt;
It’s a modern, safe, and testable state management solution built by the same creator of Provider, but without its limitations.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore Riverpod step-by-step, and build a simple Authentication ViewModel as a real-world example.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Riverpod?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;No BuildContext needed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Works perfectly with async data&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easy testing &amp;amp; maintainability&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Less rebuilds = better performance&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Clean separation between UI and logic&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Setting up Riverpod
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Add the package to your project&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flutter pub add flutter_riverpod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Wrap your app with ProviderScope in main.dart&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void main() {
  runApp(const ProviderScope(child: MyApp()));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Basic Providers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Let’s look at a few types of providers Riverpod offers&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Provider&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Used for read-only values.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;final greetingProvider = Provider((ref) =&amp;gt; 'Hello Riverpod!');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Usage:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Widget build(BuildContext context, WidgetRef ref) {
  final message = ref.watch(greetingProvider);
  return Text(message);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;StateProvider&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Used for mutable state (like counters)&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;final counterProvider = StateProvider&amp;lt;int&amp;gt;((ref) =&amp;gt; 0);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Usage:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;final count = ref.watch(counterProvider);
ref.read(counterProvider.notifier).state++;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;FutureProvider&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;For asynchronous data (e.g., fetching from an API)&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;final userProvider = FutureProvider((ref) async {
  await Future.delayed(const Duration(seconds: 2));
  return 'User Loaded!';
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Building an Authentication ViewModel
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Let’s build a small example showing how Riverpod can manage real app logic — user login/logout.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User Model&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class User {
  final String id;
  final String email;
  final String name;

  User({required this.id, required this.email, required this.name});
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repository&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class AuthRepository {
  Future&amp;lt;User&amp;gt; login(String email, String password) async {
    await Future.delayed(const Duration(seconds: 2));

    if (email == 'joe@test.com' &amp;amp;&amp;amp; password == '123456') {
      return User(id: '1', email: email, name: 'Joe');
    } else {
      throw Exception('Invalid credentials');
    }
  }

  Future&amp;lt;void&amp;gt; logout() async =&amp;gt; Future.delayed(const Duration(milliseconds: 500));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ViewModel&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter_riverpod/flutter_riverpod.dart';

class AuthState {
  final bool isLoading;
  final User? user;
  final String? error;

  const AuthState({this.isLoading = false, this.user, this.error});
}

class AuthViewModel extends StateNotifier&amp;lt;AuthState&amp;gt; {
  final AuthRepository _repo;

  AuthViewModel(this._repo) : super(const AuthState());

  Future&amp;lt;void&amp;gt; login(String email, String password) async {
    try {
      state = AuthState(isLoading: true);
      final user = await _repo.login(email, password);
      state = AuthState(user: user);
    } catch (e) {
      state = AuthState(error: e.toString());
    }
  }

  Future&amp;lt;void&amp;gt; logout() async {
    await _repo.logout();
    state = const AuthState();
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Providers&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;final authRepositoryProvider = Provider((ref) =&amp;gt; AuthRepository());
final authViewModelProvider =
    StateNotifierProvider&amp;lt;AuthViewModel, AuthState&amp;gt;((ref) {
  return AuthViewModel(ref.watch(authRepositoryProvider));
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Login Screen&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class LoginScreen extends ConsumerWidget {
  const LoginScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final state = ref.watch(authViewModelProvider);
    final viewModel = ref.read(authViewModelProvider.notifier);

    final email = TextEditingController();
    final pass = TextEditingController();

    return Scaffold(
      appBar: AppBar(title: const Text('Login')),
      body: Center(
        child: state.isLoading
            ? const CircularProgressIndicator()
            : Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  TextField(controller: email, decoration: const InputDecoration(labelText: 'Email')),
                  TextField(controller: pass, decoration: const InputDecoration(labelText: 'Password'), obscureText: true),
                  ElevatedButton(
                    onPressed: () =&amp;gt; viewModel.login(email.text, pass.text),
                    child: const Text('Login'),
                  ),
                  if (state.error != null)
                    Text(state.error!, style: const TextStyle(color: Colors.red)),
                  if (state.user != null)
                    Text('Welcome, ${state.user!.name}!'),
                ],
              ),
      ),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Riverpod gives you the power of scalable and testable state management — perfect for both small and enterprise Flutter apps.&lt;/p&gt;

&lt;p&gt;If you’re building something serious, Riverpod is definitely worth mastering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extra
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Official Docs:&lt;/strong&gt; &lt;a href="https://riverpod.dev" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;/p&gt;

</description>
      <category>elevate</category>
      <category>riverpod</category>
    </item>
  </channel>
</rss>
