• Flutter Times
  • Posts
  • Dependency Injection with Kiwi and BLoC: A Great Experience!

Dependency Injection with Kiwi and BLoC: A Great Experience!

When building Flutter apps, dependency injection helps keep code clean, modular, and testable. Using Kiwi with BLoC (Business Logic Component) architecture has been an incredible experience. It simplifies managing dependencies across your app and makes working with complex data flows much easier. Here's how I did it:

What is Kiwi?

Kiwi is a lightweight dependency injection library for Dart and Flutter. It helps you manage and inject services into different parts of your app.

Setting Up Kiwi

  1. First, add Kiwi to your pubspec.yaml:

dependencies:
  kiwi: ^3.0.0
  1. Then, create a Kiwi container for registering your services:

import 'package:kiwi/kiwi.dart';

class Injector {
  static final KiwiContainer container = KiwiContainer();

  static void setup() {
    container.registerFactory((c) => ApiService());
    container.registerFactory((c) => UserRepository(c<ApiService>()));
    container.registerFactory((c) => UserBloc(c<UserRepository>()));
  }
}

Here, we register ApiService, UserRepository, and UserBloc. Notice how UserRepository depends on ApiService and UserBloc depends on UserRepository.

BLoC Integration with Kiwi

To integrate BLoC with Kiwi, inject the dependencies into your BLoC. Here's how:

  1. Define a UserBloc class:

import 'package:flutter_bloc/flutter_bloc.dart';
import 'user_repository.dart';

class UserBloc extends Cubit<UserState> {
  final UserRepository _userRepository;

  UserBloc(this._userRepository) : super(UserInitial());

  Future<void> fetchUsers() async {
    try {
      emit(UserLoading());
      final users = await _userRepository.getUsers();
      emit(UserLoaded(users));
    } catch (e) {
      emit(UserError("Failed to fetch users"));
    }
  }
}
  1. Inject UserBloc in your widget:

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:kiwi/kiwi.dart';
import 'user_bloc.dart';

class UserPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => KiwiContainer().resolve<UserBloc>()..fetchUsers(),
      child: Scaffold(
        appBar: AppBar(title: Text('Users')),
        body: BlocBuilder<UserBloc, UserState>(
          builder: (context, state) {
            if (state is UserLoading) {
              return Center(child: CircularProgressIndicator());
            } else if (state is UserLoaded) {
              return ListView.builder(
                itemCount: state.users.length,
                itemBuilder: (context, index) => ListTile(
                  title: Text(state.users[index].name),
                ),
              );
            } else {
              return Center(child: Text('Failed to load users'));
            }
          },
        ),
      ),
    );
  }
}

Why is Kiwi + BLoC Great?

  • Separation of Concerns: BLoC handles business logic, Kiwi manages dependencies. This makes your codebase more organized.

  • Testability: You can easily mock dependencies for testing without rewriting your logic.

  • Scalability: As your app grows, Kiwi makes it easier to register and inject services.

This setup has been a game-changer for my Flutter apps! 🎉 Give it a try, and you’ll see how much cleaner and more scalable your projects become.