Setup
To start using the Clean Framework components, you need to add the library on the pubspec.yaml of the project. Use the latest version available.
Adding clean_framework
package as a dependency
The project depends on the clean_framework package
.
In the pubspec.yaml
, add the following to dependencies
section:
clean_framework: any
Or you can just add the package directly using the following command:
flutter pub add clean_framework
Project Structure
We suggest you organize your app into Features, with the assumption that features don't depend on each other. The goal should be to be able to delete a feature completely and don't break any code.
Each feature could be organized in this way:
lib
providers_loader.dart
features
my_new_feature
domain
my_new_feature_usecase.dart
my_new_feature_entity.dart
my_new_feature_outputs.dart
my_new_feature_inputs.
presentation
my_new_feature_presenter.dart
my_new_feature_view_model.dart
my_new_feature_ui.dart
external_interfaces
my_new_feature_gateway.dart
Notice that the name of the feature is a prefix for all the files inside. We prefer this naming convention so they are easier to idenfiy on searches, but you are free to follow any convention that suits your need.
The folder structure is also a suggestion, you can add multiple layers if the feature begins to grow and have multiple screens and interactions.
The Providers
Use Cases, Gateways and External Interfaces are instances of classes that are not Flutter Widgets, so they are not dependant on the Flutter Context. To have access to them, you can "publish" them using the Providers pattern.
If you notice on the files list shown above, outside the features folder we have a file where we list all the providers used on the app. For large projects this is probably not the best idea, since this file can be long and bloated, so probably splitting the providers by feature could work better.
This is an example on how this file can be coded:
final myNewFeatureUseCaseProvider =
UseCaseProvider<MyNewFeatureEntity, MyNewFeatureUseCase>(
(_) => LastLoginUseCase(),
);
final myNewFeatureGatewayProvider = GatewayProvider<MyNewFeatureGateway>(
(_) => MyNewFeatureGateway(),
);
void loadProviders() {
myNewFeatureUseCaseProvider.getUseCaseFromContext(providersContext);
MyNewFeatureGatewayProvider.getGateway(providersContext);
restExternalInterface.getExternalInterface(providersContext);
}
Clean Framework uses Riverpod for the Providers behavior, so you can understand why the providers are global instances. For anyone not familiar to how Riverpod works, this might seem innapropiate, specially comming from a strict OO formation. Justifying why this is useful and desirable, please refer to the Riverpod documentation, since the creator already did a great job explaining this approach.
Providers create instances lazyly, but some of the listeners need to be connected before use cases make any request. That is why we use a global function to "touch" all gateway and external interfaces providers to ensure they are created when the app starts.
The last consideration is to remember to use the function on the main function:
void main() {
loadProviders();
runApp(MyApp());
}