Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/infinum/flutter-dasher
https://github.com/infinum/flutter-dasher
Last synced: 4 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/infinum/flutter-dasher
- Owner: infinum
- License: mit
- Created: 2022-06-14T06:03:02.000Z (over 2 years ago)
- Default Branch: master
- Last Pushed: 2024-02-07T07:38:25.000Z (about 1 year ago)
- Last Synced: 2024-08-01T12:20:25.141Z (7 months ago)
- Language: Dart
- Size: 425 KB
- Stars: 115
- Watchers: 10
- Forks: 8
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Table of contents
- [Flutter Clean Architecture Sample App - Dasher](#flutter-clean-architecture-sample-app---dasher)
- [Architecture structure](#architecture-structure)
* [Presentation](#presentation)
+ [Widgets (UI)](#widgets--ui-)
+ [Presenter](#presenter)
* [Domain](#domain)
+ [Interactor](#interactor)
+ [Data Holder](#data-holder)
* [Outer layer](#outer-layer)
+ [Repository](#repository)
+ [Source remote](#source-remote)
+ [Source local](#source-local)
+ [Device](#device)
- [Folder structure](#folder-structure)
- [Riverpod and GetIt](#riverpod-and-getit)
- [Example of architecture flow](#example-of-architecture-flow)
* [Presentation](#presentation-1)
+ [Widget](#widget)
+ [Presenter](#presenter-1)
* [Domain](#domain-1)
+ [Interactor](#interactor-1)
+ [Repository](#repository-1)
- [Screenshots](#screenshots)
- [Infinum architecture Mason brick](#infinum-architecture-mason-brick)
* [How to use](#how-to-use)
+ [Tools to install](#tools-to-install)
+ [Create new project](#create-new-project)
+ [Mason brick setup](#mason-brick-setup)
* [Variables](#variables)
* [Outputs](#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://infinum.com/handbook/flutter/architecture/architecture).

## 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://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://infinum.com/handbook/flutter/project-flow/flutter-flavors).# Riverpod and GetIt
This architecture structure is using [Riverpod](https://riverpod.dev/) for Presentation layer
and [GetIt](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://infinum.com/handbook/flutter/architecture/using-riverpod).

# Example of architecture flow
In this example, we'll show the architecture flow for fetching new Tweets on the Dashboard screen.## Presentation
### Widget
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
For `FeedRequestPresenter` we are using `RequestProvider`, you can find more about it [here](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
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
`FeedRepositoryImpl` is part of Source remote layer. This repository is using [twitter_api_v2](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.
```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://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://fvm.app/docs/getting_started/installation).
```
dart pub global activate fvm
```
Also install [Mason CLI](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
```