# Table of contents
- [Flutter Clean Architecture Sample App - Dasher](https://github.com/infinum/flutter-dasher/blob/master/#flutter-clean-architecture-sample-app---dasher)
- [Architecture structure](https://github.com/infinum/flutter-dasher/blob/master/#architecture-structure)
* [Presentation](https://github.com/infinum/flutter-dasher/blob/master/#presentation)
+ [Widgets (UI)](https://github.com/infinum/flutter-dasher/blob/master/#widgets--ui-)
+ [Presenter](https://github.com/infinum/flutter-dasher/blob/master/#presenter)
* [Domain](https://github.com/infinum/flutter-dasher/blob/master/#domain)
+ [Interactor](https://github.com/infinum/flutter-dasher/blob/master/#interactor)
+ [Data Holder](https://github.com/infinum/flutter-dasher/blob/master/#data-holder)
* [Outer layer](https://github.com/infinum/flutter-dasher/blob/master/#outer-layer)
+ [Repository](https://github.com/infinum/flutter-dasher/blob/master/#repository)
+ [Source remote](https://github.com/infinum/flutter-dasher/blob/master/#source-remote)
+ [Source local](https://github.com/infinum/flutter-dasher/blob/master/#source-local)
+ [Device](https://github.com/infinum/flutter-dasher/blob/master/#device)
- [Folder structure](https://github.com/infinum/flutter-dasher/blob/master/#folder-structure)
- [Riverpod and GetIt](https://github.com/infinum/flutter-dasher/blob/master/#riverpod-and-getit)
- [Example of architecture flow](https://github.com/infinum/flutter-dasher/blob/master/#example-of-architecture-flow)
* [Presentation](https://github.com/infinum/flutter-dasher/blob/master/#presentation-1)
+ [Widget](https://github.com/infinum/flutter-dasher/blob/master/#widget)
+ [Presenter](https://github.com/infinum/flutter-dasher/blob/master/#presenter-1)
* [Domain](https://github.com/infinum/flutter-dasher/blob/master/#domain-1)
+ [Interactor](https://github.com/infinum/flutter-dasher/blob/master/#interactor-1)
+ [Repository](https://github.com/infinum/flutter-dasher/blob/master/#repository-1)
- [Screenshots](https://github.com/infinum/flutter-dasher/blob/master/#screenshots)
- [Infinum architecture Mason brick](https://github.com/infinum/flutter-dasher/blob/master/#infinum-architecture-mason-brick)
* [How to use](https://github.com/infinum/flutter-dasher/blob/master/#how-to-use)
+ [Tools to install](https://github.com/infinum/flutter-dasher/blob/master/#tools-to-install)
+ [Create new project](https://github.com/infinum/flutter-dasher/blob/master/#create-new-project)
+ [Mason brick setup](https://github.com/infinum/flutter-dasher/blob/master/#mason-brick-setup)
* [Variables](https://github.com/infinum/flutter-dasher/blob/master/#variables)
* [Outputs](https://github.com/infinum/flutter-dasher/blob/master/#outputs)
# Flutter Clean Architecture Sample App - Dasher
This project is a starting point for a Flutter application. Dasher App will introduce you to
clean architecture structure and how inner / outer layers are connected.
# Architecture structure
Dasher app uses the architecture structure described in [handbook](https://github.com/infinum/flutter-dasher/blob/master/https://infinum.com/handbook/flutter/architecture/architecture).
![flutter-architecture-layers](https://github.com/infinum/flutter-dasher/blob/master/https://user-images.githubusercontent.com/1117315/178720727-76ff7094-3f48-4127-b7ca-376f91e69682.png)
## Presentation
There is no business logic on this layer, it is only used to show UI and handle events. Read more
about Presentation layer in [handbook](https://github.com/infinum/flutter-dasher/blob/master/https://infinum.com/handbook/flutter/architecture/architecture#presenters-and-widgets).
### Widgets (UI)
- Notify presenter of events such as screen display and user touch events.
- Observes presenter state and can rebuild on state change.
### Presenter
- Contains presentation logic, usually controlling the view state.
## Domain
This layer is responsible for business logic.
### Interactor
- The main job of the interactor is combining different repositories and handling business logic.
### Data Holder
- Singleton class that holds data in memory, that doesn't call repositories or other outer layers.
## Outer layer
### Repository
- It uses concrete implementations like dio, hive, add2calendar, other plugins and abstracts them
from the rest of the application.
- Repository should be behind and interface.
- Interface belongs to the domain and the implementation belongs to the outer layers.
### Source remote
- Represents communication with remote sources (web, http clients, sockets).
### Source local
- Represents communication with local sources (database, shared_prefs).
### Device
- Represents communication with device hardware (e.g. sensors) or software
(calendar, permissions).
# Folder structure
Top-level folder structure you will find in the project under the /lib:
- **app** contains app run_app with various setups like the setup of flutter.onError crash handling and dependency initialization.
- **common** contains code that's common to all layers and accessible by all layers.
- **device** is an outer layer that represents communication with device hardware (e.g. sensors) or software (calendar, permissions).
- **domain** is the inner layer that usually contains interactors, data holders. This layer should only contain business logic and not know about specific of ui, web, etc. or other layers.
- **source_local** is an outer layer that represents communication with local sources (database, shared_prefs).
- **source_remote** is an outer layer that represents communication with remote sources (web, http clients, sockets).
- **ui** is the layer where we package by feature widgets and presenters. Presenters contains presentation logic and they access domain and are provided in the view tree by Provider/Riverpod package.
- **main_production.dart** and **main_staging.dart** two versions of main file, each version has
it's own flavor in practice this usually means having two versions. Find more about flavors
[here](https://github.com/infinum/flutter-dasher/blob/master/https://infinum.com/handbook/flutter/project-flow/flutter-flavors).
# Riverpod and GetIt
This architecture structure is using [Riverpod](https://github.com/infinum/flutter-dasher/blob/master/https://riverpod.dev/) for Presentation layer
and [GetIt](https://github.com/infinum/flutter-dasher/blob/master/https://pub.dev/packages/get_it) for Domain and outer layers (source remote, source
local and device).
Read more about how to use riverpod in [handbook](https://github.com/infinum/flutter-dasher/blob/master/https://infinum.com/handbook/flutter/architecture/using-riverpod).
![flutter-architecture-layers-riverpod-getit](https://github.com/infinum/flutter-dasher/blob/master/https://user-images.githubusercontent.com/1117315/178723072-9a2823cc-6b4d-4d15-be04-a6f5fd0bba97.png)
# Example of architecture flow
In this example, we'll show the architecture flow for fetching new Tweets on the Dashboard screen.
## Presentation
### Widget
![flutter-architecture-layers-riverpod-getit (2)](https://github.com/infinum/flutter-dasher/blob/master/https://user-images.githubusercontent.com/1117315/178748869-3200cf10-0505-4aba-ad7b-f90e4e471ffb.png)
One of the widgets on the Dashboard screen is `DasherTweetsList`. Inside the Tweets list widget is
created reference to watch `feedRequestPresenter`.
```dart
final _presenter = ref.watch(feedRequestPresenter);
```
### Presenter
![flutter-architecture-layers-riverpod-getit (3)](https://github.com/infinum/flutter-dasher/blob/master/https://user-images.githubusercontent.com/1117315/178749119-d3928b11-0a4e-4899-890c-da310c467b5d.png)
For `FeedRequestPresenter` we are using `RequestProvider`, you can find more about it [here](https://github.com/infinum/flutter-dasher/blob/master/https://github.com/infinum/flutter-bits/tree/master/request_provider).
Inside `FeedRequestPresenter` we created instance of `FetchFeedInteractor` interface.
```dart
final feedRequestPresenter = ChangeNotifierProvider.autoDispose(
(ref) => FeedRequestPresenter(GetIt.instance.get()),
);
class FeedRequestPresenter extends RequestProvider> {
FeedRequestPresenter(this._feedTimelineInteractor) {
fetchTweetsTimeline();
}
final FetchFeedInteractor _feedTimelineInteractor;
Future fetchTweetsTimeline() {
return executeRequest(requestBuilder: _feedTimelineInteractor.fetchFeedTimeline);
}
}
```
From this part, we slowly transition toward Domain layer.
## Domain
### Interactor
![flutter-architecture-layers-riverpod-getit (4)](https://github.com/infinum/flutter-dasher/blob/master/https://user-images.githubusercontent.com/1117315/178749319-34a00de7-1f33-438d-b501-ecd80ad1b88f.png)
Domain is a business logic layer, where we have an implementation of `FetchFeedInteractor` called
`FetchFeedInteractorImpl`. Our task is to create an instance of Repository which is responsible
for handling outer logic for getting user timeline tweets. `FeedRepository` is also behind an interface.
```dart
class FetchFeedInteractorImpl implements FetchFeedInteractor {
FetchFeedInteractorImpl(this._feedRepository);
final FeedRepository _feedRepository;
@override
Future> fetchFeedTimeline() {
return _feedRepository.fetchFeedTimeline();
}
}
```
### Repository
![flutter-architecture-layers-riverpod-getit (5)](https://github.com/infinum/flutter-dasher/blob/master/https://user-images.githubusercontent.com/1117315/178749678-9b33376c-5144-4644-855b-786cac657827.png)
`FeedRepositoryImpl` is part of Source remote layer. This repository is using [twitter_api_v2](https://github.com/infinum/flutter-dasher/blob/master/https://pub.dev/packages/twitter_api_v2)
package for fetching data from Twitter's API.
```dart
Future> fetchFeedTimeline() async {
final response = await twitterApi.tweetsService.lookupHomeTimeline(
userId: userDataHolder.user!.id,
tweetFields: [
TweetField.publicMetrics,
TweetField.createdAt,
],
userFields: [
UserField.createdAt,
UserField.profileImageUrl,
],
expansions: [
TweetExpansion.authorId,
],
);
return _getTweetsListWithAuthors(response);
}
```
after a successful response, data is passed back to `FeedRequestPresenter` in his state,
and it triggers state listeners. Inside the build method of `DasherTweetsList` we use state
listeners of `FeedRequestPresenter` so we can easily show/hide widgets depending on the emitted event.
![flutter-architecture-layers-riverpod-getit (6)](https://github.com/infinum/flutter-dasher/blob/master/https://user-images.githubusercontent.com/1117315/178750109-7e92255e-6894-4b03-aff8-0d6793388afc.png)
```dart
_presenter.state.maybeWhen(
success: (feed) => _TweetsList(
feed: feed,
),
initial: () => const CircularProgressIndicator(),
loading: (feed) {
if (feed == null) {
return const CircularProgressIndicator();
} else {
return _TweetsList(
feed: feed,
);
}
},
failure: (e) => Text('Error occurred $e'),
orElse: () => const CircularProgressIndicator(),
),
```
# Screenshots
| Login | Feed |
| ----- | ---- |
| | |
| Profile | New Tweet |
| ------- | --------- |
| | |
# Infinum architecture Mason brick
Easiest way to set up our architecture in the project is with usage of [Mason](https://github.com/infinum/flutter-dasher/blob/master/https://pub.dev/packages/mason_cli) bricks. The infinum_architecture brick is published on https://brickhub.dev/bricks/infinum_architecture/ and it will generate all the required directories and files ready to start the project.
## How to use
### Tools to install
Make sure you have installed [FVM - Flutter Version Management](https://github.com/infinum/flutter-dasher/blob/master/https://fvm.app/docs/getting_started/installation).
```
dart pub global activate fvm
```
Also install [Mason CLI](https://github.com/infinum/flutter-dasher/blob/master/https://pub.dev/packages/mason_cli) it's must have for using Mason bricks.
```
dart pub global activate mason_cli
```
### Create new project
Create new Flutter project:
```
flutter create {project_name}
```
move to project folder:
```
cd {project_name}
```
### Mason brick setup
Initialize mason:
```
mason init
```
Add mason brick to your project:
```
mason add infinum_architecture
```
Start generating Infinum architecture folder structure:
```
mason make infinum_architecture --on-conflict overwrite
```
## Variables
| Variable | Description | Default | Type |
| ----------------------- | --------------------------------------------------------------------------- | ------- | --------- |
| `project_name` | This name is used to name main function and files `run{project_name}App()` | example | `string` |
| `flutter_version` | Defines which version of FVM you want to install | stable | `string` |
| `brick_look` | Optional Look | true | `bool` |
| `brick_request_provider`| Optional Request Provider | true | `bool` |
## Outputs
```
lib
┣ app
┃ ┣ di
┃ ┃ ┗ inject_dependencies.dart
┃ ┣ example_app.dart
┃ ┗ run_example_app.dart
┣ common
┃ ┣ error_handling
┃ ┃ ┣ base
┃ ┃ ┃ ┣ expected_exception.dart
┃ ┃ ┃ ┗ localized_exception.dart
┃ ┃ ┗ error_formatter.dart
┃ ┣ flavor
┃ ┃ ┣ app_build_mode.dart
┃ ┃ ┣ flavor.dart
┃ ┃ ┣ flavor_config.dart
┃ ┃ ┗ flavor_values.dart
┃ ┗ logger
┃ ┣ custom_loggers.dart
┃ ┗ firebase_log_printer.dart
┣ device
┃ ┗ di
┃ ┗ inject_dependencies.dart
┣ domain
┃ ┗ di
┃ ┗ inject_dependencies.dart
┣ source_local
┃ ┗ di
┃ ┗ inject_dependencies.dart
┣ source_remote
┃ ┗ di
┃ ┗ inject_dependencies.dart
┣ ui
┃ ┣ common
┃ ┃ ┣ generic
┃ ┃ ┃ ┗ generic_error.dart
┃ ┗ home
┃ ┗ home_screen.dart
┣ main_production.dart
┗ main_staging.dart
```