This project aims to demonstrate best practices and architectural patterns for developing Flutter applications.
This project covers:
- A way to Handle Failures in your Flutter Projects.
- A few good practices like abstractions of third party libraries and dependency injection.
- User Feature: Demonstrates the implementation of clean architecture principles (domain, data, presentation) for managing user-related functionality.
- Network Package: Provides a comprehensive network layer abstraction with domain-driven design. It separates concerns into domain, data, and infrastructure layers, offering flexibility and ease of testing.
- Core: Includes essential definitions and abstractions, such as
Failure
andEither
classes for handling errors andServiceLocator
working as an abstraction ofGetIt
.
- Clone the repository:
git clone https://github.com/willcav/flutter_best_practices.git
- Navigate to the project directory:
cd flutter_best_practices
- Install dependencies
flutter pub get
- Explore the project structure and adapt it to your application`s needs.
Error handling is a crucial aspect of any application, and this project employs the Either class to manage error situations effectively.
The Either class is an abstraction that represents a value of one of two possible types: L
for Left, typically representing an error or failure, and R
for Right, representing a successful result. This approach allows us to handle errors explicitly in our code without relying extensively on try-catch
blocks.
Example usage:
// Example of a function that returns an asynchronous result
Future<Either<Failure, DogEntity>> getDog() async {
try {
final dog = await _repository.fetchDog();
return Right(user); // Success
} catch (e) {
return Left(Failure("Failed to fetch dog")); // Failure
}
}
// Using the result of the [getDog] function
getDog().then((result) {
result.fold(
(failure) {
// Handle the error
print("Error: ${failure.message}");
},
(dog) {
// Use the dog data
print("Dog data fetched successfully: $dog");
},
);
});
This approach enables us to manage error situations explicitly in our code, improving readability and reducing the likelihood of errors being overlooked or mishandled.
The project follows clean architecture principles to separate concerns and maintain a clear separation of layers: domain, data, and presentation.
The Network
package provides a robust network layer abstraction, facilitating HTTP requests with flexibility and testability. It utilizes domain-driven design and inversion of control principles for effective dependency management.
The current network implementation utilizes Dio
, however, it can be effortlessly replaced with another library without requiring modifications to the app, as the structure adheres to the defined interfaces.
This architecture allows for seamless integration with different HTTP client implementations while maintaining consistency and flexibility in your application's network layer. The defined interfaces facilitate dependency injection and decoupling, making it easy to adapt to changes in requirements or technology stacks without disrupting the overall architecture.
Abstractions serve as a high-level representation of the functionality provided by third-party libraries.
By leveraging abstractions and wrappers, we can achieve greater maintainability in our codebases. When the need arises to switch to a different service locator library or upgrade to a newer version, the changes can be confined to the implementation of the abstraction or wrapper.
In this project, the GetIt dependency is encapsulated within the GetItDriver implementation, adhering to the interface we've defined.
I welcome contributions to enhance and expand this repository! Here are some ways you can contribute:
- Implement additional features or architectural patterns.
- Improve documentation and code quality.
- Address existing issues or propose new ideas.