{"id":13552569,"url":"https://github.com/ShadyBoukhary/flutter_clean_architecture","last_synced_at":"2025-04-03T03:32:16.141Z","repository":{"id":44599425,"uuid":"184160556","full_name":"ShadyBoukhary/flutter_clean_architecture","owner":"ShadyBoukhary","description":"Clean architecture flutter: A Flutter package that makes it easy and intuitive to implement Uncle Bob's Clean Architecture in Flutter. This package provides basic classes that are tuned to work with Flutter and are designed according to the Clean Architecture.","archived":false,"fork":false,"pushed_at":"2024-08-28T03:44:15.000Z","size":344,"stargazers_count":708,"open_issues_count":7,"forks_count":175,"subscribers_count":28,"default_branch":"master","last_synced_at":"2025-04-01T08:42:59.312Z","etag":null,"topics":["clean-architecture","clean-code","dart","dart2","dartlang","design-pattern","flutter","flutter-package","hacktoberfest","uncle-bob"],"latest_commit_sha":null,"homepage":"https://pub.dartlang.org/packages/flutter_clean_architecture","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ShadyBoukhary.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-04-29T23:54:26.000Z","updated_at":"2025-03-24T10:41:09.000Z","dependencies_parsed_at":"2024-01-19T07:05:10.740Z","dependency_job_id":"4213371a-5567-4fa6-9b07-e4482a078924","html_url":"https://github.com/ShadyBoukhary/flutter_clean_architecture","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ShadyBoukhary%2Fflutter_clean_architecture","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ShadyBoukhary%2Fflutter_clean_architecture/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ShadyBoukhary%2Fflutter_clean_architecture/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ShadyBoukhary%2Fflutter_clean_architecture/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ShadyBoukhary","download_url":"https://codeload.github.com/ShadyBoukhary/flutter_clean_architecture/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246933421,"owners_count":20857049,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clean-architecture","clean-code","dart","dart2","dartlang","design-pattern","flutter","flutter-package","hacktoberfest","uncle-bob"],"created_at":"2024-08-01T12:02:06.173Z","updated_at":"2025-04-03T03:32:11.108Z","avatar_url":"https://github.com/ShadyBoukhary.png","language":"Dart","funding_links":[],"categories":["Dart"],"sub_categories":[],"readme":"# flutter_clean_architecture Package\n\n\n![CI](https://github.com/shadyboukhary/flutter_clean_architecture/workflows/Continuous%20Integration/badge.svg)\n\n\n## Overview\nA Flutter package that makes it easy and intuitive to implement [Uncle Bob's Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) in Flutter. This package provides basic classes that are tuned to work with Flutter and are designed according to the Clean Architecture.\n\n## Installation\n\n### 1. Depend on It\nAdd this to your package's pubspec.yaml file:\n\n```yaml\n\ndependencies:\n  flutter_clean_architecture: ^6.0.1\n\n```\n\n### 2. Install it\nYou can install packages from the command line:\n\nwith Flutter:\n\n```shell\n$ flutter packages get\n```\n\nAlternatively, your editor might support `flutter packages get`. Check the docs for your editor to learn more.\n\n### 3. Import it\nNow in your Dart code, you can use:\n\n```dart\nimport 'package:flutter_clean_architecture/flutter_clean_architecture.dart';\n```\n\n## Flutter Clean Architecture Primer\n### Introduction\nIt is architecture based on the book and blog by Uncle Bob. It is a combination of concepts taken from the Onion Architecture and other architectures. The main focus of the architecture is separation of concerns and scalability. It consists of four main modules: `App`, `Domain`, `Data`, and `Device`.\n\n### The Dependency Rule\n**Source code dependencies only point inwards**. This means inward modules are neither aware of nor dependent on outer modules. However, outer modules are both aware of and dependent on inner modules. Outer modules represent the mechanisms by which the business rules and policies (inner modules) operate. The more you move inward, the more abstraction is present. The outer you move the more concrete implementations are present. Inner modules are not aware of any classes, functions, names, libraries, etc.. present in the outer modules. They simply represent **rules** and are completely independent from the implementations.\n\n### Layers\n\n#### Domain\nThe `Domain` module defines the business logic of the application. It is a module that is independent from the development platform i.e. it is written purely in the programming language and does not contain any elements from the platform. In the case of `Flutter`, `Domain` would be written purely in `Dart` without any `Flutter` elements. The reason for that is that `Domain` should only be concerned with the business logic of the application, not with the implementation details. This also allows for easy migration between platforms, should any issues arise.\n\n##### Contents of Domain\n`Domain` is made up of several things.\n* **Entities**\n  * Enterprise-wide business rules\n  * Made up of classes that can contain methods\n  * Business objects of the application\n  * Used application-wide\n  * Least likely to change when something in the application changes\n* **Usecases**\n  * Application-specific business rules\n  * Encapsulate all the usecases of the application\n  * Orchestrate the flow of data throughout the app\n  * Should not be affected by any UI changes whatsoever\n  * Might change if the functionality and flow of application change\n* **Repositories**\n  * Abstract classes that define the expected functionality of outer layers\n  * Are not aware of outer layers, simply define expected functionality\n    * E.g. The `Login` usecase expects a `Repository` that has `login` functionality\n  * Passed to `Usecases` from outer layers\n\n`Domain` represents the inner-most layer. Therefore, it the most abstract layer in the architecture.\n\n#### App\n`App` is the layer outside `Domain`. `App` crosses the boundaries of the layers to communicate with `Domain`. However, the **Dependency Rule** is never violated. Using `polymorphism`, `App` communicates with `Domain` using inherited class: classes that implement or extend the `Repositories` present in the `Domain` layer. Since `polymorphism` is used, the `Repositories` passed to `Domain` still adhere to the **Dependency Rule** since as far as `Domain` is concerned, they are abstract. The implementation is hidden behind the `polymorphism`.\n\n##### Contents of App\nSince `App` is the presentation layer of the application, it is the most framework-dependent layer, as it contains the UI and the event handlers of the UI. For every page in the application, `App` defines at least 3 classes: a `Controller`, a `Presenter`, and a `View`.\n\n* **View**\n  * Represents only the UI of the page. The `View` builds the page's UI, styles it, and depends on the `Controller` to handle its events. The `View` **has-a** `Controller`.\n  * In the case of Flutter\n    * The `View` is comprised of 2 classes\n      * One that extends `View`, which would be the root `Widget` representing the `View`\n      * One that extends `ViewState` with the template specialization of the other class and its `Controller`. \n    * The `ViewState` contains the `view` getter, which is technically the UI implementation\n    * `StatefulWidget` contains the `State` as per `Flutter`\n    * The `StatefulWidget` only serves to pass arguments to the `State` from other pages such as a title etc.. It only instantiates the `State` object (the `ViewState`) and provides it with the `Controller` it needs through it's consumer.\n    * The `StatefulWidget`  **has-a** `State` object (the `ViewState`) which **has-a** `Controller`\n    * In summary, both the `StatefulWidget` and the `State` are represented by a  `View` and `ViewState` of the page.\n    * The `ViewState` class maintains a `GlobalKey` that can be used as a key in its scaffold. If used, the `Controller` can easily access it via `getState()` in order to show snackbars and other dialogs. This is helpful but optional. \n* **Controller**\n  * Every `ViewState` **has-a** `Controller`. The `Controller` provides the needed member data of the `ViewState` i.e. dynamic data. The `Controller` also implements the event-handlers of the `ViewState` widgets, but has no access to the `Widgets` themselves. The `ViewState` uses the `Controller`, not the other way around. When the `ViewState` calls a handler from the `Controller`, `refreshUI()` can be called to update the view.\n  * Every `Controller` extends the `Controller` abstract class, which implements `WidgetsBindingObserver`. Every `Controller` class is responsible for handling lifecycle events for the `View` and can override:\n    * **void onInActive()**\n    * **void onPaused()** \n    * **void onResumed()** \n    * **void onDetached()**\n    * **void onDisposed()**\n    * **void onReassembled()**\n    * **void onDidChangeDependencies()**\n    * **void onInitState()**\n    * etc..\n  * Also, every `Controller` **has** to implement **initListeners()** that initializes the listeners for the `Presenter` for consistency.\n  * The `Controller` **has-a** `Presenter`. The `Controller` will pass the `Repository` to the `Presenter`, which it communicate later with the `Usecase`. The `Controller` will specify what listeners the `Presenter` should call for all success and error events as mentioned previously. Only the `Controller` is allowed to obtain instances of a `Repository` from the `Data` or `Device` module in the outermost layer.\n  * The `Controller` has access to the `ViewState` and can refresh the `ControlledWidgets` via `refreshUI()`.\n* **Presenter**\n  * Every `Controller` **has-a** `Presenter`. The `Presenter` communicates with the `Usecase` as mentioned at the beginning of the `App` layer. The `Presenter` will have members that are functions, which are optionally set by the `Controller` and will be called if set upon the `Usecase` sending back data, completing, or erroring.\n  * The `Presenter` is comprised of two classes\n    * `Presenter` e.g. `LoginPresenter`\n      * Contains the event-handlers set by the `Controller`\n      * Contains the `Usecase` to be used\n      * Initializes and executes the usecase with the `Observer\u003cT\u003e` class and the appropriate arguments. E.g. with `username` and `password` in the case of a `LoginPresenter`\n    * A class that implements `Observer\u003cT\u003e`\n      * Has reference to the `Presenter` class. Ideally, this should be an inner class but `Dart` does not yet support them.\n      * Implements 3 functions\n        * **onNext(T)**\n        * **onComplete()**\n        * **onError()**\n      * These 3 methods represent all possible outputs of the `Usecase`\n        * If the `Usecase` returns an object, it will be passed to `onNext(T)`. \n        * If it errors, it will call `onError(e)`. \n        * Once it completes, it will call `onComplete()`. \n       * These methods will then call the corresponding methods of the `Presenter` that are set by the `Controller`. This way, the event is passed to the `Controller`, which can then manipulate data and update the `ViewState`\n* Extra\n  * `Utility` classes (any commonly used functions like timestamp getters etc..)\n  * `Constants` classes (`const` strings for convenience)\n  * `Navigator` (if needed)\n  \n#### Data\nRepresents the data-layer of the application. The `Data` module, which is a part of the outermost layer, is responsible for data retrieval. This can be in the form of API calls to a server, a local database, or even both. \n\n##### Contents of Data\n* **Repositories**\n  * Every `Repository` **should** implement `Repository` from the **Domain** layer.\n  * Using `polymorphism`, these repositories from the data layer can be passed across the boundaries of layers, starting from the `View` down to the `Usecases` through the `Controller` and `Presenter`.\n  * Retrieve data from databases or other methods. \n  * Responsible for any API calls and high-level data manipulation such as\n    * Registering a user with a database\n    * Uploading data\n    * Downloading data\n    * Handling local storage\n    * Calling an API\n* **Models** (not a must depending on the application)\n  * Extensions of `Entities` with the addition of extra members that might be platform-dependent. For example, in the case of local databases, this can be manifested as an `isDeleted` or an `isDirty` entry in the local database. Such entries cannot be present in the `Entities` as that would violate the **Dependency Rule** since **Domain** should not be aware of the implementation.\n  * In the case of our application, models in the `Data` layer will not be necessary as we do not have a local database. Therefore, it is unlikely that we will need extra entries in the `Entities` that are platform-dependent.\n* **Mappers**\n  * Map `Entity` objects to `Models` and vice-versa.\n  * Static classes with static methods that receive either an `Entity` or a `Model` and return the other.\n  * Only necessary in the presence of `Models`\n* Extra\n  * `Utility` classes if needed\n  * `Constants` classes if needed\n\n#### Device\nPart of the outermost layer, `Device` communicates directly with the platform i.e. Android and iOS. `Device` is responsible for Native functionality such as `GPS` and other functionality present within the platform itself like the filesystem. `Device` calls all Native APIs. \n\n##### Contents of Data\n* **Devices**\n  * Similar to `Repositories` in `Data`, `Devices` are classes that communicate with a specific functionality in the platform.\n  * Passed through the layers the same way `Repositories` are pass across the boundaries of the layer: using polymorphism between the `App` and `Domain` layer. That means the `Controller` passes it to the `Presenter` then the `Presenter` passes it polymorphically to the `Usecase`, which receives it as an abstract class.\n* Extra\n  * `Utility` classes if needed\n  * `Constants` classes if needed\n## Usage\n\n### Folder structure\n\n```\nlib/\n    app/                          \u003c--- application layer\n        pages/                        \u003c-- pages or screens\n          login/                        \u003c-- some page in the app\n            login_controller.dart         \u003c-- login controller extends `Controller`\n            login_presenter.dart          \u003c-- login presenter extends `Presenter`\n            login_view.dart               \u003c-- login view, 2 classes extend `View` and `ViewState` resp.\n        widgets/                      \u003c-- custom widgets\n        utils/                        \u003c-- utility functions/classes/constants\n        navigator.dart                \u003c-- optional application navigator\n    data/                         \u003c--- data layer\n        repositories/                 \u003c-- repositories (retrieve data, heavy processing etc..)\n          data_auth_repo.dart           \u003c-- example repo: handles all authentication\n        helpers/                      \u003c-- any helpers e.g. http helper\n        constants.dart                \u003c-- constants such as API keys, routes, urls, etc..\n    device/                       \u003c--- device layer\n        repositories/                 \u003c--- repositories that communicate with the platform e.g. GPS\n        utils/                        \u003c--- any utility classes/functions\n    domain/                       \u003c--- domain layer (business and enterprise) PURE DART\n        entities/                   \u003c--- enterprise entities (core classes of the app)\n          user.dart                   \u003c-- example entity\n          manager.dart                \u003c-- example entity\n        usecases/                   \u003c--- business processes e.g. Login, Logout, GetUser, etc..\n          login_usecase.dart          \u003c-- example usecase extends `UseCase` or `CompletableUseCase`\n        repositories/               \u003c--- abstract classes that define functionality for data and device layers\n    main.dart                     \u003c--- entry point\n\n```\n\n### Example Code\nCheckout a small example [here](./example/) and a full application built [here](https://github.com/ShadyBoukhary/Axion-Technologies-HnH).\n\n#### View and ControlledWidgetBuilder\n\n```dart\nimport 'package:flutter_clean_architecture/flutter_clean_architecture.dart';\nclass CounterPage extends CleanView {\n    @override\n     // Dependencies can be injected here\n     State\u003cStatefulWidget\u003e createState() =\u003e CounterState();\n}\n\nclass CounterState extends CleanViewState\u003cCounterPage, CounterController\u003e {\n     CounterState() : super(CounterController());\n\n     @override\n     Widget get view =\u003e MaterialApp(\n        title: 'Flutter Demo',\n        home: Scaffold(\n        key: globalKey, // using the built-in global key of the `View` for the scaffold or any other\n                        // widget provides the controller with a way to access them via getContext(), getState(), getStateKey()\n        body: Column(\n          children: \u003cWidget\u003e[\n            Center(\n              // show the number of times the button has been clicked\n              child: ControlledWidgetBuilder\u003cCounterController\u003e(\n                builder: (context, controller) {\n                  return Text(controller.counter.toString());\n                }\n              ),\n            ),\n            // you can refresh manually inside the controller\n            // using refreshUI()\n            ControlledWidgetBuilder\u003cCounterController\u003e(\n                builder: (context, controller) {\n                  return MaterialButton(onPressed: controller.increment);\n                }\n              ),\n          ],\n        ),\n      ),\n    );\n}\n```\n##### Responsive view state\n\nTo deal with screens on flutter web, you can take advantage of the responsive view state,\nthat abstracts the main web apps breakpoints (desktop, tablet and mobile) to ease development\nfor web with `flutter_clean_architecture`\n\nFor example:\n\n```dart\nimport 'package:flutter_clean_architecture/flutter_clean_architecture.dart';\nclass CounterPage extends CleanView {\n    @override\n     // Dependencies can be injected here\n     State\u003cStatefulWidget\u003e createState() =\u003e CounterState();\n}\n\nclass CounterState extends ResponsiveViewState\u003cCounterPage, CounterController\u003e {\n     CounterState() : super(CounterController());\n\n     Widget AppScaffold({Widget child}) {\n         return MaterialApp(\n              title: 'Flutter Demo',\n           home: Scaffold(\n             key: globalKey, // using the built-in global key of the `View` for the scaffold or any other\n                             // widget provides the controller with a way to access them via getContext(), getState(), getStateKey()\n             body: child\n           ),\n         );\n     }\n \n     @override\n     ViewBuilder get mobileView =\u003e AppScaffold(\n         child: Column(\n             children: \u003cWidget\u003e[\n               // you can refresh manually inside the controller\n               // using refreshUI()\n               ControlledWidgetBuilder\u003cCounterController\u003e(\n                 builder: (context, controller) {\n                   return Text('Counter on mobile view ${controller.counter.toString()}');\n                 }\n               ),\n             ],\n           )\n      );\n\n     @override\n     ViewBuilder get tabletBuilder =\u003e AppScaffold(\n       child: Column(\n           children: \u003cWidget\u003e[\n             // you can refresh manually inside the controller\n             // using refreshUI()\n             ControlledWidgetBuilder\u003cCounterController\u003e(\n               builder: (context, controller) {\n                 return Text('Counter on tablet view ${controller.counter.toString()}');\n               }\n             ),\n           ],\n         )\n     );\n\n     @override\n     ViewBuilder get desktopBuilder =\u003e AppScaffold(\n        child: Row(\n            children: \u003cWidget\u003e[\n              // you can refresh manually inside the controller\n              // using refreshUI()\n              ControlledWidgetBuilder\u003cCounterController\u003e(\n                builder: (context, controller) {\n                  return Text('Counter on desktop view ${controller.counter.toString()}');\n                }\n              ),\n            ],\n          )\n      );\n}\n```\n\n##### Widgets with Common Controller\nIn the event that multiple widgets need to use the same `Controller` of a certain `Page`,\nthe `Controller` can be retrieved inside the children widgets of that page via \n`FlutterCleanArchitecture.getController\u003cHomeController\u003e(context)`.\n\nFor example:\n\n```dart\n\nimport '../pages/home/home_controller.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_clean_architecture/flutter_clean_architecture.dart';\n\nclass HomePageButton extends StatelessWidget {\n  final String text;\n  HomePageButton({@required this.text});\n\n  @override\n  Widget build(BuildContext context) {\n    // use a common controller assuming HomePageButton is always a child of Home\n    HomeController controller =\n        FlutterCleanArchitecture.getController\u003cHomeController\u003e(context);\n    return GestureDetector(\n      onTap: controller.buttonPressed,\n      child: Container(\n        height: 50.0,\n        alignment: FractionalOffset.center,\n        decoration: BoxDecoration(\n          color: Color.fromRGBO(230, 38, 39, 1.0),\n          borderRadius: BorderRadius.circular(25.0),\n        ),\n        child: Text(\n          text,\n          style: const TextStyle(\n              color: Colors.white,\n              fontSize: 20.0,\n              fontWeight: FontWeight.w300,\n              letterSpacing: 0.4),\n        ),\n      ),\n    );\n  }\n}\n\n```\n\n\n#### Controller \n\n```dart\nimport 'package:flutter_clean_architecture/flutter_clean_architecture.dart';\n\nclass CounterController extends Controller {\n  int counter;\n  final LoginPresenter presenter;\n  CounterController() : counter = 0, presenter = LoginPresenter(), super();\n\n  void increment() {\n    counter++;\n  }\n\n  /// Shows a snackbar\n  void showSnackBar() {\n    ScaffoldState scaffoldState = getState(); // get the state, in this case, the scaffold\n    scaffoldState.showSnackBar(SnackBar(content: Text('Hi')));\n  }\n\n  @override\n  void initListeners() {\n    // Initialize presenter listeners here\n    // These will be called upon success, failure, or data retrieval after usecase execution\n     presenter.loginOnComplete = () =\u003e print('Login Successful');\n     presenter.loginOnError = (e) =\u003e print(e);\n     presenter.loginOnNext = () =\u003e print(\"onNext\");\n  }\n\n  void login() {\n      // pass appropriate credentials here\n      // assuming you have text fields to retrieve them and whatnot\n      presenter.login();\n  }\n}\n\n```\n\n#### Presenter\n```dart\nimport 'package:flutter_clean_architecture/flutter_clean_architecture.dart'\n    as clean;\n\nclass LoginPresenter extends clean.Presenter {\n\n  Function loginOnComplete; // alternatively `void loginOnComplete();`\n  Function loginOnError;\n  Function loginOnNext; // not needed in the case of a login presenter\n\n  final LoginUseCase loginUseCase;\n  // dependency injection from controller\n  LoginPresenter(authenticationRepo): loginUseCase = LoginUseCase(authenticationRepo);\n\n  /// login function called by the controller\n  void login(String email, String password) {\n    loginUseCase.execute(_LoginUseCaseObserver(this), LoginUseCaseParams(email, password));\n  }\n\n   /// Disposes of the [LoginUseCase] and unsubscribes\n   @override\n   void dispose() {\n     _loginUseCase.dispose();\n   }\n}\n\n/// The [Observer] used to observe the `Stream` of the [LoginUseCase]\nclass _LoginUseCaseObserver extends clean.Observer\u003cvoid\u003e{\n\n  // The above presenter\n  // This is not optimal, but it is a workaround due to dart limitations. Dart does\n  // not support inner classes or anonymous classes.\n  final LoginPresenter loginPresenter;\n\n  _LoginUseCaseObserver(this.loginPresenter);\n\n  /// implement if the `Stream` emits a value\n  // in this case, unnecessary\n  void onNext(_) {}\n\n  /// Login is successful, trigger event in [LoginController]\n  void onComplete() {\n    // any cleaning or preparation goes here\n    assert(loginPresenter.loginOnComplete != null);\n    loginPresenter.loginOnComplete();\n\n  }\n\n  /// Login was unsuccessful, trigger event in [LoginController]\n  void onError(e) {\n    // any cleaning or preparation goes here\n    assert(loginPresenter.loginOnError != null);\n    loginPresenter.loginOnError(e);\n  }\n}\n\n```\n#### UseCase\n```dart\nimport 'package:flutter_clean_architecture/flutter_clean_architecture.dart';\n\n// In this case, no parameters were needed. Hence, void. Otherwise, change to appropriate.\nclass LoginUseCase extends CompletableUseCase\u003cLoginUseCaseParams\u003e {\n  final AuthenticationRepository _authenticationRepository; // some dependency to be injected\n                                          // the functionality is hidden behind this\n                                          // abstract class defined in the Domain module\n                                          // It should be implemented inside the Data or Device\n                                          // module and passed polymorphically.\n\n  LoginUseCase(this._authenticationRepository);\n\n  @override\n  // Since the parameter type is void, `_` ignores the parameter. Change according to the type\n  // used in the template.\n  Future\u003cStream\u003cvoid\u003e\u003e buildUseCaseStream(params) async {\n    final StreamController controller = StreamController();\n    try {\n        // assuming you pass credentials here\n      await _authenticationRepository.authenticate(email: params.email, password: params.password);\n      logger.finest('LoginUseCase successful.');\n      // triggers onComplete\n      controller.close();\n    } catch (e) {\n      print(e);\n      logger.severe('LoginUseCase unsuccessful.');\n      // Trigger .onError\n      controller.addError(e);\n    }\n    return controller.stream;\n  }\n}\n\nclass LoginUseCaseParams {\n    final String email;\n    final String password;\n    LoginUseCaseParams(this.email, this.password);\n}\n```\n##### Background UseCase\nA usecase can be made to run on a separate isolate using the `BackgroundUseCase` class. \nImplementing this kind of usecase is a little different than a regular usecase due to the constraints of an isolate.\nIn order to create a `BackgroundUseCase`, simply extend the class and override the `buildUseCaseTask` method.\nThis method should return a `UseCaseTask`, which is just a function that has a void return type and takes a\n`BackgroundUseCaseParameters` parameter. This method should be static and will contain all the code you wish to run\non a separate isolate. This method should communicate with the main isolate using the `port` provided in the `BackgroundUseCaseParameters`\nas follows. This example is of a `BackgroundUseCase` that performs matrix multiplication.\n\n```dart\n\nclass MatMulUseCase extends BackgroundUseCase\u003cList\u003cList\u003cdouble\u003e\u003e, MatMulUseCaseParams\u003e {\n\n  // must be overridden\n  @override\n  buildUseCaseTask() {\n    return matmul;  // returns the static method that contains the code to be run on an isolate\n  }\n\n  /// This method will be executed on a separate isolate. The [params] contain all the data and the sendPort \n  /// needed\n  static void matmul(BackgroundUseCaseParams params) async {\n    MatMulUseCaseParams matMulParams = params.params as MatMulUseCaseParams;\n    List\u003cList\u003cdouble\u003e\u003e result = List\u003cList\u003cdouble\u003e\u003e.generate(\n        10, (i) =\u003e List\u003cdouble\u003e.generate(10, (j) =\u003e 0));\n\n    for (int i = 0; i \u003c matMulParams.mat1.length; i++) {\n      for (int j = 0; j \u003c matMulParams.mat1.length; j++) {\n        for (int k = 0; k \u003c matMulParams.mat1.length; k++) {\n          result[i][j] += matMulParams.mat1[i][k] * matMulParams.mat2[k][j];\n        }\n      }\n    }\n    // send the result back to the main isolate\n    // this will be forwarded to the observer listneres\n    params.port.send(BackgroundUseCaseMessage(data: result));\n\n  }\n}\n```\nJust like a regular [UseCase], a parameter class is recommended for any [BackgroundUseCase].\nAn example corresponding to the above example would be\n\n ```dart\nclass MatMulUseCaseParams {\n  List\u003cList\u003cdouble\u003e\u003e mat1;\n  List\u003cList\u003cdouble\u003e\u003e mat2;\n  MatMulUseCaseParams(this.mat1, this.mat2);\n  MatMulUseCaseParams.random() {\n    var size = 10;\n    mat1 = List\u003cList\u003cdouble\u003e\u003e.generate(size,\n        (i) =\u003e List\u003cdouble\u003e.generate(size, (j) =\u003e i.toDouble() * size + j));\n\n    mat2 = List\u003cList\u003cdouble\u003e\u003e.generate(size,\n        (i) =\u003e List\u003cdouble\u003e.generate(size, (j) =\u003e i.toDouble() * size + j));\n  }\n}\n```\n\n#### Repository in Domain\n\n```dart\n\nabstract class AuthenticationRepository {\n  Future\u003cvoid\u003e register(\n      {@required String firstName,\n      @required String lastName,\n      @required String email,\n      @required String password});\n\n  /// Authenticates a user using his [username] and [password]\n  Future\u003cvoid\u003e authenticate(\n      {@required String email, @required String password});\n\n  /// Returns whether the [User] is authenticated.\n  Future\u003cbool\u003e isAuthenticated();\n\n  /// Returns the current authenticated [User].\n  Future\u003cUser\u003e getCurrentUser();\n\n  /// Resets the password of a [User]\n  Future\u003cvoid\u003e forgotPassword(String email);\n\n  /// Logs out the [User]\n  Future\u003cvoid\u003e logout();\n}\n\n```\nThis repository should be implemented in **Data** layer\n\n```dart\n\nclass DataAuthenticationRepository extends AuthenticationRepository {\n  // singleton\n  static DataAuthenticationRepository _instance = DataAuthenticationRepository._internal();\n  DataAuthenticationRepository._internal();\n  factory DataAuthenticationRepository() =\u003e _instance;\n\n    @override\n  Future\u003cvoid\u003e register(\n      {@required String firstName,\n      @required String lastName,\n      @required String email,\n      @required String password}) {\n          // TODO: implement\n      }\n\n  /// Authenticates a user using his [username] and [password]\n  @override\n  Future\u003cvoid\u003e authenticate(\n      {@required String email, @required String password}) {\n          // TODO: implement\n      }\n\n  /// Returns whether the [User] is authenticated.\n  @override\n  Future\u003cbool\u003e isAuthenticated() {\n      // TODO: implement\n  }\n\n  /// Returns the current authenticated [User].\n  @override\n  Future\u003cUser\u003e getCurrentUser() {\n      // TODO: implement\n  }\n\n  /// Resets the password of a [User]\n  @override\n  Future\u003cvoid\u003e forgotPassword(String email) {\n      // TODO: implement\n  }\n\n  /// Logs out the [User]\n  @override\n  Future\u003cvoid\u003e logout() {\n      // TODO: implement\n  }\n}\n```\nIf the repository is platform-related, implement it in the **Device** layer.\n\n#### Entity \nDefined in **Domain** layer.\n```dart\n\nclass User {\n    final String name;\n    final String email;\n    final String uid;\n    User(this.name, this.email, this.uid);\n}\n\n```\n\nCheckout a small example [here](./example/) and a full application built [here](https://github.com/ShadyBoukhary/Axion-Technologies-HnH).\n\n## Authors\n**[Shady Boukhary](https://github.com/ShadyBoukhary)** \n**[Rafael Monteiro](https://github.com/rafaelcmm)**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FShadyBoukhary%2Fflutter_clean_architecture","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FShadyBoukhary%2Fflutter_clean_architecture","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FShadyBoukhary%2Fflutter_clean_architecture/lists"}