Flutter Riverpod Tutorial Part 8: Dependency injection

Search for a command to run...

No comments yet. Be the first to comment.
In this series, I'll cover every concept of Riverpod in a comprehensive manner, so that any user can understand total ins and outs of any Riverpod concept.
Setup Riverpod Before we start using Riverpod. Let's set it up in our Flutter Project Add the dependency: flutter pub add flutter_riverpod Wrap our application with ProviderScope: This is necessary for Riverpod to work. It should wrap your top-level...
While I generally write about Flutter. This problem has been bugging me for a while and there seem to be limited resources to find a solution. So for anyone who is looking for a solution. Here you go: Type edge:flags into the address bar and open it...
In this tutorial, we’ll go through covering interceptor, which is a very crucial feature of Dio. You can find the entire source code for the project here: dio_tasker Understanding Interceptors: What is an interceptor? An interceptor in Dio is a powe...

In this part, we’ll enhance our Task Manager app by parsing JSON responses and handling different types of errors. We’ll use Dio’s built-in error handling mechanisms. You can find the source code of the entire app here: dio_tasker Parsing JSON Respon...

While building apps in Flutter is extremely fluid and wonderfully intuitive. We obviously know that we can’t build the next TikTok or Twitter by only building offline apps. Which is why we absolutely need internet capabilities in our Flutter apps. Th...

Understanding when to use setState in Flutter is crucial for managing our app’s state effectively. Here’s a detailed guide: When to use setState ? Updating the UI: Use setState when we need to update the UI in response to changes in the internal sta...

In this tutorial, we'll explore how to set up dependency injection with Riverpod, manage dependencies across your app, and test with dependency injection.
Dependency injection is a technique where an object receives its dependencies from an external source rather than creating them itself, promoting loose coupling and easier testing.
Setting up Dependency Injection with Riverpod
Managing Dependencies Across the App
Testing with Dependency Injection
Step 1: Define Dependencies
Let's create a service class that you'll use as a dependency. For example we will use this AuthService
class AuthService {
String login(String login, String password) {
//Mock Login Service
if (login == 'admin' && password == 'admin') {
return 'Login successful!';
} else {
return 'Login failed!';
}
}
}
Step 2: Create a Provider for the Service
Since we are going to test the code we need to create the provider in a separate file. like auth_service_provider.dart.
final authServiceProvider = Provider<AuthService>((ref) => AuthService());
So in here let's use the authServiceProvider in our LoginPage
class LoginPage extends HookConsumerWidget {
const LoginPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final authService = ref.watch(authServiceProvider);
final usernameController = useTextEditingController();
final passwordController = useTextEditingController();
final message = useState('');
return Scaffold(
appBar: AppBar(
title: const Text('Login'),
),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
TextField(
controller: usernameController,
decoration: const InputDecoration(labelText: 'Username'),
),
const SizedBox(
height: 20,
),
TextField(
controller: passwordController,
decoration: const InputDecoration(labelText: 'Password'),
),
const SizedBox(
height: 20,
),
ElevatedButton(
onPressed: () {
final result = authService.login(
usernameController.text, passwordController.text);
message.value = result;
},
child: const Text('Login')),
const SizedBox(
height: 20,
),
Text(
message.value,
style: const TextStyle(fontSize: 24),
),
],
),
),
);
}
}
Step 1: Write Tests for the AuthService
Create a test file, auth_service_test.dart. Make sure to put this under the test/ directory.
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/auth_provider.dart'; // Adjust the import as per your project structure
import 'package:hooks_riverpod/hooks_riverpod.dart';
void main() {
test('AuthService login test', () {
final container = ProviderContainer();
final authService = container.read(authServiceProvider);
expect(authService.login('admin', 'admin'), 'Login successful!');
expect(authService.login('user', 'wrong_password'), 'Login failed!');
});
}
So ProviderContainer() assists us with managing Providers and overriding them if needed. We don't need to do it in our regular files because ProviderScope() we using in main.dart already encompasses our entire application.
Step 2: Mock Dependencies for Testing
Create a mock version of AuthService for more complex testing scenarios.
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/auth_provider.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class MockAuthService extends AuthService {
@override
String login(String username, String password) {
return 'Mock login for $username';
}
}
void main() {
test('AuthService mock test', () {
final container = ProviderContainer(
overrides: [
authServiceProvider.overrideWithValue(MockAuthService()),
],
);
final authService = container.read(authServiceProvider);
expect(authService.login('test', 'test'), 'Mock login for test');
});
}
Perfect Now. If we want to execute the test. Just run the following command
flutter test
In this tutorial, we explored how to set up dependency injection with Riverpod, manage dependencies across your app, and write tests using dependency injection. These techniques help create modular, testable, and maintainable applications.