├── .gitignore ├── .metadata ├── LICENSE ├── Makefile ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── stuart │ │ │ │ └── flutter_modular_architecture │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── documentation ├── architecture-1.png ├── architecture-2.png ├── screenshot-1.png ├── screenshot-2.png └── screenshot-3.png ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── app.dart ├── dependencies.dart ├── main.dart └── navigation.dart ├── packages ├── features │ ├── bike_services │ │ ├── lib │ │ │ ├── bike_services_features.dart │ │ │ ├── navigation_intents.dart │ │ │ └── src │ │ │ │ ├── blocs │ │ │ │ └── services │ │ │ │ │ ├── bloc.dart │ │ │ │ │ ├── event.dart │ │ │ │ │ ├── state.dart │ │ │ │ │ └── state.freezed.dart │ │ │ │ ├── dtos │ │ │ │ └── service_dto.dart │ │ │ │ ├── mappers │ │ │ │ └── service_mapper.dart │ │ │ │ └── pages │ │ │ │ └── service_selector_page.dart │ │ ├── pubspec.lock │ │ └── pubspec.yaml │ └── bike_stations │ │ ├── lib │ │ ├── bike_stations_features.dart │ │ ├── navigation_intents.dart │ │ └── src │ │ │ ├── blocs │ │ │ └── service_stations │ │ │ │ ├── bloc.dart │ │ │ │ ├── event.dart │ │ │ │ ├── state.dart │ │ │ │ └── state.freezed.dart │ │ │ ├── dtos │ │ │ └── station_dto.dart │ │ │ ├── extensions │ │ │ └── distance_extension.dart │ │ │ ├── mappers │ │ │ └── station_mapper.dart │ │ │ ├── pages │ │ │ ├── favorite_stations_page.dart │ │ │ └── service_stations_page.dart │ │ │ └── widgets │ │ │ └── station_list_item.dart │ │ ├── pubspec.lock │ │ └── pubspec.yaml ├── shared │ ├── all │ │ └── core │ │ │ ├── lib │ │ │ ├── core.dart │ │ │ └── src │ │ │ │ ├── dependencies │ │ │ │ ├── dependency_configuration_context.dart │ │ │ │ ├── dependency_configurator.dart │ │ │ │ └── injector.dart │ │ │ │ └── failures │ │ │ │ ├── failure.dart │ │ │ │ ├── network_failure.dart │ │ │ │ └── permission_failure.dart │ │ │ ├── pubspec.lock │ │ │ └── pubspec.yaml │ ├── features │ │ └── intent_launcher │ │ │ ├── lib │ │ │ ├── intent_launcher.dart │ │ │ └── src │ │ │ │ ├── extensions │ │ │ │ ├── build_context_extension.dart │ │ │ │ └── widget_extension.dart │ │ │ │ ├── inherited_intent_launcher.dart │ │ │ │ ├── intent_launcher.dart │ │ │ │ ├── intent_launcher_impl.dart │ │ │ │ ├── intent_not_registered_exception.dart │ │ │ │ └── intents │ │ │ │ └── navigation_intent.dart │ │ │ ├── pubspec.lock │ │ │ └── pubspec.yaml │ └── use_cases │ │ └── .keep └── use_cases │ ├── bike_services │ ├── lib │ │ ├── bike_services_use_cases.dart │ │ ├── dependency_configurator.dart │ │ ├── private_ports.dart │ │ └── src │ │ │ ├── models │ │ │ ├── city.dart │ │ │ ├── city.freezed.dart │ │ │ ├── service.dart │ │ │ └── service.freezed.dart │ │ │ ├── repositories │ │ │ └── service_repository.dart │ │ │ ├── responses │ │ │ └── sorted_services_response.dart │ │ │ └── use_cases │ │ │ ├── get_all_services.dart │ │ │ ├── get_all_services_sorted_by_distance.dart │ │ │ └── impl │ │ │ ├── get_all_services_impl.dart │ │ │ └── get_all_services_sorted_by_distance_impl.dart │ ├── packages │ │ └── real_adapters │ │ │ ├── lib │ │ │ ├── dependency_configurator.dart │ │ │ └── src │ │ │ │ ├── data_sources │ │ │ │ └── service_api │ │ │ │ │ ├── dtos │ │ │ │ │ ├── location_dto.dart │ │ │ │ │ ├── location_dto.g.dart │ │ │ │ │ ├── service_dto.dart │ │ │ │ │ └── service_dto.g.dart │ │ │ │ │ ├── mappers │ │ │ │ │ └── service_dto_mapper.dart │ │ │ │ │ └── service_api_data_source.dart │ │ │ │ └── repositories │ │ │ │ └── service_repository_impl.dart │ │ │ ├── pubspec.lock │ │ │ └── pubspec.yaml │ ├── pubspec.lock │ └── pubspec.yaml │ ├── bike_stations │ ├── lib │ │ ├── bike_stations_use_cases.dart │ │ ├── dependency_configurator.dart │ │ ├── private_ports.dart │ │ └── src │ │ │ ├── models │ │ │ ├── station.dart │ │ │ └── station.freezed.dart │ │ │ ├── repositories │ │ │ └── station_repository.dart │ │ │ ├── responses │ │ │ └── sorted_stations_response.dart │ │ │ └── use_cases │ │ │ ├── get_service_stations.dart │ │ │ ├── get_service_stations_sorted_by_distance.dart │ │ │ └── impl │ │ │ ├── get_service_stations_impl.dart │ │ │ └── get_service_stations_sorted_by_distance_impl.dart │ ├── packages │ │ └── real_adapters │ │ │ ├── lib │ │ │ ├── dependency_configurator.dart │ │ │ └── src │ │ │ │ ├── data_sources │ │ │ │ └── service_api │ │ │ │ │ ├── dtos │ │ │ │ │ ├── station_dto.dart │ │ │ │ │ ├── station_dto.g.dart │ │ │ │ │ ├── station_extra_dto.dart │ │ │ │ │ └── station_extra_dto.g.dart │ │ │ │ │ ├── mappers │ │ │ │ │ └── service_dto_mapper.dart │ │ │ │ │ └── station_api_data_source.dart │ │ │ │ └── repositories │ │ │ │ └── station_repository_impl.dart │ │ │ ├── pubspec.lock │ │ │ └── pubspec.yaml │ ├── pubspec.lock │ └── pubspec.yaml │ └── location │ ├── lib │ ├── dependency_configurator.dart │ ├── location_use_cases.dart │ ├── private_ports.dart │ └── src │ │ ├── models │ │ ├── accuracy.dart │ │ └── location_permission.dart │ │ ├── repositories │ │ └── location_repository.dart │ │ └── use_cases │ │ ├── ask_location_permission.dart │ │ ├── get_current_location.dart │ │ ├── get_last_know_location.dart │ │ ├── impl │ │ ├── ask_location_permission_impl.dart │ │ ├── get_current_location_impl.dart │ │ ├── get_last_know_location_impl.dart │ │ └── is_location_service_enabled_impl.dart │ │ └── is_location_service_enabled.dart │ ├── packages │ ├── fake_adapters │ │ ├── lib │ │ │ ├── dependency_configurator.dart │ │ │ └── src │ │ │ │ └── repositories │ │ │ │ └── location_repository_impl.dart │ │ ├── pubspec.lock │ │ └── pubspec.yaml │ └── real_adapters │ │ ├── lib │ │ ├── dependency_configurator.dart │ │ └── src │ │ │ ├── mappers │ │ │ ├── accuracy_mapper.dart │ │ │ ├── location_permission_mapper.dart │ │ │ └── position_mapper.dart │ │ │ └── repositories │ │ │ └── location_repository_impl.dart │ │ ├── pubspec.lock │ │ └── pubspec.yaml │ ├── pubspec.lock │ └── pubspec.yaml ├── pubspec.lock ├── pubspec.yaml └── test └── widget_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: c860cba910319332564e1e9d470a17074c1f2dfd 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Stuart 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PUBSPEC_YAML = $(shell find . -name pubspec.yaml) 2 | PUBSPEC_LOCK = $(PUBSPEC_YAML:.yaml=.lock) 3 | 4 | # Runs `flutter pub get` in the packages that `pubspec.yaml` was modified. 5 | pub_get: $(PUBSPEC_LOCK) 6 | 7 | %pubspec.lock: %pubspec.yaml 8 | @cd $(@D) && \ 9 | flutter pub get 10 | 11 | # Runs `flutter pub get` in all the project packages. 12 | pub_get_all: 13 | @find . -name pubspec.yaml -exec echo "### Getting packages for {}" \; \ 14 | -execdir flutter pub get \; 15 | 16 | # Runs `flutter pub run build_runner build` in all the project packages. 17 | generate_sources_all: 18 | @find . -name pubspec.yaml -exec echo "### Generating sources for {}" \; \ 19 | -execdir flutter pub run build_runner build --delete-conflicting-outputs \; 20 | 21 | # Runs `flutter clean` in all the project packages. 22 | clean_all: 23 | @find . -name pubspec.yaml -exec echo "### Cleaning {}" \; \ 24 | -execdir flutter clean \; 25 | 26 | .PHONY: pub_get pub_get_all generate_sources_all clean_all 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bici sample app 2 | 3 | With this app you can find available bikes and e-bikes near to you. The app allows you 4 | to locate bikes in a database that containes real time info of the public bicycle 5 | services from more than 400 cities. 6 | 7 | This project has been created as a demonstration of the modular architecture used 8 | in Flutter projects at [Stuart](https://stuart.com/blog/tech/). 9 | 10 | This app relies on the public [API](http://api.citybik.es/v2/) provided by 11 | [CityBikes](https://citybik.es/). 12 | 13 | | ![Screenshot 1](documentation/screenshot-1.png) | ![Screenshot 2](documentation/screenshot-2.png) | ![Screenshot 3](documentation/screenshot-3.png) | 14 | |-------------------------------------------------|-------------------------------------------------|-------------------------------------------------| 15 | 16 | Where are the maps? For sake of simplicity, this project doesn't contain maps. This is to avoid 17 | the developers having to set a Google Maps API key in the project. 18 | 19 | # How to run it 20 | 21 | 1. Install all dependencies: 22 | ```shell 23 | make pub_get_all 24 | ``` 25 | 2. With a device connected run the app: 26 | ```shell 27 | flutter run 28 | ``` 29 | Please check [Makefile](Makefile) to discover all the available `make` commands. 30 | 31 | # Architecture overview 32 | 33 | This architecture will be mainly composed by **Use Cases** and **Features** packages that will 34 | work as independent modules. 35 | 36 | ![Architecture 1](documentation/architecture-1.png) 37 | 38 | This architecture has deeply inspired the talk 39 | [Android at Scale @Square](https://www.droidcon.com/2019/11/15/android-at-scale-square/). 40 | 41 | ## What is a Use Case? 42 | 43 | A Use Case orchestrates the flow of data to and from the Entities by implementing application 44 | specific business rules. A Use Case belongs to the Domain layer and it is used by classes from 45 | Presentation layer. 46 | 47 | For instance if in the Presentation layer there are some classes grouped in a Feature Package 48 | implementing all the UI necessary for a user to log in, these classes for example will depend on 49 | some Authentication Use Cases Package that could expose a LogInUseCase that will login the user 50 | after receive a username and a password as parameters. 51 | 52 | ## What is a Use Cases package? 53 | 54 | A Use Cases Package is the one that exposes Use Cases that can be used by Features Packages or 55 | other Use Cases Packages. These Use Cases grouped in the same package are related to each other. 56 | Some examples: 57 | 58 | **Authentication Use Cases Package:** Log in user, register new user, remember password, etc. 59 | 60 | **Articles Use Cases Package:** Get all articles, create new article, archive article, etc. 61 | 62 | A Use Cases Package only exposes Use Cases as public interfaces and also the related Domain Models. 63 | The same package contains the implementations for all the declared Use Cases. This means that it 64 | contains all the logic that belongs to the domain layer. 65 | 66 | In the other hand, the package does not implement any logic that belongs to the data layer. This 67 | means that any Repository or other low level classes used by the Use Cases, needs to be 68 | implemented somewhere else. 69 | 70 | The place to implement all the data logic of the Use Cases is in the inner Adapter packages. These 71 | inner packages will implement interfaces exported in a file name `private_ports.dart`. This file 72 | will export interfaces like Repositories which the Use Cases will depend on. Each Use Cases Package 73 | will have at least one of the following inner packages: 74 | 75 | - **Real adapters:** Contains the real implementations for the interfaces defined in the 76 | private_ports.dart file. These are the implementations that are used at production. 77 | - **Fake adapters:** Contains fake implementations for the interfaces defined in the 78 | private_ports.dart file. This package can be used to wire up the dependencies for UI automated 79 | testing or to create development apps (Apps to develop and test certain features in a isolated 80 | context). 81 | 82 | :information_source: Notice here the use of words Ports and Adapters where they have the same 83 | meaning as in the Port and Adapters architecture (Or Hexagonal architecture). 84 | 85 | ![Architecture 2](documentation/architecture-2.png) 86 | 87 | ## What is a Features Package? 88 | 89 | Features Packages have the UI and presentation code to implement a specific functionality of the 90 | app. Normally are composed of BLoCs, Screens (Or pages), Widgets and data classes that will be 91 | used in the communication between BLoCs and Screens/Widgets. This means that a Features Package 92 | will contain only logic from the Presentation Layer and they will depend on Use Cases packages 93 | to get the implementation of the business logic needed for that feature. 94 | 95 | Here are some examples of possible Features Packages: 96 | 97 | - login: Groups all the screens related to login/register. 98 | - home: Groups all the code for the main screen. 99 | - articles: Groups all the code that let the user read articles. 100 | - publish_article: Groups all the code that let the user publish articles. 101 | - navigation_drawer: The side drawer with the menu to navigate. 102 | 103 | ## Dependency management 104 | 105 | This project uses construction-based dependency injection. This means that all the classes will 106 | receive their dependencies as parameters of their constructor. 107 | 108 | ```dart 109 | class GetUserByIdUseCase { 110 | final UserRepository userRepository; 111 | 112 | GetUserByIdUseCase(this.userRepository); // Dependencies injected by constructor 113 | 114 | @override 115 | Stream call(String userId) { 116 | return userRepository.getUserById(userId); 117 | } 118 | } 119 | ``` 120 | 121 | To satisfy these dependencies each package with concrete implementations will expose a file called 122 | `dependency_configurator.dart` with an implementation of an interface called 123 | [DependencyConfigurator](packages/shared/all/core/lib/src/dependencies/dependency_configurator.dart). 124 | This interface defines a method that must be implemented and it receives a `GetIt` instance that can 125 | be used to register all the dependencies that are provided by the package. 126 | [GetIt](https://pub.dev/packages/get_it) is simple but powerful service locator, but if you want 127 | you can use any other tool with this architecture. `DependencyConfigurator` also receives a 128 | `DependencyConfiguratorContext` instance with extra configuration info. 129 | 130 | ```dart 131 | // dependency_configuration_context.dart 132 | class DependencyConfigurationContext { 133 | final String apiBaseUrl; 134 | 135 | ConfigurationContext({ 136 | required this.apiBaseUrl, 137 | }); 138 | } 139 | 140 | // dependency_configurator.dart 141 | abstract class DependencyConfigurator { 142 | void configureDependencies( 143 | DependencyConfiguratorContext context, 144 | GetIt getIt, 145 | ); 146 | } 147 | ``` 148 | 149 | Then these `dependency_configurator.dart` can be used from a main module to register all the 150 | different dependencies. For instance from main method we can call the following 151 | `configureDependencies()` method: 152 | 153 | ```dart 154 | // dependencies.dart 155 | final configurators = [ 156 | ArticlesDependencyConfigurator(), 157 | ArticlesFakeAdaptersDependencyConfigurator(), 158 | ]; 159 | 160 | void configureDependencies(DependencyConfigurationContext context) { 161 | for (var configurator in configurators) { 162 | configurator.configureDependencies(context, GetIt.instance); 163 | } 164 | } 165 | ``` 166 | 167 | When a configured dependency needs to be injected, it can be done by using the method `inject()`. 168 | These method will be used normally from code located in the Presentation layer in this way: 169 | 170 | ```dart 171 | class ListUsersScreen extends StatelessWidget { 172 | @override 173 | Widget build(BuildContext context) { 174 | return BlocProvider( 175 | create: (context) => ListUsersBloc( 176 | inject(), // Use of the inject() method 177 | inject(), 178 | ), 179 | child: Container(), 180 | ); 181 | } 182 | } 183 | ``` 184 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | analyzer: 13 | language: 14 | strict-casts: true 15 | strict-raw-types: true 16 | 17 | linter: 18 | # The lint rules applied to this project can be customized in the 19 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 20 | # included above or to enable additional rules. A list of all available lints 21 | # and their documentation is published at 22 | # https://dart-lang.github.io/linter/lints/index.html. 23 | # 24 | # Instead of disabling a lint rule for the entire project in the 25 | # section below, it can also be suppressed for a single line of code 26 | # or a specific dart file by using the `// ignore: name_of_lint` and 27 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 28 | # producing the lint. 29 | rules: 30 | prefer_single_quotes: true 31 | prefer_relative_imports: true 32 | 33 | # Additional information about this file can be found at 34 | # https://dart.dev/guides/language/analysis-options 35 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion flutter.compileSdkVersion 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | 36 | kotlinOptions { 37 | jvmTarget = '1.8' 38 | } 39 | 40 | sourceSets { 41 | main.java.srcDirs += 'src/main/kotlin' 42 | } 43 | 44 | defaultConfig { 45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 46 | applicationId "com.stuart.flutter_modular_architecture" 47 | minSdkVersion flutter.minSdkVersion 48 | targetSdkVersion flutter.targetSdkVersion 49 | versionCode flutterVersionCode.toInteger() 50 | versionName flutterVersionName 51 | } 52 | 53 | buildTypes { 54 | release { 55 | // TODO: Add your own signing config for the release build. 56 | // Signing with the debug keys for now, so `flutter run --release` works. 57 | signingConfig signingConfigs.debug 58 | } 59 | } 60 | } 61 | 62 | flutter { 63 | source '../..' 64 | } 65 | 66 | dependencies { 67 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 68 | } 69 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 20 | 24 | 28 | 29 | 30 | 31 | 32 | 33 | 35 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/stuart/flutter_modular_architecture/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.stuart.flutter_modular_architecture 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /documentation/architecture-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/documentation/architecture-1.png -------------------------------------------------------------------------------- /documentation/architecture-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/documentation/architecture-2.png -------------------------------------------------------------------------------- /documentation/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/documentation/screenshot-1.png -------------------------------------------------------------------------------- /documentation/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/documentation/screenshot-2.png -------------------------------------------------------------------------------- /documentation/screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/documentation/screenshot-3.png -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - geolocator_apple (1.2.0): 4 | - Flutter 5 | 6 | DEPENDENCIES: 7 | - Flutter (from `Flutter`) 8 | - geolocator_apple (from `.symlinks/plugins/geolocator_apple/ios`) 9 | 10 | EXTERNAL SOURCES: 11 | Flutter: 12 | :path: Flutter 13 | geolocator_apple: 14 | :path: ".symlinks/plugins/geolocator_apple/ios" 15 | 16 | SPEC CHECKSUMS: 17 | Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a 18 | geolocator_apple: cc556e6844d508c95df1e87e3ea6fa4e58c50401 19 | 20 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c 21 | 22 | COCOAPODS: 1.11.3 23 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Bici 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | flutter_modular_architecture 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | NSLocationWhenInUseUsageDescription 28 | This app needs access to location when open. 29 | UILaunchStoryboardName 30 | LaunchScreen 31 | UIMainStoryboardFile 32 | Main 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UIViewControllerBasedStatusBarAppearance 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/app.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'navigation.dart'; 4 | 5 | class MyApp extends StatelessWidget { 6 | const MyApp({Key? key}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return MaterialApp( 11 | title: 'Bici', 12 | theme: ThemeData( 13 | primarySwatch: Colors.green, 14 | ), 15 | onGenerateRoute: onGenerateRoute, 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/dependencies.dart: -------------------------------------------------------------------------------- 1 | import 'package:_bike_services_use_cases/dependency_configurator.dart'; 2 | import 'package:_bike_services_use_cases_real_adapters/dependency_configurator.dart'; 3 | import 'package:_bike_stations_use_cases/dependency_configurator.dart'; 4 | import 'package:_bike_stations_use_cases_real_adapters/dependency_configurator.dart'; 5 | import 'package:_core/core.dart'; 6 | import 'package:_location_use_cases/dependency_configurator.dart'; 7 | import 'package:_location_use_cases_real_adapters/dependency_configurator.dart'; 8 | import 'package:get_it/get_it.dart'; 9 | 10 | final configurators = [ 11 | // Bike services 12 | BikeServicesRealAdaptersDependencyConfigurator(), 13 | BikeServicesDependencyConfigurator(), 14 | // Bike stations 15 | BikeStationsRealAdaptersDependencyConfigurator(), 16 | BikeStationsDependencyConfigurator(), 17 | // Location 18 | LocationDependencyConfigurator(), 19 | LocationRealAdaptersDependencyConfigurator(), 20 | ]; 21 | 22 | void configureDependencies(DependencyConfigurationContext context) { 23 | for (var configurator in configurators) { 24 | configurator.configureDependencies(context, GetIt.instance); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import 'app.dart'; 5 | import 'dependencies.dart'; 6 | 7 | void main() { 8 | configureDependencies(const DependencyConfigurationContext()); 9 | runApp(const MyApp()); 10 | } 11 | -------------------------------------------------------------------------------- /lib/navigation.dart: -------------------------------------------------------------------------------- 1 | import 'package:_bike_services_features/bike_services_features.dart'; 2 | import 'package:_bike_services_features/navigation_intents.dart'; 3 | import 'package:_bike_stations_features/bike_stations_features.dart'; 4 | import 'package:_bike_stations_features/navigation_intents.dart'; 5 | import 'package:_intent_launcher/intent_launcher.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | final _launcher = IntentLauncher() 9 | ..onNavigationIntent((context, intent) { 10 | return Navigator.pushNamed( 11 | context, 12 | '/stations', 13 | arguments: [intent.serviceId, intent.serviceName], 14 | ); 15 | }) 16 | ..onNavigationIntent((context, intent) { 17 | return Navigator.pushNamed(context, '/stations/favorites'); 18 | }); 19 | 20 | Route? onGenerateRoute(RouteSettings settings) { 21 | if ('/' == settings.name) { 22 | return _route((_) => const ServiceSelectorPage().wrapWith(_launcher)); 23 | } else if ('/stations' == settings.name) { 24 | final args = settings.arguments as List; 25 | final serviceId = args[0]; 26 | final serviceName = args[1]; 27 | return _route((_) { 28 | return ServiceStationsPage( 29 | serviceId: serviceId, 30 | serviceName: serviceName, 31 | ).wrapWith(_launcher); 32 | }); 33 | } else if ('/stations/favorites' == settings.name) { 34 | return _route((_) => const FavoriteStationsPage().wrapWith(_launcher)); 35 | } 36 | return null; 37 | } 38 | 39 | Route _route(WidgetBuilder builder) { 40 | return MaterialPageRoute(builder: builder); 41 | } 42 | -------------------------------------------------------------------------------- /packages/features/bike_services/lib/bike_services_features.dart: -------------------------------------------------------------------------------- 1 | // Pages 2 | export 'src/pages/service_selector_page.dart' show ServiceSelectorPage; 3 | -------------------------------------------------------------------------------- /packages/features/bike_services/lib/navigation_intents.dart: -------------------------------------------------------------------------------- 1 | import 'package:_intent_launcher/intent_launcher.dart'; 2 | 3 | class BikeServiceStations implements NavigationIntent { 4 | final String serviceId; 5 | final String serviceName; 6 | 7 | const BikeServiceStations({ 8 | required this.serviceId, 9 | required this.serviceName, 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /packages/features/bike_services/lib/src/blocs/services/bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:_bike_services_use_cases/bike_services_use_cases.dart'; 4 | import 'package:_location_use_cases/location_use_cases.dart'; 5 | import 'package:either_dart/either.dart'; 6 | import 'package:flutter_bloc/flutter_bloc.dart'; 7 | 8 | import '../../mappers/service_mapper.dart'; 9 | import 'event.dart'; 10 | import 'state.dart'; 11 | 12 | class ServicesBloc extends Bloc { 13 | final GetAllServicesSortedByDistance _getAllServicesSortedByDistance; 14 | 15 | final AskLocationPermission _askLocationPermission; 16 | 17 | ServicesBloc( 18 | this._getAllServicesSortedByDistance, 19 | this._askLocationPermission, 20 | ) : super(ServicesState.loading()) { 21 | on((event, emit) async { 22 | await _loadServices(emit); 23 | }); 24 | 25 | on((event, emit) async { 26 | emit(ServicesState.loading()); 27 | await _loadServices(emit); 28 | }); 29 | } 30 | 31 | Future _loadServices(Emitter emit) { 32 | return _askLocationPermission().then((_) { 33 | return _getAllServicesSortedByDistance().fold( 34 | (failure) => emit(ServicesState.failure()), 35 | (response) { 36 | final serviceDtos = response.services 37 | .map((service) => service.toServiceDto()) 38 | .toList(); 39 | emit(ServicesState.success( 40 | services: serviceDtos, 41 | showLocationPermissionWarning: response.sortedFrom == null, 42 | )); 43 | }, 44 | ); 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/features/bike_services/lib/src/blocs/services/event.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | @sealed 4 | abstract class ServicesEvent {} 5 | 6 | class NearServicesStarted implements ServicesEvent {} 7 | 8 | class NearServicesReloadPressed implements ServicesEvent {} 9 | -------------------------------------------------------------------------------- /packages/features/bike_services/lib/src/blocs/services/state.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | import '../../dtos/service_dto.dart'; 4 | 5 | part 'state.freezed.dart'; 6 | 7 | @freezed 8 | class ServicesState with _$ServicesState { 9 | factory ServicesState.loading() = ServiceSelectorLoadInProgress; 10 | 11 | factory ServicesState.success({ 12 | required List services, 13 | required bool showLocationPermissionWarning, 14 | }) = ServiceSelectorLoadSuccess; 15 | 16 | factory ServicesState.failure() = ServiceSelectorLoadFailure; 17 | } 18 | -------------------------------------------------------------------------------- /packages/features/bike_services/lib/src/dtos/service_dto.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | @immutable 4 | class ServiceDto { 5 | final String id; 6 | final String cityName; 7 | final String serviceName; 8 | 9 | const ServiceDto({ 10 | required this.id, 11 | required this.cityName, 12 | required this.serviceName, 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /packages/features/bike_services/lib/src/mappers/service_mapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:_bike_services_use_cases/bike_services_use_cases.dart'; 2 | 3 | import '../dtos/service_dto.dart'; 4 | 5 | extension ServiceMapper on Service { 6 | ServiceDto toServiceDto() { 7 | return ServiceDto( 8 | id: id, 9 | cityName: city.name, 10 | serviceName: name, 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/features/bike_services/lib/src/pages/service_selector_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:_intent_launcher/intent_launcher.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | 6 | import '../../navigation_intents.dart'; 7 | import '../blocs/services/bloc.dart'; 8 | import '../blocs/services/event.dart'; 9 | import '../blocs/services/state.dart'; 10 | 11 | class ServiceSelectorPage extends StatelessWidget { 12 | const ServiceSelectorPage({Key? key}) : super(key: key); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return BlocProvider( 17 | create: (BuildContext context) { 18 | return ServicesBloc( 19 | inject(), 20 | inject(), 21 | )..add(NearServicesStarted()); 22 | }, 23 | child: Scaffold( 24 | appBar: AppBar( 25 | leading: const Icon(Icons.directions_bike), 26 | title: const Text('Bici'), 27 | ), 28 | body: _buildBlocListener( 29 | context: context, 30 | child: _buildBody(context), 31 | ), 32 | ), 33 | ); 34 | } 35 | 36 | Widget _buildBlocListener({ 37 | required BuildContext context, 38 | required Widget child, 39 | }) { 40 | return BlocListener( 41 | listener: (context, state) { 42 | state.maybeMap( 43 | success: (success) { 44 | if (success.showLocationPermissionWarning) { 45 | _showLocationPermissionWarning(context); 46 | } 47 | }, 48 | orElse: () => null, 49 | ); 50 | }, 51 | child: child, 52 | ); 53 | } 54 | 55 | Widget _buildBody(BuildContext context) { 56 | return Padding( 57 | padding: const EdgeInsets.all(16.0), 58 | child: Column( 59 | crossAxisAlignment: CrossAxisAlignment.start, 60 | children: [ 61 | const SizedBox(height: 16.0), 62 | Center( 63 | child: Text( 64 | 'Welcome to Bici!', 65 | style: Theme.of(context).textTheme.headline5, 66 | ), 67 | ), 68 | const SizedBox(height: 32.0), 69 | Expanded( 70 | child: BlocBuilder( 71 | builder: (BuildContext context, state) { 72 | return state.map( 73 | loading: (state) => _buildStateLoading(context), 74 | success: (state) => _buildStateSuccess(context, state), 75 | failure: (state) => _buildStateFailure(context), 76 | ); 77 | }, 78 | ), 79 | ), 80 | ], 81 | ), 82 | ); 83 | } 84 | 85 | Widget _buildStateLoading(BuildContext context) { 86 | return const Center(child: CircularProgressIndicator()); 87 | } 88 | 89 | Widget _buildStateSuccess( 90 | BuildContext context, 91 | ServiceSelectorLoadSuccess state, 92 | ) { 93 | return Column( 94 | crossAxisAlignment: CrossAxisAlignment.start, 95 | children: [ 96 | Text( 97 | 'Please, select a bike service:', 98 | style: Theme.of(context).textTheme.titleMedium, 99 | ), 100 | const SizedBox(height: 16.0), 101 | Expanded( 102 | child: Material( 103 | child: ListView.builder( 104 | itemBuilder: (context, index) { 105 | final service = state.services[index]; 106 | return ListTile( 107 | title: Text(service.cityName), 108 | subtitle: Text(service.serviceName), 109 | onTap: () => context.go(BikeServiceStations( 110 | serviceId: service.id, 111 | serviceName: service.serviceName, 112 | )), 113 | ); 114 | }, 115 | itemCount: state.services.length, 116 | ), 117 | ), 118 | ), 119 | ], 120 | ); 121 | } 122 | 123 | Widget _buildStateFailure(BuildContext context) { 124 | return Center( 125 | child: Column( 126 | mainAxisAlignment: MainAxisAlignment.center, 127 | children: [ 128 | const Text('An unexpected network error occurred.'), 129 | const SizedBox(height: 8.0), 130 | ElevatedButton( 131 | onPressed: () { 132 | context.read().add(NearServicesReloadPressed()); 133 | }, 134 | child: const Text('Reload'), 135 | ), 136 | ], 137 | ), 138 | ); 139 | } 140 | 141 | void _showLocationPermissionWarning(BuildContext context) { 142 | final snackBar = SnackBar( 143 | content: Text( 144 | 'Please, enable location services and grant location permissions', 145 | style: Theme.of(context).textTheme.titleMedium, 146 | ), 147 | backgroundColor: Theme.of(context).errorColor, 148 | duration: const Duration(seconds: 15), 149 | ); 150 | ScaffoldMessenger.of(context).showSnackBar(snackBar); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /packages/features/bike_services/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _bike_services_features 2 | description: Bike services features 3 | 4 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 5 | 6 | environment: 7 | sdk: ">=2.16.2 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | 13 | cupertino_icons: ^1.0.2 14 | flutter_bloc: ^8.0.0 15 | freezed_annotation: ^2.0.3 16 | 17 | _core: 18 | path: ../../shared/all/core 19 | _intent_launcher: 20 | path: ../../shared/features/intent_launcher 21 | 22 | _bike_services_use_cases: 23 | path: ../../use_cases/bike_services 24 | _location_use_cases: 25 | path: ../../use_cases/location 26 | 27 | dev_dependencies: 28 | flutter_test: 29 | sdk: flutter 30 | 31 | build_runner: ^2.1.11 32 | flutter_lints: ^1.0.0 33 | freezed: ^2.0.3+1 34 | 35 | flutter: 36 | uses-material-design: true 37 | 38 | # To add assets to your application, add an assets section, like this: 39 | # assets: 40 | # - images/a_dot_burr.jpeg 41 | # - images/a_dot_ham.jpeg 42 | 43 | # An image asset can refer to one or more resolution-specific "variants", see 44 | # https://flutter.dev/assets-and-images/#resolution-aware. 45 | 46 | # For details regarding adding assets from package dependencies, see 47 | # https://flutter.dev/assets-and-images/#from-packages 48 | 49 | # To add custom fonts to your application, add a fonts section here, 50 | # in this "flutter" section. Each entry in this list should have a 51 | # "family" key with the font family name, and a "fonts" key with a 52 | # list giving the asset and other descriptors for the font. For 53 | # example: 54 | # fonts: 55 | # - family: Schyler 56 | # fonts: 57 | # - asset: fonts/Schyler-Regular.ttf 58 | # - asset: fonts/Schyler-Italic.ttf 59 | # style: italic 60 | # - family: Trajan Pro 61 | # fonts: 62 | # - asset: fonts/TrajanPro.ttf 63 | # - asset: fonts/TrajanPro_Bold.ttf 64 | # weight: 700 65 | # 66 | # For details regarding fonts from package dependencies, 67 | # see https://flutter.dev/custom-fonts/#from-packages 68 | -------------------------------------------------------------------------------- /packages/features/bike_stations/lib/bike_stations_features.dart: -------------------------------------------------------------------------------- 1 | // Pages 2 | export 'src/pages/favorite_stations_page.dart' show FavoriteStationsPage; 3 | export 'src/pages/service_stations_page.dart' show ServiceStationsPage; 4 | -------------------------------------------------------------------------------- /packages/features/bike_stations/lib/navigation_intents.dart: -------------------------------------------------------------------------------- 1 | import 'package:_intent_launcher/intent_launcher.dart'; 2 | 3 | class FavoriteBikeStations implements NavigationIntent { 4 | const FavoriteBikeStations(); 5 | } 6 | -------------------------------------------------------------------------------- /packages/features/bike_stations/lib/src/blocs/service_stations/bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:_bike_stations_use_cases/bike_stations_use_cases.dart'; 4 | import 'package:either_dart/either.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | 7 | import '../../mappers/station_mapper.dart'; 8 | import 'event.dart'; 9 | import 'state.dart'; 10 | 11 | class ServiceStationsBloc 12 | extends Bloc { 13 | final String _serviceId; 14 | 15 | final GetServiceStationsSortedByDistance _getServiceStationsSortedByDistance; 16 | 17 | ServiceStationsBloc( 18 | this._serviceId, 19 | this._getServiceStationsSortedByDistance, 20 | ) : super(ServiceStationsState.loading()) { 21 | on((event, emit) async { 22 | await _loadServices(emit); 23 | }); 24 | 25 | on((event, emit) async { 26 | emit(ServiceStationsState.loading()); 27 | await _loadServices(emit); 28 | }); 29 | } 30 | 31 | Future _loadServices(Emitter emit) { 32 | return _getServiceStationsSortedByDistance(_serviceId).fold( 33 | (left) => emit(ServiceStationsState.failure()), 34 | (response) { 35 | emit(ServiceStationsState.success( 36 | stations: response.stations 37 | .map((station) => station.toStationDto(response.sortedFrom)) 38 | .toList(), 39 | showLocationPermissionWarning: response.sortedFrom == null, 40 | )); 41 | }, 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/features/bike_stations/lib/src/blocs/service_stations/event.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | @sealed 4 | abstract class ServiceStationsEvent {} 5 | 6 | class ServiceStationsStarted implements ServiceStationsEvent {} 7 | 8 | class ServiceStationsReloadPressed implements ServiceStationsEvent {} 9 | -------------------------------------------------------------------------------- /packages/features/bike_stations/lib/src/blocs/service_stations/state.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | import '../../dtos/station_dto.dart'; 4 | 5 | part 'state.freezed.dart'; 6 | 7 | @freezed 8 | class ServiceStationsState with _$ServiceStationsState { 9 | factory ServiceStationsState.loading() = _ServiceStationsLoadInProgress; 10 | 11 | factory ServiceStationsState.success({ 12 | required List stations, 13 | required bool showLocationPermissionWarning, 14 | }) = ServiceStationsLoadSuccess; 15 | 16 | factory ServiceStationsState.failure() = ServiceStationsLoadFailure; 17 | } 18 | -------------------------------------------------------------------------------- /packages/features/bike_stations/lib/src/dtos/station_dto.dart: -------------------------------------------------------------------------------- 1 | import 'package:distance/distance.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | 4 | @immutable 5 | class StationDto { 6 | final String id; 7 | final String name; 8 | final int emptySlots; 9 | final int freeBikes; 10 | final int freeNormalBikes; 11 | final int freeEBikes; 12 | final Distance? distance; 13 | final bool isFavorite; 14 | 15 | const StationDto({ 16 | required this.id, 17 | required this.name, 18 | required this.emptySlots, 19 | required this.freeBikes, 20 | required this.freeNormalBikes, 21 | required this.freeEBikes, 22 | required this.distance, 23 | required this.isFavorite, 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /packages/features/bike_stations/lib/src/extensions/distance_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:distance/distance.dart'; 2 | 3 | extension DistanceExtension on Distance { 4 | String format() { 5 | if (inKilometers >= 1.0) { 6 | return '${inKilometers.toStringAsFixed(2)} km'; 7 | } else { 8 | return '${inMeters.toInt()} meters'; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/features/bike_stations/lib/src/mappers/station_mapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:_bike_stations_use_cases/bike_stations_use_cases.dart'; 2 | import 'package:latlong2/latlong.dart'; 3 | 4 | import '../dtos/station_dto.dart'; 5 | 6 | extension StationMapper on Station { 7 | StationDto toStationDto([LatLng? distanceFrom]) { 8 | return StationDto( 9 | id: id, 10 | name: name, 11 | emptySlots: emptySlots, 12 | freeBikes: freeBikes, 13 | freeNormalBikes: freeNormalBikes, 14 | freeEBikes: freeEBikes, 15 | distance: distanceFrom != null ? distanceTo(distanceFrom) : null, 16 | isFavorite: isFavorite, 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/features/bike_stations/lib/src/pages/favorite_stations_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:distance/distance.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../dtos/station_dto.dart'; 5 | import '../widgets/station_list_item.dart'; 6 | 7 | const _stations = [ 8 | StationDto( 9 | id: 'e02c5db9e6f6fca078798c9b2d486a81', 10 | name: 'Jardins De Can Ferrero/Pg.De La Zona Franca', 11 | emptySlots: 22, 12 | freeBikes: 9, 13 | freeNormalBikes: 2, 14 | freeEBikes: 7, 15 | distance: Distance(meters: 50), 16 | isFavorite: true, 17 | ), 18 | ]; 19 | 20 | class FavoriteStationsPage extends StatelessWidget { 21 | const FavoriteStationsPage({Key? key}) : super(key: key); 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return Scaffold( 26 | appBar: AppBar( 27 | title: const Text('Favorite stations'), 28 | ), 29 | body: Padding( 30 | padding: const EdgeInsets.all(16.0), 31 | child: ListView.builder( 32 | itemBuilder: (context, index) { 33 | final station = _stations[index]; 34 | return StationListItem(station: station); 35 | }, 36 | itemCount: _stations.length, 37 | ), 38 | ), 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/features/bike_stations/lib/src/pages/service_stations_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:_intent_launcher/intent_launcher.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | 6 | import '../../navigation_intents.dart'; 7 | import '../blocs/service_stations/bloc.dart'; 8 | import '../blocs/service_stations/event.dart'; 9 | import '../blocs/service_stations/state.dart'; 10 | import '../widgets/station_list_item.dart'; 11 | 12 | class ServiceStationsPage extends StatelessWidget { 13 | final String serviceId; 14 | final String serviceName; 15 | 16 | const ServiceStationsPage({ 17 | Key? key, 18 | required this.serviceId, 19 | required this.serviceName, 20 | }) : super(key: key); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return BlocProvider( 25 | create: (context) => ServiceStationsBloc( 26 | serviceId, 27 | inject(), 28 | )..add(ServiceStationsStarted()), 29 | child: Scaffold( 30 | appBar: _buildAppBar(context), 31 | body: _buildBlocListener( 32 | child: _buildBody(), 33 | ), 34 | ), 35 | ); 36 | } 37 | 38 | Widget _buildBlocListener({required Widget child}) { 39 | return BlocListener( 40 | listener: (context, state) { 41 | state.maybeMap( 42 | success: (success) { 43 | if (success.showLocationPermissionWarning) { 44 | _showLocationPermissionWarning(context); 45 | } 46 | }, 47 | orElse: () => null, 48 | ); 49 | }, 50 | child: child, 51 | ); 52 | } 53 | 54 | AppBar _buildAppBar(BuildContext context) { 55 | return AppBar( 56 | title: Text('$serviceName stations'), 57 | actions: [ 58 | IconButton( 59 | icon: const Icon(Icons.star), 60 | tooltip: 'Favorite stations', 61 | onPressed: () => context.go(const FavoriteBikeStations()), 62 | ), 63 | ], 64 | ); 65 | } 66 | 67 | Widget _buildBody() { 68 | return Padding( 69 | padding: const EdgeInsets.all(16.0), 70 | child: BlocBuilder( 71 | builder: (context, state) { 72 | return state.map( 73 | loading: (state) => _buildStateLoading(context), 74 | success: (state) => _buildStateSuccess(context, state), 75 | failure: (state) => _buildStateFailure(context), 76 | ); 77 | }, 78 | ), 79 | ); 80 | } 81 | 82 | Widget _buildStateLoading(BuildContext context) { 83 | return const Center(child: CircularProgressIndicator()); 84 | } 85 | 86 | Widget _buildStateSuccess( 87 | BuildContext context, ServiceStationsLoadSuccess state) { 88 | return ListView.builder( 89 | itemBuilder: (context, index) { 90 | final station = state.stations[index]; 91 | return StationListItem(station: station); 92 | }, 93 | itemCount: state.stations.length, 94 | ); 95 | } 96 | 97 | Widget _buildStateFailure(BuildContext context) { 98 | return Center( 99 | child: Column( 100 | mainAxisAlignment: MainAxisAlignment.center, 101 | children: [ 102 | const Text('An unexpected network error occurred.'), 103 | const SizedBox(height: 8.0), 104 | ElevatedButton( 105 | onPressed: () { 106 | context 107 | .read() 108 | .add(ServiceStationsReloadPressed()); 109 | }, 110 | child: const Text('Reload'), 111 | ), 112 | ], 113 | ), 114 | ); 115 | } 116 | 117 | void _showLocationPermissionWarning(BuildContext context) { 118 | final snackBar = SnackBar( 119 | content: Text( 120 | 'Please, enable location services and grant location permissions', 121 | style: Theme.of(context).textTheme.titleMedium, 122 | ), 123 | backgroundColor: Theme.of(context).errorColor, 124 | duration: const Duration(seconds: 15), 125 | ); 126 | ScaffoldMessenger.of(context).showSnackBar(snackBar); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /packages/features/bike_stations/lib/src/widgets/station_list_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../dtos/station_dto.dart'; 4 | import '../extensions/distance_extension.dart'; 5 | 6 | class StationListItem extends StatelessWidget { 7 | const StationListItem({ 8 | Key? key, 9 | required this.station, 10 | }) : super(key: key); 11 | 12 | final StationDto station; 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return ListTile( 17 | leading: _buildLeading(context), 18 | title: Text(station.name), 19 | subtitle: _buildSubtitle(context), 20 | trailing: _buildTrailing(context), 21 | isThreeLine: true, 22 | ); 23 | } 24 | 25 | Widget _buildLeading(BuildContext context) { 26 | return Stack( 27 | alignment: AlignmentDirectional.center, 28 | children: [ 29 | SizedBox( 30 | width: 40.0, 31 | height: 40.0, 32 | child: CircularProgressIndicator( 33 | value: station.freeBikes / (station.freeBikes + station.emptySlots), 34 | backgroundColor: 35 | Theme.of(context).backgroundColor.withOpacity(0.30), 36 | ), 37 | ), 38 | Text(station.freeBikes.toString()), 39 | ], 40 | ); 41 | } 42 | 43 | Widget _buildSubtitle(BuildContext context) { 44 | return Column( 45 | crossAxisAlignment: CrossAxisAlignment.start, 46 | children: [ 47 | _buildSubtitleBikeCount(context), 48 | const SizedBox(height: 4.0), 49 | _buildSubtitleDistance(), 50 | ], 51 | ); 52 | } 53 | 54 | Widget _buildSubtitleBikeCount(BuildContext context) { 55 | return Text.rich( 56 | TextSpan( 57 | children: [ 58 | TextSpan( 59 | text: '${station.freeEBikes} ', 60 | style: TextStyle( 61 | fontWeight: FontWeight.bold, 62 | color: Theme.of(context).primaryColorDark, 63 | ), 64 | ), 65 | const TextSpan(text: 'electric / '), 66 | TextSpan( 67 | text: '${station.freeNormalBikes} ', 68 | style: TextStyle( 69 | fontWeight: FontWeight.bold, 70 | color: Theme.of(context).primaryColorDark, 71 | ), 72 | ), 73 | const TextSpan(text: 'mechanical'), 74 | ], 75 | ), 76 | ); 77 | } 78 | 79 | Widget _buildSubtitleDistance() { 80 | return station.distance != null 81 | ? Text.rich( 82 | TextSpan( 83 | children: [ 84 | const TextSpan(text: 'At '), 85 | TextSpan( 86 | text: station.distance?.format() ?? 'unknown', 87 | style: const TextStyle(fontWeight: FontWeight.bold), 88 | ), 89 | ], 90 | ), 91 | ) 92 | : Container(); 93 | } 94 | 95 | Widget _buildTrailing(BuildContext context) { 96 | return IconButton( 97 | icon: Icon( 98 | station.isFavorite ? Icons.star : Icons.star_border, 99 | color: station.isFavorite ? Theme.of(context).primaryColor : null, 100 | ), 101 | tooltip: station.isFavorite ? 'Unmark as favorite' : 'Mark as favorite', 102 | onPressed: () { 103 | const snackBar = SnackBar(content: Text('Not implemented yet')); 104 | ScaffoldMessenger.of(context).showSnackBar(snackBar); 105 | }, 106 | ); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /packages/features/bike_stations/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _bike_stations_features 2 | description: Bike stations features 3 | 4 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 5 | 6 | environment: 7 | sdk: ">=2.16.2 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | cupertino_icons: ^1.0.2 13 | distance: ^1.0.0 14 | flutter_bloc: ^8.0.0 15 | freezed_annotation: ^2.0.3 16 | 17 | _core: 18 | path: ../../shared/all/core 19 | _intent_launcher: 20 | path: ../../shared/features/intent_launcher 21 | 22 | _bike_stations_use_cases: 23 | path: ../../use_cases/bike_stations 24 | _location_use_cases: 25 | path: ../../use_cases/location 26 | 27 | dev_dependencies: 28 | flutter_test: 29 | sdk: flutter 30 | build_runner: ^2.1.11 31 | flutter_lints: ^1.0.0 32 | freezed: ^2.0.3+1 33 | 34 | flutter: 35 | uses-material-design: true 36 | 37 | # To add assets to your application, add an assets section, like this: 38 | # assets: 39 | # - images/a_dot_burr.jpeg 40 | # - images/a_dot_ham.jpeg 41 | 42 | # An image asset can refer to one or more resolution-specific "variants", see 43 | # https://flutter.dev/assets-and-images/#resolution-aware. 44 | 45 | # For details regarding adding assets from package dependencies, see 46 | # https://flutter.dev/assets-and-images/#from-packages 47 | 48 | # To add custom fonts to your application, add a fonts section here, 49 | # in this "flutter" section. Each entry in this list should have a 50 | # "family" key with the font family name, and a "fonts" key with a 51 | # list giving the asset and other descriptors for the font. For 52 | # example: 53 | # fonts: 54 | # - family: Schyler 55 | # fonts: 56 | # - asset: fonts/Schyler-Regular.ttf 57 | # - asset: fonts/Schyler-Italic.ttf 58 | # style: italic 59 | # - family: Trajan Pro 60 | # fonts: 61 | # - asset: fonts/TrajanPro.ttf 62 | # - asset: fonts/TrajanPro_Bold.ttf 63 | # weight: 700 64 | # 65 | # For details regarding fonts from package dependencies, 66 | # see https://flutter.dev/custom-fonts/#from-packages 67 | -------------------------------------------------------------------------------- /packages/shared/all/core/lib/core.dart: -------------------------------------------------------------------------------- 1 | // Dependencies 2 | export 'src/dependencies/dependency_configuration_context.dart' 3 | show DependencyConfigurationContext; 4 | export 'src/dependencies/dependency_configurator.dart' 5 | show DependencyConfigurator; 6 | export 'src/dependencies/injector.dart' show inject; 7 | 8 | // Failures 9 | export 'src/failures/failure.dart' show Failure; 10 | export 'src/failures/network_failure.dart' show NetworkFailure; 11 | export 'src/failures/permission_failure.dart' show PermissionFailure; 12 | -------------------------------------------------------------------------------- /packages/shared/all/core/lib/src/dependencies/dependency_configuration_context.dart: -------------------------------------------------------------------------------- 1 | class DependencyConfigurationContext { 2 | const DependencyConfigurationContext(); 3 | } 4 | -------------------------------------------------------------------------------- /packages/shared/all/core/lib/src/dependencies/dependency_configurator.dart: -------------------------------------------------------------------------------- 1 | import 'package:get_it/get_it.dart'; 2 | 3 | import 'dependency_configuration_context.dart'; 4 | 5 | abstract class DependencyConfigurator { 6 | void configureDependencies( 7 | DependencyConfigurationContext context, 8 | GetIt getIt, 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /packages/shared/all/core/lib/src/dependencies/injector.dart: -------------------------------------------------------------------------------- 1 | import 'package:get_it/get_it.dart'; 2 | 3 | GetIt get _getIt => GetIt.instance; 4 | 5 | T inject({ 6 | String? instanceName, 7 | dynamic param1, 8 | dynamic param2, 9 | }) { 10 | return _getIt.get( 11 | instanceName: instanceName, 12 | param1: param1, 13 | param2: param2, 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /packages/shared/all/core/lib/src/failures/failure.dart: -------------------------------------------------------------------------------- 1 | abstract class Failure { 2 | final Object cause; 3 | 4 | Failure(this.cause); 5 | 6 | @override 7 | String toString() { 8 | return '$runtimeType{cause: ${cause.runtimeType}: $cause}'; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/shared/all/core/lib/src/failures/network_failure.dart: -------------------------------------------------------------------------------- 1 | import 'failure.dart'; 2 | 3 | class NetworkFailure extends Failure { 4 | NetworkFailure(Object cause) : super(cause); 5 | } 6 | -------------------------------------------------------------------------------- /packages/shared/all/core/lib/src/failures/permission_failure.dart: -------------------------------------------------------------------------------- 1 | import 'failure.dart'; 2 | 3 | class PermissionFailure extends Failure { 4 | PermissionFailure(Object cause) : super(cause); 5 | } 6 | -------------------------------------------------------------------------------- /packages/shared/all/core/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.9.0" 11 | collection: 12 | dependency: transitive 13 | description: 14 | name: collection 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "1.16.0" 18 | flutter_lints: 19 | dependency: "direct dev" 20 | description: 21 | name: flutter_lints 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.0.4" 25 | get_it: 26 | dependency: "direct main" 27 | description: 28 | name: get_it 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "7.2.0" 32 | lints: 33 | dependency: transitive 34 | description: 35 | name: lints 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.0.1" 39 | meta: 40 | dependency: transitive 41 | description: 42 | name: meta 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.8.0" 46 | sdks: 47 | dart: ">=2.16.2 <3.0.0" 48 | -------------------------------------------------------------------------------- /packages/shared/all/core/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _core 2 | description: Core functionality used by features and use cases 3 | 4 | publish_to: 'none' 5 | 6 | environment: 7 | sdk: ">=2.16.2 <3.0.0" 8 | 9 | dependencies: 10 | get_it: ^7.2.0 11 | 12 | dev_dependencies: 13 | flutter_lints: ^1.0.0 14 | -------------------------------------------------------------------------------- /packages/shared/features/intent_launcher/lib/intent_launcher.dart: -------------------------------------------------------------------------------- 1 | // Main interface 2 | export 'src/intent_launcher.dart' show IntentLauncher; 3 | 4 | // Intents 5 | export 'src/intents/navigation_intent.dart' show NavigationIntent; 6 | 7 | // Extensions 8 | export 'src/extensions/build_context_extension.dart' 9 | show BuildContextExtension; 10 | export 'src/extensions/widget_extension.dart' 11 | show WidgetExtension; 12 | 13 | // Exceptions 14 | export 'src/intent_not_registered_exception.dart' 15 | show IntentNotRegisteredException; 16 | -------------------------------------------------------------------------------- /packages/shared/features/intent_launcher/lib/src/extensions/build_context_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../intent_launcher.dart'; 4 | import '../intents/navigation_intent.dart'; 5 | 6 | extension BuildContextExtension on BuildContext { 7 | Future go(NavigationIntent intent) { 8 | return IntentLauncher.go(this, intent); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/shared/features/intent_launcher/lib/src/extensions/widget_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../intent_launcher.dart'; 4 | import '../inherited_intent_launcher.dart'; 5 | 6 | extension WidgetExtension on Widget { 7 | Widget wrapWith(IntentLauncher launcher) { 8 | return InheritedIntentLauncher( 9 | child: this, 10 | launcher: launcher, 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/shared/features/intent_launcher/lib/src/inherited_intent_launcher.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'intent_launcher.dart'; 4 | 5 | class InheritedIntentLauncher extends InheritedWidget { 6 | final IntentLauncher launcher; 7 | 8 | const InheritedIntentLauncher({ 9 | Key? key, 10 | required Widget child, 11 | required this.launcher, 12 | }) : super(key: key, child: child); 13 | 14 | static InheritedIntentLauncher of(BuildContext context) { 15 | final InheritedIntentLauncher? result = 16 | context.dependOnInheritedWidgetOfExactType(); 17 | assert(result != null, 'No InheritedIntentLauncher found in context'); 18 | return result!; 19 | } 20 | 21 | @override 22 | bool updateShouldNotify(InheritedIntentLauncher oldWidget) { 23 | return false; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/shared/features/intent_launcher/lib/src/intent_launcher.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'intent_launcher_impl.dart'; 4 | import 'intents/navigation_intent.dart'; 5 | 6 | typedef NavigationIntentHandler = Future Function( 7 | BuildContext context, T intent); 8 | 9 | abstract class IntentLauncher { 10 | factory IntentLauncher() { 11 | return IntentLauncherImpl(); 12 | } 13 | 14 | static Future go( 15 | BuildContext context, NavigationIntent navigationIntent) { 16 | return IntentLauncherImpl.launchNavigationIntent(context, navigationIntent); 17 | } 18 | 19 | void onNavigationIntent( 20 | NavigationIntentHandler handler); 21 | 22 | @protected 23 | Future handleNavigationIntent( 24 | BuildContext context, 25 | NavigationIntent navigationIntent, 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /packages/shared/features/intent_launcher/lib/src/intent_launcher_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'inherited_intent_launcher.dart'; 4 | import 'intent_launcher.dart'; 5 | import 'intent_not_registered_exception.dart'; 6 | import 'intents/navigation_intent.dart'; 7 | 8 | class IntentLauncherImpl implements IntentLauncher { 9 | final _navigationIntentHandlers = {}; 10 | 11 | static Future launchNavigationIntent( 12 | BuildContext context, 13 | NavigationIntent navigationIntent, 14 | ) { 15 | final inherited = 16 | context.dependOnInheritedWidgetOfExactType(); 17 | assert( 18 | inherited != null, 19 | 'No IntentLauncher found in context.\n' 20 | 'Please use `wrapWith(IntentLauncher)` extension method.'); 21 | return inherited!.launcher 22 | .handleNavigationIntent(context, navigationIntent); 23 | } 24 | 25 | @override 26 | void onNavigationIntent( 27 | NavigationIntentHandler handler) { 28 | _navigationIntentHandlers[T] = handler; 29 | } 30 | 31 | @override 32 | Future handleNavigationIntent( 33 | BuildContext context, 34 | NavigationIntent navigationIntent, 35 | ) { 36 | final handler = _navigationIntentHandlers[navigationIntent.runtimeType]; 37 | if (handler != null) { 38 | return handler(context, navigationIntent) as Future; 39 | } else { 40 | throw IntentNotRegisteredException(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/shared/features/intent_launcher/lib/src/intent_not_registered_exception.dart: -------------------------------------------------------------------------------- 1 | class IntentNotRegisteredException implements Exception {} 2 | -------------------------------------------------------------------------------- /packages/shared/features/intent_launcher/lib/src/intents/navigation_intent.dart: -------------------------------------------------------------------------------- 1 | abstract class NavigationIntent {} -------------------------------------------------------------------------------- /packages/shared/features/intent_launcher/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.8.2" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.1.0" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.2.0" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.3.1" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.1.0" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.15.0" 46 | cupertino_icons: 47 | dependency: "direct main" 48 | description: 49 | name: cupertino_icons 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.0.4" 53 | fake_async: 54 | dependency: transitive 55 | description: 56 | name: fake_async 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.2.0" 60 | flutter: 61 | dependency: "direct main" 62 | description: flutter 63 | source: sdk 64 | version: "0.0.0" 65 | flutter_lints: 66 | dependency: "direct dev" 67 | description: 68 | name: flutter_lints 69 | url: "https://pub.dartlang.org" 70 | source: hosted 71 | version: "1.0.4" 72 | flutter_test: 73 | dependency: "direct dev" 74 | description: flutter 75 | source: sdk 76 | version: "0.0.0" 77 | lints: 78 | dependency: transitive 79 | description: 80 | name: lints 81 | url: "https://pub.dartlang.org" 82 | source: hosted 83 | version: "1.0.1" 84 | matcher: 85 | dependency: transitive 86 | description: 87 | name: matcher 88 | url: "https://pub.dartlang.org" 89 | source: hosted 90 | version: "0.12.11" 91 | material_color_utilities: 92 | dependency: transitive 93 | description: 94 | name: material_color_utilities 95 | url: "https://pub.dartlang.org" 96 | source: hosted 97 | version: "0.1.3" 98 | meta: 99 | dependency: transitive 100 | description: 101 | name: meta 102 | url: "https://pub.dartlang.org" 103 | source: hosted 104 | version: "1.7.0" 105 | path: 106 | dependency: transitive 107 | description: 108 | name: path 109 | url: "https://pub.dartlang.org" 110 | source: hosted 111 | version: "1.8.0" 112 | sky_engine: 113 | dependency: transitive 114 | description: flutter 115 | source: sdk 116 | version: "0.0.99" 117 | source_span: 118 | dependency: transitive 119 | description: 120 | name: source_span 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "1.8.1" 124 | stack_trace: 125 | dependency: transitive 126 | description: 127 | name: stack_trace 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "1.10.0" 131 | stream_channel: 132 | dependency: transitive 133 | description: 134 | name: stream_channel 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "2.1.0" 138 | string_scanner: 139 | dependency: transitive 140 | description: 141 | name: string_scanner 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "1.1.0" 145 | term_glyph: 146 | dependency: transitive 147 | description: 148 | name: term_glyph 149 | url: "https://pub.dartlang.org" 150 | source: hosted 151 | version: "1.2.0" 152 | test_api: 153 | dependency: transitive 154 | description: 155 | name: test_api 156 | url: "https://pub.dartlang.org" 157 | source: hosted 158 | version: "0.4.8" 159 | typed_data: 160 | dependency: transitive 161 | description: 162 | name: typed_data 163 | url: "https://pub.dartlang.org" 164 | source: hosted 165 | version: "1.3.0" 166 | vector_math: 167 | dependency: transitive 168 | description: 169 | name: vector_math 170 | url: "https://pub.dartlang.org" 171 | source: hosted 172 | version: "2.1.1" 173 | sdks: 174 | dart: ">=2.16.2 <3.0.0" 175 | -------------------------------------------------------------------------------- /packages/shared/features/intent_launcher/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _intent_launcher 2 | description: Allow you to launch (navitage) screens from other feature modules 3 | by launching `NavigationIntents`. 4 | 5 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 6 | 7 | environment: 8 | sdk: ">=2.16.2 <3.0.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | cupertino_icons: ^1.0.2 14 | 15 | dev_dependencies: 16 | flutter_test: 17 | sdk: flutter 18 | flutter_lints: ^1.0.0 19 | 20 | flutter: 21 | uses-material-design: true 22 | 23 | # To add assets to your application, add an assets section, like this: 24 | # assets: 25 | # - images/a_dot_burr.jpeg 26 | # - images/a_dot_ham.jpeg 27 | 28 | # An image asset can refer to one or more resolution-specific "variants", see 29 | # https://flutter.dev/assets-and-images/#resolution-aware. 30 | 31 | # For details regarding adding assets from package dependencies, see 32 | # https://flutter.dev/assets-and-images/#from-packages 33 | 34 | # To add custom fonts to your application, add a fonts section here, 35 | # in this "flutter" section. Each entry in this list should have a 36 | # "family" key with the font family name, and a "fonts" key with a 37 | # list giving the asset and other descriptors for the font. For 38 | # example: 39 | # fonts: 40 | # - family: Schyler 41 | # fonts: 42 | # - asset: fonts/Schyler-Regular.ttf 43 | # - asset: fonts/Schyler-Italic.ttf 44 | # style: italic 45 | # - family: Trajan Pro 46 | # fonts: 47 | # - asset: fonts/TrajanPro.ttf 48 | # - asset: fonts/TrajanPro_Bold.ttf 49 | # weight: 700 50 | # 51 | # For details regarding fonts from package dependencies, 52 | # see https://flutter.dev/custom-fonts/#from-packages 53 | -------------------------------------------------------------------------------- /packages/shared/use_cases/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuartApp/flutter-modular-architecture/096ebec8d7c8925a925df019758d01ee95a7c856/packages/shared/use_cases/.keep -------------------------------------------------------------------------------- /packages/use_cases/bike_services/lib/bike_services_use_cases.dart: -------------------------------------------------------------------------------- 1 | // Models 2 | export 'src/models/city.dart' show City; 3 | export 'src/models/service.dart' show Service; 4 | 5 | // Responses 6 | export 'src/responses/sorted_services_response.dart' 7 | show SortedServicesResponse; 8 | 9 | // Use cases 10 | export 'src/use_cases/get_all_services.dart' show GetAllServices; 11 | export 'src/use_cases/get_all_services_sorted_by_distance.dart' 12 | show GetAllServicesSortedByDistance; 13 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/lib/dependency_configurator.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:_location_use_cases/location_use_cases.dart'; 3 | import 'package:get_it/get_it.dart'; 4 | 5 | import 'bike_services_use_cases.dart'; 6 | import 'src/repositories/service_repository.dart'; 7 | import 'src/use_cases/impl/get_all_services_impl.dart'; 8 | import 'src/use_cases/impl/get_all_services_sorted_by_distance_impl.dart'; 9 | 10 | class BikeServicesDependencyConfigurator implements DependencyConfigurator { 11 | @override 12 | void configureDependencies( 13 | DependencyConfigurationContext context, 14 | GetIt getIt, 15 | ) { 16 | getIt.registerFactory( 17 | () => GetAllServicesImpl(getIt())); 18 | 19 | getIt.registerFactory( 20 | () => GetAllServicesSortedByDistanceImpl( 21 | getIt(), 22 | getIt(), 23 | )); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/lib/private_ports.dart: -------------------------------------------------------------------------------- 1 | // Repositories 2 | export 'src/repositories/service_repository.dart' show ServiceRepository; -------------------------------------------------------------------------------- /packages/use_cases/bike_services/lib/src/models/city.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | import 'package:latlong2/latlong.dart'; 5 | 6 | part 'city.freezed.dart'; 7 | 8 | @freezed 9 | class City with _$City { 10 | const factory City({ 11 | required String name, 12 | required LatLng center, 13 | }) = _City; 14 | 15 | const City._(); 16 | 17 | double distanceToSortIndex(LatLng location) { 18 | return pow(location.latitude - center.latitude, 2).toDouble() + 19 | pow(location.longitude - center.longitude, 2).toDouble(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/lib/src/models/city.freezed.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint 4 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target 5 | 6 | part of 'city.dart'; 7 | 8 | // ************************************************************************** 9 | // FreezedGenerator 10 | // ************************************************************************** 11 | 12 | T _$identity(T value) => value; 13 | 14 | final _privateConstructorUsedError = UnsupportedError( 15 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); 16 | 17 | /// @nodoc 18 | mixin _$City { 19 | String get name => throw _privateConstructorUsedError; 20 | LatLng get center => throw _privateConstructorUsedError; 21 | 22 | @JsonKey(ignore: true) 23 | $CityCopyWith get copyWith => throw _privateConstructorUsedError; 24 | } 25 | 26 | /// @nodoc 27 | abstract class $CityCopyWith<$Res> { 28 | factory $CityCopyWith(City value, $Res Function(City) then) = 29 | _$CityCopyWithImpl<$Res>; 30 | $Res call({String name, LatLng center}); 31 | } 32 | 33 | /// @nodoc 34 | class _$CityCopyWithImpl<$Res> implements $CityCopyWith<$Res> { 35 | _$CityCopyWithImpl(this._value, this._then); 36 | 37 | final City _value; 38 | // ignore: unused_field 39 | final $Res Function(City) _then; 40 | 41 | @override 42 | $Res call({ 43 | Object? name = freezed, 44 | Object? center = freezed, 45 | }) { 46 | return _then(_value.copyWith( 47 | name: name == freezed 48 | ? _value.name 49 | : name // ignore: cast_nullable_to_non_nullable 50 | as String, 51 | center: center == freezed 52 | ? _value.center 53 | : center // ignore: cast_nullable_to_non_nullable 54 | as LatLng, 55 | )); 56 | } 57 | } 58 | 59 | /// @nodoc 60 | abstract class _$$_CityCopyWith<$Res> implements $CityCopyWith<$Res> { 61 | factory _$$_CityCopyWith(_$_City value, $Res Function(_$_City) then) = 62 | __$$_CityCopyWithImpl<$Res>; 63 | @override 64 | $Res call({String name, LatLng center}); 65 | } 66 | 67 | /// @nodoc 68 | class __$$_CityCopyWithImpl<$Res> extends _$CityCopyWithImpl<$Res> 69 | implements _$$_CityCopyWith<$Res> { 70 | __$$_CityCopyWithImpl(_$_City _value, $Res Function(_$_City) _then) 71 | : super(_value, (v) => _then(v as _$_City)); 72 | 73 | @override 74 | _$_City get _value => super._value as _$_City; 75 | 76 | @override 77 | $Res call({ 78 | Object? name = freezed, 79 | Object? center = freezed, 80 | }) { 81 | return _then(_$_City( 82 | name: name == freezed 83 | ? _value.name 84 | : name // ignore: cast_nullable_to_non_nullable 85 | as String, 86 | center: center == freezed 87 | ? _value.center 88 | : center // ignore: cast_nullable_to_non_nullable 89 | as LatLng, 90 | )); 91 | } 92 | } 93 | 94 | /// @nodoc 95 | 96 | class _$_City extends _City { 97 | const _$_City({required this.name, required this.center}) : super._(); 98 | 99 | @override 100 | final String name; 101 | @override 102 | final LatLng center; 103 | 104 | @override 105 | String toString() { 106 | return 'City(name: $name, center: $center)'; 107 | } 108 | 109 | @override 110 | bool operator ==(dynamic other) { 111 | return identical(this, other) || 112 | (other.runtimeType == runtimeType && 113 | other is _$_City && 114 | const DeepCollectionEquality().equals(other.name, name) && 115 | const DeepCollectionEquality().equals(other.center, center)); 116 | } 117 | 118 | @override 119 | int get hashCode => Object.hash( 120 | runtimeType, 121 | const DeepCollectionEquality().hash(name), 122 | const DeepCollectionEquality().hash(center)); 123 | 124 | @JsonKey(ignore: true) 125 | @override 126 | _$$_CityCopyWith<_$_City> get copyWith => 127 | __$$_CityCopyWithImpl<_$_City>(this, _$identity); 128 | } 129 | 130 | abstract class _City extends City { 131 | const factory _City( 132 | {required final String name, required final LatLng center}) = _$_City; 133 | const _City._() : super._(); 134 | 135 | @override 136 | String get name => throw _privateConstructorUsedError; 137 | @override 138 | LatLng get center => throw _privateConstructorUsedError; 139 | @override 140 | @JsonKey(ignore: true) 141 | _$$_CityCopyWith<_$_City> get copyWith => throw _privateConstructorUsedError; 142 | } 143 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/lib/src/models/service.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | import 'city.dart'; 4 | 5 | part 'service.freezed.dart'; 6 | 7 | @freezed 8 | class Service with _$Service { 9 | const factory Service({ 10 | required String id, 11 | required String name, 12 | required City city, 13 | required bool hasEBikes, 14 | }) = _Service; 15 | } 16 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/lib/src/models/service.freezed.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint 4 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target 5 | 6 | part of 'service.dart'; 7 | 8 | // ************************************************************************** 9 | // FreezedGenerator 10 | // ************************************************************************** 11 | 12 | T _$identity(T value) => value; 13 | 14 | final _privateConstructorUsedError = UnsupportedError( 15 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); 16 | 17 | /// @nodoc 18 | mixin _$Service { 19 | String get id => throw _privateConstructorUsedError; 20 | String get name => throw _privateConstructorUsedError; 21 | City get city => throw _privateConstructorUsedError; 22 | bool get hasEBikes => throw _privateConstructorUsedError; 23 | 24 | @JsonKey(ignore: true) 25 | $ServiceCopyWith get copyWith => throw _privateConstructorUsedError; 26 | } 27 | 28 | /// @nodoc 29 | abstract class $ServiceCopyWith<$Res> { 30 | factory $ServiceCopyWith(Service value, $Res Function(Service) then) = 31 | _$ServiceCopyWithImpl<$Res>; 32 | $Res call({String id, String name, City city, bool hasEBikes}); 33 | 34 | $CityCopyWith<$Res> get city; 35 | } 36 | 37 | /// @nodoc 38 | class _$ServiceCopyWithImpl<$Res> implements $ServiceCopyWith<$Res> { 39 | _$ServiceCopyWithImpl(this._value, this._then); 40 | 41 | final Service _value; 42 | // ignore: unused_field 43 | final $Res Function(Service) _then; 44 | 45 | @override 46 | $Res call({ 47 | Object? id = freezed, 48 | Object? name = freezed, 49 | Object? city = freezed, 50 | Object? hasEBikes = freezed, 51 | }) { 52 | return _then(_value.copyWith( 53 | id: id == freezed 54 | ? _value.id 55 | : id // ignore: cast_nullable_to_non_nullable 56 | as String, 57 | name: name == freezed 58 | ? _value.name 59 | : name // ignore: cast_nullable_to_non_nullable 60 | as String, 61 | city: city == freezed 62 | ? _value.city 63 | : city // ignore: cast_nullable_to_non_nullable 64 | as City, 65 | hasEBikes: hasEBikes == freezed 66 | ? _value.hasEBikes 67 | : hasEBikes // ignore: cast_nullable_to_non_nullable 68 | as bool, 69 | )); 70 | } 71 | 72 | @override 73 | $CityCopyWith<$Res> get city { 74 | return $CityCopyWith<$Res>(_value.city, (value) { 75 | return _then(_value.copyWith(city: value)); 76 | }); 77 | } 78 | } 79 | 80 | /// @nodoc 81 | abstract class _$$_ServiceCopyWith<$Res> implements $ServiceCopyWith<$Res> { 82 | factory _$$_ServiceCopyWith( 83 | _$_Service value, $Res Function(_$_Service) then) = 84 | __$$_ServiceCopyWithImpl<$Res>; 85 | @override 86 | $Res call({String id, String name, City city, bool hasEBikes}); 87 | 88 | @override 89 | $CityCopyWith<$Res> get city; 90 | } 91 | 92 | /// @nodoc 93 | class __$$_ServiceCopyWithImpl<$Res> extends _$ServiceCopyWithImpl<$Res> 94 | implements _$$_ServiceCopyWith<$Res> { 95 | __$$_ServiceCopyWithImpl(_$_Service _value, $Res Function(_$_Service) _then) 96 | : super(_value, (v) => _then(v as _$_Service)); 97 | 98 | @override 99 | _$_Service get _value => super._value as _$_Service; 100 | 101 | @override 102 | $Res call({ 103 | Object? id = freezed, 104 | Object? name = freezed, 105 | Object? city = freezed, 106 | Object? hasEBikes = freezed, 107 | }) { 108 | return _then(_$_Service( 109 | id: id == freezed 110 | ? _value.id 111 | : id // ignore: cast_nullable_to_non_nullable 112 | as String, 113 | name: name == freezed 114 | ? _value.name 115 | : name // ignore: cast_nullable_to_non_nullable 116 | as String, 117 | city: city == freezed 118 | ? _value.city 119 | : city // ignore: cast_nullable_to_non_nullable 120 | as City, 121 | hasEBikes: hasEBikes == freezed 122 | ? _value.hasEBikes 123 | : hasEBikes // ignore: cast_nullable_to_non_nullable 124 | as bool, 125 | )); 126 | } 127 | } 128 | 129 | /// @nodoc 130 | 131 | class _$_Service implements _Service { 132 | const _$_Service( 133 | {required this.id, 134 | required this.name, 135 | required this.city, 136 | required this.hasEBikes}); 137 | 138 | @override 139 | final String id; 140 | @override 141 | final String name; 142 | @override 143 | final City city; 144 | @override 145 | final bool hasEBikes; 146 | 147 | @override 148 | String toString() { 149 | return 'Service(id: $id, name: $name, city: $city, hasEBikes: $hasEBikes)'; 150 | } 151 | 152 | @override 153 | bool operator ==(dynamic other) { 154 | return identical(this, other) || 155 | (other.runtimeType == runtimeType && 156 | other is _$_Service && 157 | const DeepCollectionEquality().equals(other.id, id) && 158 | const DeepCollectionEquality().equals(other.name, name) && 159 | const DeepCollectionEquality().equals(other.city, city) && 160 | const DeepCollectionEquality().equals(other.hasEBikes, hasEBikes)); 161 | } 162 | 163 | @override 164 | int get hashCode => Object.hash( 165 | runtimeType, 166 | const DeepCollectionEquality().hash(id), 167 | const DeepCollectionEquality().hash(name), 168 | const DeepCollectionEquality().hash(city), 169 | const DeepCollectionEquality().hash(hasEBikes)); 170 | 171 | @JsonKey(ignore: true) 172 | @override 173 | _$$_ServiceCopyWith<_$_Service> get copyWith => 174 | __$$_ServiceCopyWithImpl<_$_Service>(this, _$identity); 175 | } 176 | 177 | abstract class _Service implements Service { 178 | const factory _Service( 179 | {required final String id, 180 | required final String name, 181 | required final City city, 182 | required final bool hasEBikes}) = _$_Service; 183 | 184 | @override 185 | String get id => throw _privateConstructorUsedError; 186 | @override 187 | String get name => throw _privateConstructorUsedError; 188 | @override 189 | City get city => throw _privateConstructorUsedError; 190 | @override 191 | bool get hasEBikes => throw _privateConstructorUsedError; 192 | @override 193 | @JsonKey(ignore: true) 194 | _$$_ServiceCopyWith<_$_Service> get copyWith => 195 | throw _privateConstructorUsedError; 196 | } 197 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/lib/src/repositories/service_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:either_dart/either.dart'; 3 | 4 | import '../models/service.dart'; 5 | 6 | abstract class ServiceRepository { 7 | Future>> getAllServices(); 8 | } 9 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/lib/src/responses/sorted_services_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:latlong2/latlong.dart'; 3 | 4 | import '../models/service.dart'; 5 | 6 | @immutable 7 | class SortedServicesResponse { 8 | final LatLng? sortedFrom; 9 | final List services; 10 | 11 | const SortedServicesResponse({ 12 | this.sortedFrom, 13 | required this.services, 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/lib/src/use_cases/get_all_services.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:either_dart/either.dart'; 3 | 4 | import '../models/service.dart'; 5 | 6 | abstract class GetAllServices { 7 | Future>> call(); 8 | } 9 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/lib/src/use_cases/get_all_services_sorted_by_distance.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:either_dart/either.dart'; 3 | 4 | import '../responses/sorted_services_response.dart'; 5 | 6 | abstract class GetAllServicesSortedByDistance { 7 | Future> call(); 8 | } 9 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/lib/src/use_cases/impl/get_all_services_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:either_dart/either.dart'; 3 | 4 | import '../../models/service.dart'; 5 | import '../../repositories/service_repository.dart'; 6 | import '../get_all_services.dart'; 7 | 8 | class GetAllServicesImpl implements GetAllServices { 9 | final ServiceRepository _serviceRepository; 10 | 11 | GetAllServicesImpl(this._serviceRepository); 12 | 13 | @override 14 | Future>> call() { 15 | return _serviceRepository.getAllServices(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/lib/src/use_cases/impl/get_all_services_sorted_by_distance_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:_location_use_cases/location_use_cases.dart'; 3 | import 'package:either_dart/either.dart'; 4 | 5 | import '../../responses/sorted_services_response.dart'; 6 | import '../get_all_services.dart'; 7 | import '../get_all_services_sorted_by_distance.dart'; 8 | 9 | class GetAllServicesSortedByDistanceImpl 10 | implements GetAllServicesSortedByDistance { 11 | final GetAllServices _getAllServices; 12 | 13 | final GetLastKnownLocation _getLastKnownLocation; 14 | 15 | GetAllServicesSortedByDistanceImpl( 16 | this._getAllServices, 17 | this._getLastKnownLocation, 18 | ); 19 | 20 | @override 21 | Future> call() { 22 | return _getAllServices().thenRight((services) { 23 | return _getLastKnownLocation().fold( 24 | (failure) { 25 | return Right(SortedServicesResponse(services: services)); 26 | }, 27 | (location) { 28 | return Right(SortedServicesResponse( 29 | services: services.toList() 30 | ..sort((a, b) { 31 | final aIndex = a.city.distanceToSortIndex(location); 32 | final bIndex = b.city.distanceToSortIndex(location); 33 | return aIndex.compareTo(bIndex); 34 | }), 35 | sortedFrom: location, 36 | )); 37 | }, 38 | ); 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/packages/real_adapters/lib/dependency_configurator.dart: -------------------------------------------------------------------------------- 1 | import 'package:_bike_services_use_cases/private_ports.dart'; 2 | import 'package:_core/core.dart'; 3 | import 'package:get_it/get_it.dart'; 4 | 5 | import 'src/data_sources/service_api/service_api_data_source.dart'; 6 | import 'src/repositories/service_repository_impl.dart'; 7 | 8 | class BikeServicesRealAdaptersDependencyConfigurator 9 | implements DependencyConfigurator { 10 | @override 11 | void configureDependencies( 12 | DependencyConfigurationContext context, 13 | GetIt getIt, 14 | ) { 15 | getIt.registerFactory( 16 | () => ServiceApiDataSourceImpl()); 17 | 18 | getIt.registerFactory( 19 | () => ServiceRepositoryImpl(getIt())); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/packages/real_adapters/lib/src/data_sources/service_api/dtos/location_dto.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'location_dto.g.dart'; 4 | 5 | @JsonSerializable() 6 | class LocationDto { 7 | final String city; 8 | 9 | final double latitude; 10 | 11 | final double longitude; 12 | 13 | LocationDto({ 14 | required this.city, 15 | required this.latitude, 16 | required this.longitude, 17 | }); 18 | 19 | factory LocationDto.fromJson(Map json) => 20 | _$LocationDtoFromJson(json); 21 | 22 | Map toJson() => _$LocationDtoToJson(this); 23 | } 24 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/packages/real_adapters/lib/src/data_sources/service_api/dtos/location_dto.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'location_dto.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | LocationDto _$LocationDtoFromJson(Map json) => LocationDto( 10 | city: json['city'] as String, 11 | latitude: (json['latitude'] as num).toDouble(), 12 | longitude: (json['longitude'] as num).toDouble(), 13 | ); 14 | 15 | Map _$LocationDtoToJson(LocationDto instance) => 16 | { 17 | 'city': instance.city, 18 | 'latitude': instance.latitude, 19 | 'longitude': instance.longitude, 20 | }; 21 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/packages/real_adapters/lib/src/data_sources/service_api/dtos/service_dto.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | import 'location_dto.dart'; 4 | 5 | part 'service_dto.g.dart'; 6 | 7 | @JsonSerializable() 8 | class ServiceDto { 9 | final String id; 10 | 11 | final String name; 12 | 13 | final LocationDto location; 14 | 15 | @JsonKey(name: 'ebikes') 16 | final bool? eBikes; 17 | 18 | const ServiceDto({ 19 | required this.id, 20 | required this.name, 21 | required this.location, 22 | this.eBikes, 23 | }); 24 | 25 | factory ServiceDto.fromJson(Map json) => 26 | _$ServiceDtoFromJson(json); 27 | 28 | Map toJson() => _$ServiceDtoToJson(this); 29 | } 30 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/packages/real_adapters/lib/src/data_sources/service_api/dtos/service_dto.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'service_dto.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | ServiceDto _$ServiceDtoFromJson(Map json) => ServiceDto( 10 | id: json['id'] as String, 11 | name: json['name'] as String, 12 | location: LocationDto.fromJson(json['location'] as Map), 13 | eBikes: json['ebikes'] as bool?, 14 | ); 15 | 16 | Map _$ServiceDtoToJson(ServiceDto instance) => 17 | { 18 | 'id': instance.id, 19 | 'name': instance.name, 20 | 'location': instance.location, 21 | 'ebikes': instance.eBikes, 22 | }; 23 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/packages/real_adapters/lib/src/data_sources/service_api/mappers/service_dto_mapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:_bike_services_use_cases/bike_services_use_cases.dart'; 2 | import 'package:latlong2/latlong.dart'; 3 | 4 | import '../dtos/service_dto.dart'; 5 | 6 | extension ServiceDtoMapper on ServiceDto { 7 | Service toService() { 8 | return Service( 9 | id: id, 10 | name: name, 11 | city: City( 12 | name: location.city, 13 | center: LatLng(location.latitude, location.longitude), 14 | ), 15 | hasEBikes: eBikes ?? false, 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/packages/real_adapters/lib/src/data_sources/service_api/service_api_data_source.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:http/http.dart' as http; 5 | 6 | import 'dtos/service_dto.dart'; 7 | 8 | abstract class ServiceApiDataSource { 9 | Future> getAllServices(); 10 | } 11 | 12 | class ServiceApiDataSourceImpl implements ServiceApiDataSource { 13 | @override 14 | Future> getAllServices() async { 15 | final url = Uri.parse('http://api.citybik.es/v2/networks'); 16 | final response = await http.get(url); 17 | if (response.statusCode == 200) { 18 | final json = jsonDecode(response.body); 19 | final networks = json['networks'] as List; 20 | return networks.map((network) { 21 | return ServiceDto.fromJson(network as Map); 22 | }).toList(); 23 | } else { 24 | throw HttpException('HTTP status code ${response.statusCode}'); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/packages/real_adapters/lib/src/repositories/service_repository_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:_bike_services_use_cases/bike_services_use_cases.dart'; 2 | import 'package:_bike_services_use_cases/private_ports.dart'; 3 | import 'package:_core/core.dart'; 4 | import 'package:either_dart/either.dart'; 5 | 6 | import '../data_sources/service_api/mappers/service_dto_mapper.dart'; 7 | import '../data_sources/service_api/service_api_data_source.dart'; 8 | 9 | class ServiceRepositoryImpl implements ServiceRepository { 10 | final ServiceApiDataSource _serviceApiDataSource; 11 | 12 | ServiceRepositoryImpl(this._serviceApiDataSource); 13 | 14 | @override 15 | Future>> getAllServices() async { 16 | try { 17 | final dtos = await _serviceApiDataSource.getAllServices(); 18 | return Right(dtos.map((dto) => dto.toService()).toList()); 19 | } on Exception catch (e) { 20 | return Left(NetworkFailure(e)); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/packages/real_adapters/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _bike_services_use_cases_real_adapters 2 | description: Bike services use cases real adapters 3 | 4 | publish_to: 'none' 5 | 6 | environment: 7 | sdk: ">=2.16.2 <3.0.0" 8 | 9 | dependencies: 10 | either_dart: ^0.1.4 11 | freezed_annotation: ^2.0.3 12 | http: ^0.13.4 13 | json_annotation: ^4.5.0 14 | 15 | _bike_services_use_cases: 16 | path: ../.. 17 | _core: 18 | path: ../../../../shared/all/core 19 | 20 | dev_dependencies: 21 | build_runner: ^2.1.11 22 | json_serializable: ^6.2.0 23 | flutter_lints: ^1.0.0 24 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _core: 5 | dependency: "direct main" 6 | description: 7 | path: "../../shared/all/core" 8 | relative: true 9 | source: path 10 | version: "0.0.0" 11 | _fe_analyzer_shared: 12 | dependency: transitive 13 | description: 14 | name: _fe_analyzer_shared 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "40.0.0" 18 | _location_use_cases: 19 | dependency: "direct main" 20 | description: 21 | path: "../location" 22 | relative: true 23 | source: path 24 | version: "0.0.0" 25 | analyzer: 26 | dependency: transitive 27 | description: 28 | name: analyzer 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "4.1.0" 32 | args: 33 | dependency: transitive 34 | description: 35 | name: args 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "2.3.1" 39 | async: 40 | dependency: transitive 41 | description: 42 | name: async 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "2.9.0" 46 | build: 47 | dependency: transitive 48 | description: 49 | name: build 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "2.3.0" 53 | build_config: 54 | dependency: transitive 55 | description: 56 | name: build_config 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.0.0" 60 | build_daemon: 61 | dependency: transitive 62 | description: 63 | name: build_daemon 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "3.1.0" 67 | build_resolvers: 68 | dependency: transitive 69 | description: 70 | name: build_resolvers 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "2.0.8" 74 | build_runner: 75 | dependency: "direct dev" 76 | description: 77 | name: build_runner 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "2.1.11" 81 | build_runner_core: 82 | dependency: transitive 83 | description: 84 | name: build_runner_core 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "7.2.3" 88 | built_collection: 89 | dependency: transitive 90 | description: 91 | name: built_collection 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "5.1.1" 95 | built_value: 96 | dependency: transitive 97 | description: 98 | name: built_value 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "8.3.0" 102 | checked_yaml: 103 | dependency: transitive 104 | description: 105 | name: checked_yaml 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "2.0.1" 109 | clock: 110 | dependency: transitive 111 | description: 112 | name: clock 113 | url: "https://pub.dartlang.org" 114 | source: hosted 115 | version: "1.1.0" 116 | code_builder: 117 | dependency: transitive 118 | description: 119 | name: code_builder 120 | url: "https://pub.dartlang.org" 121 | source: hosted 122 | version: "4.1.0" 123 | collection: 124 | dependency: transitive 125 | description: 126 | name: collection 127 | url: "https://pub.dartlang.org" 128 | source: hosted 129 | version: "1.15.0" 130 | convert: 131 | dependency: transitive 132 | description: 133 | name: convert 134 | url: "https://pub.dartlang.org" 135 | source: hosted 136 | version: "3.0.1" 137 | crypto: 138 | dependency: transitive 139 | description: 140 | name: crypto 141 | url: "https://pub.dartlang.org" 142 | source: hosted 143 | version: "3.0.2" 144 | dart_style: 145 | dependency: transitive 146 | description: 147 | name: dart_style 148 | url: "https://pub.dartlang.org" 149 | source: hosted 150 | version: "2.2.3" 151 | distance: 152 | dependency: "direct main" 153 | description: 154 | name: distance 155 | url: "https://pub.dartlang.org" 156 | source: hosted 157 | version: "1.0.0" 158 | either_dart: 159 | dependency: "direct main" 160 | description: 161 | name: either_dart 162 | url: "https://pub.dartlang.org" 163 | source: hosted 164 | version: "0.1.4" 165 | file: 166 | dependency: transitive 167 | description: 168 | name: file 169 | url: "https://pub.dartlang.org" 170 | source: hosted 171 | version: "6.1.2" 172 | fixnum: 173 | dependency: transitive 174 | description: 175 | name: fixnum 176 | url: "https://pub.dartlang.org" 177 | source: hosted 178 | version: "1.0.1" 179 | flutter_lints: 180 | dependency: "direct dev" 181 | description: 182 | name: flutter_lints 183 | url: "https://pub.dartlang.org" 184 | source: hosted 185 | version: "1.0.4" 186 | freezed: 187 | dependency: "direct dev" 188 | description: 189 | name: freezed 190 | url: "https://pub.dartlang.org" 191 | source: hosted 192 | version: "2.0.3+1" 193 | freezed_annotation: 194 | dependency: "direct main" 195 | description: 196 | name: freezed_annotation 197 | url: "https://pub.dartlang.org" 198 | source: hosted 199 | version: "2.0.3" 200 | frontend_server_client: 201 | dependency: transitive 202 | description: 203 | name: frontend_server_client 204 | url: "https://pub.dartlang.org" 205 | source: hosted 206 | version: "2.1.3" 207 | get_it: 208 | dependency: transitive 209 | description: 210 | name: get_it 211 | url: "https://pub.dartlang.org" 212 | source: hosted 213 | version: "7.2.0" 214 | glob: 215 | dependency: transitive 216 | description: 217 | name: glob 218 | url: "https://pub.dartlang.org" 219 | source: hosted 220 | version: "2.0.2" 221 | graphs: 222 | dependency: transitive 223 | description: 224 | name: graphs 225 | url: "https://pub.dartlang.org" 226 | source: hosted 227 | version: "2.1.0" 228 | http_multi_server: 229 | dependency: transitive 230 | description: 231 | name: http_multi_server 232 | url: "https://pub.dartlang.org" 233 | source: hosted 234 | version: "3.2.0" 235 | http_parser: 236 | dependency: transitive 237 | description: 238 | name: http_parser 239 | url: "https://pub.dartlang.org" 240 | source: hosted 241 | version: "4.0.1" 242 | intl: 243 | dependency: transitive 244 | description: 245 | name: intl 246 | url: "https://pub.dartlang.org" 247 | source: hosted 248 | version: "0.17.0" 249 | io: 250 | dependency: transitive 251 | description: 252 | name: io 253 | url: "https://pub.dartlang.org" 254 | source: hosted 255 | version: "1.0.3" 256 | js: 257 | dependency: transitive 258 | description: 259 | name: js 260 | url: "https://pub.dartlang.org" 261 | source: hosted 262 | version: "0.6.3" 263 | json_annotation: 264 | dependency: transitive 265 | description: 266 | name: json_annotation 267 | url: "https://pub.dartlang.org" 268 | source: hosted 269 | version: "4.5.0" 270 | latlong2: 271 | dependency: "direct main" 272 | description: 273 | name: latlong2 274 | url: "https://pub.dartlang.org" 275 | source: hosted 276 | version: "0.8.1" 277 | lints: 278 | dependency: transitive 279 | description: 280 | name: lints 281 | url: "https://pub.dartlang.org" 282 | source: hosted 283 | version: "1.0.1" 284 | logging: 285 | dependency: transitive 286 | description: 287 | name: logging 288 | url: "https://pub.dartlang.org" 289 | source: hosted 290 | version: "1.0.2" 291 | matcher: 292 | dependency: transitive 293 | description: 294 | name: matcher 295 | url: "https://pub.dartlang.org" 296 | source: hosted 297 | version: "0.12.11" 298 | meta: 299 | dependency: transitive 300 | description: 301 | name: meta 302 | url: "https://pub.dartlang.org" 303 | source: hosted 304 | version: "1.7.0" 305 | mime: 306 | dependency: transitive 307 | description: 308 | name: mime 309 | url: "https://pub.dartlang.org" 310 | source: hosted 311 | version: "1.0.2" 312 | package_config: 313 | dependency: transitive 314 | description: 315 | name: package_config 316 | url: "https://pub.dartlang.org" 317 | source: hosted 318 | version: "2.0.2" 319 | path: 320 | dependency: transitive 321 | description: 322 | name: path 323 | url: "https://pub.dartlang.org" 324 | source: hosted 325 | version: "1.8.2" 326 | pool: 327 | dependency: transitive 328 | description: 329 | name: pool 330 | url: "https://pub.dartlang.org" 331 | source: hosted 332 | version: "1.5.0" 333 | pub_semver: 334 | dependency: transitive 335 | description: 336 | name: pub_semver 337 | url: "https://pub.dartlang.org" 338 | source: hosted 339 | version: "2.1.1" 340 | pubspec_parse: 341 | dependency: transitive 342 | description: 343 | name: pubspec_parse 344 | url: "https://pub.dartlang.org" 345 | source: hosted 346 | version: "1.2.0" 347 | shelf: 348 | dependency: transitive 349 | description: 350 | name: shelf 351 | url: "https://pub.dartlang.org" 352 | source: hosted 353 | version: "1.3.0" 354 | shelf_web_socket: 355 | dependency: transitive 356 | description: 357 | name: shelf_web_socket 358 | url: "https://pub.dartlang.org" 359 | source: hosted 360 | version: "1.0.1" 361 | source_gen: 362 | dependency: transitive 363 | description: 364 | name: source_gen 365 | url: "https://pub.dartlang.org" 366 | source: hosted 367 | version: "1.2.2" 368 | source_span: 369 | dependency: transitive 370 | description: 371 | name: source_span 372 | url: "https://pub.dartlang.org" 373 | source: hosted 374 | version: "1.9.0" 375 | stack_trace: 376 | dependency: transitive 377 | description: 378 | name: stack_trace 379 | url: "https://pub.dartlang.org" 380 | source: hosted 381 | version: "1.10.0" 382 | stream_channel: 383 | dependency: transitive 384 | description: 385 | name: stream_channel 386 | url: "https://pub.dartlang.org" 387 | source: hosted 388 | version: "2.1.0" 389 | stream_transform: 390 | dependency: transitive 391 | description: 392 | name: stream_transform 393 | url: "https://pub.dartlang.org" 394 | source: hosted 395 | version: "2.0.0" 396 | string_scanner: 397 | dependency: transitive 398 | description: 399 | name: string_scanner 400 | url: "https://pub.dartlang.org" 401 | source: hosted 402 | version: "1.1.1" 403 | term_glyph: 404 | dependency: transitive 405 | description: 406 | name: term_glyph 407 | url: "https://pub.dartlang.org" 408 | source: hosted 409 | version: "1.2.0" 410 | timing: 411 | dependency: transitive 412 | description: 413 | name: timing 414 | url: "https://pub.dartlang.org" 415 | source: hosted 416 | version: "1.0.0" 417 | typed_data: 418 | dependency: transitive 419 | description: 420 | name: typed_data 421 | url: "https://pub.dartlang.org" 422 | source: hosted 423 | version: "1.3.0" 424 | watcher: 425 | dependency: transitive 426 | description: 427 | name: watcher 428 | url: "https://pub.dartlang.org" 429 | source: hosted 430 | version: "1.0.1" 431 | web_socket_channel: 432 | dependency: transitive 433 | description: 434 | name: web_socket_channel 435 | url: "https://pub.dartlang.org" 436 | source: hosted 437 | version: "2.2.0" 438 | yaml: 439 | dependency: transitive 440 | description: 441 | name: yaml 442 | url: "https://pub.dartlang.org" 443 | source: hosted 444 | version: "3.1.1" 445 | sdks: 446 | dart: ">=2.16.2 <3.0.0" 447 | -------------------------------------------------------------------------------- /packages/use_cases/bike_services/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _bike_services_use_cases 2 | description: Bike services use cases 3 | 4 | publish_to: 'none' 5 | 6 | environment: 7 | sdk: ">=2.16.2 <3.0.0" 8 | 9 | dependencies: 10 | distance: ^1.0.0 11 | either_dart: ^0.1.4 12 | freezed_annotation: ^2.0.3 13 | latlong2: ^0.8.1 14 | 15 | _core: 16 | path: ../../shared/all/core 17 | _location_use_cases: 18 | path: ../../use_cases/location 19 | 20 | dev_dependencies: 21 | build_runner: ^2.1.11 22 | flutter_lints: ^1.0.0 23 | freezed: ^2.0.3+1 24 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/lib/bike_stations_use_cases.dart: -------------------------------------------------------------------------------- 1 | // Models 2 | export 'src/models/station.dart' show Station; 3 | 4 | // Responses 5 | export 'src/responses/sorted_stations_response.dart' 6 | show SortedStationsResponse; 7 | 8 | // Use cases 9 | export 'src/use_cases/get_service_stations.dart' show GetServiceStations; 10 | export 'src/use_cases/get_service_stations_sorted_by_distance.dart' 11 | show GetServiceStationsSortedByDistance; 12 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/lib/dependency_configurator.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:_location_use_cases/location_use_cases.dart'; 3 | import 'package:get_it/get_it.dart'; 4 | 5 | import 'bike_stations_use_cases.dart'; 6 | import 'src/repositories/station_repository.dart'; 7 | import 'src/use_cases/impl/get_service_stations_impl.dart'; 8 | import 'src/use_cases/impl/get_service_stations_sorted_by_distance_impl.dart'; 9 | 10 | class BikeStationsDependencyConfigurator implements DependencyConfigurator { 11 | @override 12 | void configureDependencies( 13 | DependencyConfigurationContext context, 14 | GetIt getIt, 15 | ) { 16 | getIt.registerFactory( 17 | () => GetServiceStationsImpl(getIt())); 18 | 19 | getIt.registerFactory( 20 | () => GetServiceStationsSortedByDistanceImpl( 21 | getIt(), 22 | getIt(), 23 | )); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/lib/private_ports.dart: -------------------------------------------------------------------------------- 1 | // Repositories 2 | export 'src/repositories/station_repository.dart' show StationRepository; 3 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/lib/src/models/station.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:distance/distance.dart' as dist; 4 | import 'package:freezed_annotation/freezed_annotation.dart'; 5 | import 'package:latlong2/latlong.dart'; 6 | 7 | part 'station.freezed.dart'; 8 | 9 | @freezed 10 | class Station with _$Station { 11 | const factory Station({ 12 | required String id, 13 | required String name, 14 | required int emptySlots, 15 | required int freeNormalBikes, 16 | required int freeEBikes, 17 | required LatLng location, 18 | required bool isFavorite, 19 | }) = _Station; 20 | 21 | const Station._(); 22 | 23 | int get freeBikes => freeNormalBikes + freeEBikes; 24 | 25 | dist.Distance distanceTo(LatLng location) { 26 | const calcDistance = Distance(); 27 | return dist.Distance(meters: calcDistance(this.location, location).toInt()); 28 | } 29 | 30 | double distanceToSortIndex(LatLng location) { 31 | final a = location; 32 | final b = this.location; 33 | return pow(a.latitude - b.latitude, 2).toDouble() + 34 | pow(a.longitude - b.longitude, 2).toDouble(); 35 | } 36 | 37 | Station markAsFavorite() { 38 | return copyWith(isFavorite: true); 39 | } 40 | 41 | Station unMarkAsFavorite() { 42 | return copyWith(isFavorite: false); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/lib/src/models/station.freezed.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint 4 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target 5 | 6 | part of 'station.dart'; 7 | 8 | // ************************************************************************** 9 | // FreezedGenerator 10 | // ************************************************************************** 11 | 12 | T _$identity(T value) => value; 13 | 14 | final _privateConstructorUsedError = UnsupportedError( 15 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); 16 | 17 | /// @nodoc 18 | mixin _$Station { 19 | String get id => throw _privateConstructorUsedError; 20 | String get name => throw _privateConstructorUsedError; 21 | int get emptySlots => throw _privateConstructorUsedError; 22 | int get freeNormalBikes => throw _privateConstructorUsedError; 23 | int get freeEBikes => throw _privateConstructorUsedError; 24 | LatLng get location => throw _privateConstructorUsedError; 25 | bool get isFavorite => throw _privateConstructorUsedError; 26 | 27 | @JsonKey(ignore: true) 28 | $StationCopyWith get copyWith => throw _privateConstructorUsedError; 29 | } 30 | 31 | /// @nodoc 32 | abstract class $StationCopyWith<$Res> { 33 | factory $StationCopyWith(Station value, $Res Function(Station) then) = 34 | _$StationCopyWithImpl<$Res>; 35 | $Res call( 36 | {String id, 37 | String name, 38 | int emptySlots, 39 | int freeNormalBikes, 40 | int freeEBikes, 41 | LatLng location, 42 | bool isFavorite}); 43 | } 44 | 45 | /// @nodoc 46 | class _$StationCopyWithImpl<$Res> implements $StationCopyWith<$Res> { 47 | _$StationCopyWithImpl(this._value, this._then); 48 | 49 | final Station _value; 50 | // ignore: unused_field 51 | final $Res Function(Station) _then; 52 | 53 | @override 54 | $Res call({ 55 | Object? id = freezed, 56 | Object? name = freezed, 57 | Object? emptySlots = freezed, 58 | Object? freeNormalBikes = freezed, 59 | Object? freeEBikes = freezed, 60 | Object? location = freezed, 61 | Object? isFavorite = freezed, 62 | }) { 63 | return _then(_value.copyWith( 64 | id: id == freezed 65 | ? _value.id 66 | : id // ignore: cast_nullable_to_non_nullable 67 | as String, 68 | name: name == freezed 69 | ? _value.name 70 | : name // ignore: cast_nullable_to_non_nullable 71 | as String, 72 | emptySlots: emptySlots == freezed 73 | ? _value.emptySlots 74 | : emptySlots // ignore: cast_nullable_to_non_nullable 75 | as int, 76 | freeNormalBikes: freeNormalBikes == freezed 77 | ? _value.freeNormalBikes 78 | : freeNormalBikes // ignore: cast_nullable_to_non_nullable 79 | as int, 80 | freeEBikes: freeEBikes == freezed 81 | ? _value.freeEBikes 82 | : freeEBikes // ignore: cast_nullable_to_non_nullable 83 | as int, 84 | location: location == freezed 85 | ? _value.location 86 | : location // ignore: cast_nullable_to_non_nullable 87 | as LatLng, 88 | isFavorite: isFavorite == freezed 89 | ? _value.isFavorite 90 | : isFavorite // ignore: cast_nullable_to_non_nullable 91 | as bool, 92 | )); 93 | } 94 | } 95 | 96 | /// @nodoc 97 | abstract class _$$_StationCopyWith<$Res> implements $StationCopyWith<$Res> { 98 | factory _$$_StationCopyWith( 99 | _$_Station value, $Res Function(_$_Station) then) = 100 | __$$_StationCopyWithImpl<$Res>; 101 | @override 102 | $Res call( 103 | {String id, 104 | String name, 105 | int emptySlots, 106 | int freeNormalBikes, 107 | int freeEBikes, 108 | LatLng location, 109 | bool isFavorite}); 110 | } 111 | 112 | /// @nodoc 113 | class __$$_StationCopyWithImpl<$Res> extends _$StationCopyWithImpl<$Res> 114 | implements _$$_StationCopyWith<$Res> { 115 | __$$_StationCopyWithImpl(_$_Station _value, $Res Function(_$_Station) _then) 116 | : super(_value, (v) => _then(v as _$_Station)); 117 | 118 | @override 119 | _$_Station get _value => super._value as _$_Station; 120 | 121 | @override 122 | $Res call({ 123 | Object? id = freezed, 124 | Object? name = freezed, 125 | Object? emptySlots = freezed, 126 | Object? freeNormalBikes = freezed, 127 | Object? freeEBikes = freezed, 128 | Object? location = freezed, 129 | Object? isFavorite = freezed, 130 | }) { 131 | return _then(_$_Station( 132 | id: id == freezed 133 | ? _value.id 134 | : id // ignore: cast_nullable_to_non_nullable 135 | as String, 136 | name: name == freezed 137 | ? _value.name 138 | : name // ignore: cast_nullable_to_non_nullable 139 | as String, 140 | emptySlots: emptySlots == freezed 141 | ? _value.emptySlots 142 | : emptySlots // ignore: cast_nullable_to_non_nullable 143 | as int, 144 | freeNormalBikes: freeNormalBikes == freezed 145 | ? _value.freeNormalBikes 146 | : freeNormalBikes // ignore: cast_nullable_to_non_nullable 147 | as int, 148 | freeEBikes: freeEBikes == freezed 149 | ? _value.freeEBikes 150 | : freeEBikes // ignore: cast_nullable_to_non_nullable 151 | as int, 152 | location: location == freezed 153 | ? _value.location 154 | : location // ignore: cast_nullable_to_non_nullable 155 | as LatLng, 156 | isFavorite: isFavorite == freezed 157 | ? _value.isFavorite 158 | : isFavorite // ignore: cast_nullable_to_non_nullable 159 | as bool, 160 | )); 161 | } 162 | } 163 | 164 | /// @nodoc 165 | 166 | class _$_Station extends _Station { 167 | const _$_Station( 168 | {required this.id, 169 | required this.name, 170 | required this.emptySlots, 171 | required this.freeNormalBikes, 172 | required this.freeEBikes, 173 | required this.location, 174 | required this.isFavorite}) 175 | : super._(); 176 | 177 | @override 178 | final String id; 179 | @override 180 | final String name; 181 | @override 182 | final int emptySlots; 183 | @override 184 | final int freeNormalBikes; 185 | @override 186 | final int freeEBikes; 187 | @override 188 | final LatLng location; 189 | @override 190 | final bool isFavorite; 191 | 192 | @override 193 | String toString() { 194 | return 'Station(id: $id, name: $name, emptySlots: $emptySlots, freeNormalBikes: $freeNormalBikes, freeEBikes: $freeEBikes, location: $location, isFavorite: $isFavorite)'; 195 | } 196 | 197 | @override 198 | bool operator ==(dynamic other) { 199 | return identical(this, other) || 200 | (other.runtimeType == runtimeType && 201 | other is _$_Station && 202 | const DeepCollectionEquality().equals(other.id, id) && 203 | const DeepCollectionEquality().equals(other.name, name) && 204 | const DeepCollectionEquality() 205 | .equals(other.emptySlots, emptySlots) && 206 | const DeepCollectionEquality() 207 | .equals(other.freeNormalBikes, freeNormalBikes) && 208 | const DeepCollectionEquality() 209 | .equals(other.freeEBikes, freeEBikes) && 210 | const DeepCollectionEquality().equals(other.location, location) && 211 | const DeepCollectionEquality() 212 | .equals(other.isFavorite, isFavorite)); 213 | } 214 | 215 | @override 216 | int get hashCode => Object.hash( 217 | runtimeType, 218 | const DeepCollectionEquality().hash(id), 219 | const DeepCollectionEquality().hash(name), 220 | const DeepCollectionEquality().hash(emptySlots), 221 | const DeepCollectionEquality().hash(freeNormalBikes), 222 | const DeepCollectionEquality().hash(freeEBikes), 223 | const DeepCollectionEquality().hash(location), 224 | const DeepCollectionEquality().hash(isFavorite)); 225 | 226 | @JsonKey(ignore: true) 227 | @override 228 | _$$_StationCopyWith<_$_Station> get copyWith => 229 | __$$_StationCopyWithImpl<_$_Station>(this, _$identity); 230 | } 231 | 232 | abstract class _Station extends Station { 233 | const factory _Station( 234 | {required final String id, 235 | required final String name, 236 | required final int emptySlots, 237 | required final int freeNormalBikes, 238 | required final int freeEBikes, 239 | required final LatLng location, 240 | required final bool isFavorite}) = _$_Station; 241 | const _Station._() : super._(); 242 | 243 | @override 244 | String get id => throw _privateConstructorUsedError; 245 | @override 246 | String get name => throw _privateConstructorUsedError; 247 | @override 248 | int get emptySlots => throw _privateConstructorUsedError; 249 | @override 250 | int get freeNormalBikes => throw _privateConstructorUsedError; 251 | @override 252 | int get freeEBikes => throw _privateConstructorUsedError; 253 | @override 254 | LatLng get location => throw _privateConstructorUsedError; 255 | @override 256 | bool get isFavorite => throw _privateConstructorUsedError; 257 | @override 258 | @JsonKey(ignore: true) 259 | _$$_StationCopyWith<_$_Station> get copyWith => 260 | throw _privateConstructorUsedError; 261 | } 262 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/lib/src/repositories/station_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:either_dart/either.dart'; 3 | 4 | import '../models/station.dart'; 5 | 6 | abstract class StationRepository { 7 | Future>> getServiceStations(String serviceId); 8 | } 9 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/lib/src/responses/sorted_stations_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:latlong2/latlong.dart'; 3 | 4 | import '../models/station.dart'; 5 | 6 | @immutable 7 | class SortedStationsResponse { 8 | final LatLng? sortedFrom; 9 | final List stations; 10 | 11 | const SortedStationsResponse({ 12 | this.sortedFrom, 13 | required this.stations, 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/lib/src/use_cases/get_service_stations.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:either_dart/either.dart'; 3 | 4 | import '../models/station.dart'; 5 | 6 | abstract class GetServiceStations { 7 | Future>> call(String serviceId); 8 | } 9 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/lib/src/use_cases/get_service_stations_sorted_by_distance.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:either_dart/either.dart'; 3 | 4 | import '../responses/sorted_stations_response.dart'; 5 | 6 | abstract class GetServiceStationsSortedByDistance { 7 | Future> call(String serviceId); 8 | } 9 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/lib/src/use_cases/impl/get_service_stations_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:either_dart/either.dart'; 3 | 4 | import '../../models/station.dart'; 5 | import '../../repositories/station_repository.dart'; 6 | import '../get_service_stations.dart'; 7 | 8 | class GetServiceStationsImpl implements GetServiceStations { 9 | final StationRepository _stationRepository; 10 | 11 | GetServiceStationsImpl(this._stationRepository); 12 | 13 | @override 14 | Future>> call(String serviceId) { 15 | return _stationRepository.getServiceStations(serviceId); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/lib/src/use_cases/impl/get_service_stations_sorted_by_distance_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:_location_use_cases/location_use_cases.dart'; 3 | import 'package:either_dart/either.dart'; 4 | 5 | import '../../responses/sorted_stations_response.dart'; 6 | import '../get_service_stations.dart'; 7 | import '../get_service_stations_sorted_by_distance.dart'; 8 | 9 | class GetServiceStationsSortedByDistanceImpl 10 | implements GetServiceStationsSortedByDistance { 11 | final GetServiceStations _getServiceStations; 12 | 13 | final GetCurrentLocation _getCurrentLocation; 14 | 15 | GetServiceStationsSortedByDistanceImpl( 16 | this._getServiceStations, 17 | this._getCurrentLocation, 18 | ); 19 | 20 | @override 21 | Future> call( 22 | String serviceId) { 23 | return _getServiceStations(serviceId).thenRight((stations) { 24 | return _getCurrentLocation(Accuracy.high).fold( 25 | (failure) { 26 | return Right(SortedStationsResponse(stations: stations)); 27 | }, 28 | (location) { 29 | return Right(SortedStationsResponse( 30 | stations: stations.toList() 31 | ..sort((a, b) { 32 | final aIndex = a.distanceToSortIndex(location); 33 | final bIndex = b.distanceToSortIndex(location); 34 | return aIndex.compareTo(bIndex); 35 | }), 36 | sortedFrom: location, 37 | )); 38 | }, 39 | ); 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/packages/real_adapters/lib/dependency_configurator.dart: -------------------------------------------------------------------------------- 1 | import 'package:_bike_stations_use_cases/private_ports.dart'; 2 | import 'package:_core/core.dart'; 3 | import 'package:get_it/get_it.dart'; 4 | 5 | import 'src/data_sources/service_api/station_api_data_source.dart'; 6 | import 'src/repositories/station_repository_impl.dart'; 7 | 8 | class BikeStationsRealAdaptersDependencyConfigurator 9 | implements DependencyConfigurator { 10 | @override 11 | void configureDependencies( 12 | DependencyConfigurationContext context, 13 | GetIt getIt, 14 | ) { 15 | getIt.registerFactory( 16 | () => StationApiDataSourceImpl()); 17 | 18 | getIt.registerFactory( 19 | () => StationRepositoryImpl(getIt())); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/packages/real_adapters/lib/src/data_sources/service_api/dtos/station_dto.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | import 'station_extra_dto.dart'; 4 | 5 | part 'station_dto.g.dart'; 6 | 7 | @JsonSerializable() 8 | class StationDto { 9 | final String id; 10 | 11 | final String name; 12 | 13 | final double latitude; 14 | 15 | final double longitude; 16 | 17 | @JsonKey(name: 'empty_slots') 18 | final int emptySlots; 19 | 20 | @JsonKey(name: 'free_bikes') 21 | final int freeBikes; 22 | 23 | final String timestamp; 24 | 25 | final StationExtraDto? extra; 26 | 27 | const StationDto({ 28 | required this.id, 29 | required this.name, 30 | required this.latitude, 31 | required this.longitude, 32 | required this.emptySlots, 33 | required this.freeBikes, 34 | required this.timestamp, 35 | this.extra, 36 | }); 37 | 38 | factory StationDto.fromJson(Map json) => 39 | _$StationDtoFromJson(json); 40 | 41 | Map toJson() => _$StationDtoToJson(this); 42 | } 43 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/packages/real_adapters/lib/src/data_sources/service_api/dtos/station_dto.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'station_dto.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | StationDto _$StationDtoFromJson(Map json) => StationDto( 10 | id: json['id'] as String, 11 | name: json['name'] as String, 12 | latitude: (json['latitude'] as num).toDouble(), 13 | longitude: (json['longitude'] as num).toDouble(), 14 | emptySlots: json['empty_slots'] as int, 15 | freeBikes: json['free_bikes'] as int, 16 | timestamp: json['timestamp'] as String, 17 | extra: json['extra'] == null 18 | ? null 19 | : StationExtraDto.fromJson(json['extra'] as Map), 20 | ); 21 | 22 | Map _$StationDtoToJson(StationDto instance) => 23 | { 24 | 'id': instance.id, 25 | 'name': instance.name, 26 | 'latitude': instance.latitude, 27 | 'longitude': instance.longitude, 28 | 'empty_slots': instance.emptySlots, 29 | 'free_bikes': instance.freeBikes, 30 | 'timestamp': instance.timestamp, 31 | 'extra': instance.extra, 32 | }; 33 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/packages/real_adapters/lib/src/data_sources/service_api/dtos/station_extra_dto.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'station_extra_dto.g.dart'; 4 | 5 | @JsonSerializable(fieldRename: FieldRename.snake) 6 | class StationExtraDto { 7 | @JsonKey(name: 'has_ebikes') 8 | final bool? hasEBikes; 9 | 10 | @JsonKey(name: 'ebikes') 11 | final int? eBikes; 12 | 13 | final int? normalBikes; 14 | 15 | final bool? online; 16 | 17 | const StationExtraDto({ 18 | this.hasEBikes, 19 | this.eBikes, 20 | this.normalBikes, 21 | this.online, 22 | }); 23 | 24 | factory StationExtraDto.fromJson(Map json) => 25 | _$StationExtraDtoFromJson(json); 26 | 27 | Map toJson() => _$StationExtraDtoToJson(this); 28 | } 29 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/packages/real_adapters/lib/src/data_sources/service_api/dtos/station_extra_dto.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'station_extra_dto.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | StationExtraDto _$StationExtraDtoFromJson(Map json) => 10 | StationExtraDto( 11 | hasEBikes: json['has_ebikes'] as bool?, 12 | eBikes: json['ebikes'] as int?, 13 | normalBikes: json['normal_bikes'] as int?, 14 | online: json['online'] as bool?, 15 | ); 16 | 17 | Map _$StationExtraDtoToJson(StationExtraDto instance) => 18 | { 19 | 'has_ebikes': instance.hasEBikes, 20 | 'ebikes': instance.eBikes, 21 | 'normal_bikes': instance.normalBikes, 22 | 'online': instance.online, 23 | }; 24 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/packages/real_adapters/lib/src/data_sources/service_api/mappers/service_dto_mapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:_bike_stations_use_cases/bike_stations_use_cases.dart'; 2 | import 'package:latlong2/latlong.dart'; 3 | 4 | import '../dtos/station_dto.dart'; 5 | 6 | extension StationDtoMapper on StationDto { 7 | Station toService() { 8 | final int freeNormalBikes; 9 | final int freeEBikes; 10 | if (extra?.hasEBikes ?? false) { 11 | freeNormalBikes = extra?.normalBikes ?? freeBikes; 12 | freeEBikes = extra?.eBikes ?? 0; 13 | } else { 14 | freeNormalBikes = freeBikes; 15 | freeEBikes = 0; 16 | } 17 | return Station( 18 | id: id, 19 | name: name, 20 | emptySlots: emptySlots, 21 | freeNormalBikes: freeNormalBikes, 22 | freeEBikes: freeEBikes, 23 | location: LatLng(latitude, longitude), 24 | isFavorite: false, 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/packages/real_adapters/lib/src/data_sources/service_api/station_api_data_source.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:http/http.dart' as http; 5 | 6 | import 'dtos/station_dto.dart'; 7 | 8 | abstract class StationApiDataSource { 9 | Future> getAllStations(String serviceId); 10 | } 11 | 12 | class StationApiDataSourceImpl implements StationApiDataSource { 13 | @override 14 | Future> getAllStations(String serviceId) async { 15 | final url = Uri.parse('http://api.citybik.es/v2/networks/$serviceId'); 16 | final response = await http.get(url); 17 | if (response.statusCode == 200) { 18 | final json = jsonDecode(response.body); 19 | final stations = json['network']['stations'] as List; 20 | return stations.map((station) { 21 | return StationDto.fromJson(station as Map); 22 | }).toList(); 23 | } else { 24 | throw HttpException('HTTP status code ${response.statusCode}'); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/packages/real_adapters/lib/src/repositories/station_repository_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:_bike_stations_use_cases/bike_stations_use_cases.dart'; 2 | import 'package:_bike_stations_use_cases/private_ports.dart'; 3 | import 'package:_core/core.dart'; 4 | import 'package:either_dart/either.dart'; 5 | 6 | import '../data_sources/service_api/mappers/service_dto_mapper.dart'; 7 | import '../data_sources/service_api/station_api_data_source.dart'; 8 | 9 | class StationRepositoryImpl implements StationRepository { 10 | final StationApiDataSource _stationApiDataSource; 11 | 12 | StationRepositoryImpl(this._stationApiDataSource); 13 | 14 | @override 15 | Future>> getServiceStations(String serviceId) async { 16 | try { 17 | final dtos = await _stationApiDataSource.getAllStations(serviceId); 18 | return Right(dtos.map((dto) => dto.toService()).toList()); 19 | } on Exception catch (e) { 20 | return Left(NetworkFailure(e)); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/packages/real_adapters/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _bike_stations_use_cases_real_adapters 2 | description: Bike stations use cases real adapters 3 | 4 | publish_to: 'none' 5 | 6 | environment: 7 | sdk: ">=2.16.2 <3.0.0" 8 | 9 | dependencies: 10 | either_dart: ^0.1.4 11 | freezed_annotation: ^2.0.3 12 | http: ^0.13.4 13 | json_annotation: ^4.5.0 14 | 15 | _bike_stations_use_cases: 16 | path: ../.. 17 | _core: 18 | path: ../../../../shared/all/core 19 | 20 | dev_dependencies: 21 | build_runner: ^2.1.11 22 | json_serializable: ^6.2.0 23 | flutter_lints: ^1.0.0 24 | -------------------------------------------------------------------------------- /packages/use_cases/bike_stations/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _bike_stations_use_cases 2 | description: Bike stations use cases 3 | 4 | publish_to: 'none' 5 | 6 | environment: 7 | sdk: ">=2.16.2 <3.0.0" 8 | 9 | dependencies: 10 | distance: ^1.0.0 11 | either_dart: ^0.1.4 12 | freezed_annotation: ^2.0.3 13 | latlong2: ^0.8.1 14 | 15 | _core: 16 | path: ../../shared/all/core 17 | _location_use_cases: 18 | path: ../../use_cases/location 19 | 20 | dev_dependencies: 21 | build_runner: ^2.1.11 22 | flutter_lints: ^1.0.0 23 | freezed: ^2.0.3+1 24 | -------------------------------------------------------------------------------- /packages/use_cases/location/lib/dependency_configurator.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:get_it/get_it.dart'; 3 | 4 | import 'src/repositories/location_repository.dart'; 5 | import 'src/use_cases/ask_location_permission.dart'; 6 | import 'src/use_cases/get_current_location.dart'; 7 | import 'src/use_cases/get_last_know_location.dart'; 8 | import 'src/use_cases/impl/ask_location_permission_impl.dart'; 9 | import 'src/use_cases/impl/get_current_location_impl.dart'; 10 | import 'src/use_cases/impl/get_last_know_location_impl.dart'; 11 | import 'src/use_cases/impl/is_location_service_enabled_impl.dart'; 12 | import 'src/use_cases/is_location_service_enabled.dart'; 13 | 14 | class LocationDependencyConfigurator implements DependencyConfigurator { 15 | @override 16 | void configureDependencies( 17 | DependencyConfigurationContext context, 18 | GetIt getIt, 19 | ) { 20 | getIt.registerFactory( 21 | () => AskLocationPermissionImpl(getIt())); 22 | 23 | getIt.registerFactory( 24 | () => GetCurrentLocationImpl(getIt())); 25 | 26 | getIt.registerFactory( 27 | () => GetLastKnowLocationImpl(getIt())); 28 | 29 | getIt.registerFactory( 30 | () => IsLocationServiceEnabledImpl(getIt())); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/use_cases/location/lib/location_use_cases.dart: -------------------------------------------------------------------------------- 1 | // Models 2 | export 'src/models/accuracy.dart' show Accuracy; 3 | export 'src/models/location_permission.dart' show LocationPermission; 4 | 5 | // Use cases 6 | export 'src/use_cases/ask_location_permission.dart' show AskLocationPermission; 7 | export 'src/use_cases/get_current_location.dart' show GetCurrentLocation; 8 | export 'src/use_cases/get_last_know_location.dart' show GetLastKnownLocation; 9 | export 'src/use_cases/is_location_service_enabled.dart' 10 | show IsLocationServiceEnabled; 11 | -------------------------------------------------------------------------------- /packages/use_cases/location/lib/private_ports.dart: -------------------------------------------------------------------------------- 1 | // Repositories 2 | export 'src/repositories/location_repository.dart' show LocationRepository; -------------------------------------------------------------------------------- /packages/use_cases/location/lib/src/models/accuracy.dart: -------------------------------------------------------------------------------- 1 | enum Accuracy { 2 | low, 3 | high, 4 | } 5 | -------------------------------------------------------------------------------- /packages/use_cases/location/lib/src/models/location_permission.dart: -------------------------------------------------------------------------------- 1 | enum LocationPermission { 2 | denied, 3 | deniedForever, 4 | whileInUse, 5 | always, 6 | } 7 | -------------------------------------------------------------------------------- /packages/use_cases/location/lib/src/repositories/location_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:either_dart/either.dart'; 3 | import 'package:latlong2/latlong.dart'; 4 | 5 | import '../models/accuracy.dart'; 6 | import '../models/location_permission.dart'; 7 | 8 | abstract class LocationRepository { 9 | Future isLocationServiceEnabled(); 10 | 11 | Future askLocationPermission(); 12 | 13 | Future> getLastKnowLocation(); 14 | 15 | Future> getCurrentLocation( 16 | Accuracy accuracy); 17 | } 18 | -------------------------------------------------------------------------------- /packages/use_cases/location/lib/src/use_cases/ask_location_permission.dart: -------------------------------------------------------------------------------- 1 | import '../models/location_permission.dart'; 2 | 3 | abstract class AskLocationPermission { 4 | Future call(); 5 | } 6 | -------------------------------------------------------------------------------- /packages/use_cases/location/lib/src/use_cases/get_current_location.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:either_dart/either.dart'; 3 | import 'package:latlong2/latlong.dart'; 4 | 5 | import '../models/accuracy.dart'; 6 | 7 | abstract class GetCurrentLocation { 8 | Future> call(Accuracy accuracy); 9 | } 10 | -------------------------------------------------------------------------------- /packages/use_cases/location/lib/src/use_cases/get_last_know_location.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:either_dart/either.dart'; 3 | import 'package:latlong2/latlong.dart'; 4 | 5 | abstract class GetLastKnownLocation { 6 | Future> call(); 7 | } 8 | -------------------------------------------------------------------------------- /packages/use_cases/location/lib/src/use_cases/impl/ask_location_permission_impl.dart: -------------------------------------------------------------------------------- 1 | import '../../models/location_permission.dart'; 2 | import '../../repositories/location_repository.dart'; 3 | import '../ask_location_permission.dart'; 4 | 5 | class AskLocationPermissionImpl implements AskLocationPermission { 6 | final LocationRepository _locationRepository; 7 | 8 | AskLocationPermissionImpl(this._locationRepository); 9 | 10 | @override 11 | Future call() { 12 | return _locationRepository.askLocationPermission(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/use_cases/location/lib/src/use_cases/impl/get_current_location_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:either_dart/either.dart'; 3 | import 'package:latlong2/latlong.dart'; 4 | 5 | import '../../models/accuracy.dart'; 6 | import '../../repositories/location_repository.dart'; 7 | import '../get_current_location.dart'; 8 | 9 | class GetCurrentLocationImpl implements GetCurrentLocation { 10 | final LocationRepository _locationRepository; 11 | 12 | GetCurrentLocationImpl(this._locationRepository); 13 | 14 | @override 15 | Future> call(Accuracy accuracy) { 16 | return _locationRepository.getCurrentLocation(accuracy); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/use_cases/location/lib/src/use_cases/impl/get_last_know_location_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:either_dart/either.dart'; 3 | import 'package:latlong2/latlong.dart'; 4 | 5 | import '../../models/accuracy.dart'; 6 | import '../../repositories/location_repository.dart'; 7 | import '../get_last_know_location.dart'; 8 | 9 | class GetLastKnowLocationImpl implements GetLastKnownLocation { 10 | final LocationRepository _locationRepository; 11 | 12 | GetLastKnowLocationImpl(this._locationRepository); 13 | 14 | @override 15 | Future> call() { 16 | return _locationRepository.getLastKnowLocation().thenRight((value) { 17 | if (value != null) { 18 | return Future.value(Right(value)); 19 | } else { 20 | return _locationRepository.getCurrentLocation(Accuracy.low); 21 | } 22 | }); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/use_cases/location/lib/src/use_cases/impl/is_location_service_enabled_impl.dart: -------------------------------------------------------------------------------- 1 | import '../../repositories/location_repository.dart'; 2 | import '../is_location_service_enabled.dart'; 3 | 4 | class IsLocationServiceEnabledImpl implements IsLocationServiceEnabled { 5 | final LocationRepository _locationRepository; 6 | 7 | IsLocationServiceEnabledImpl(this._locationRepository); 8 | 9 | @override 10 | Future call() { 11 | return _locationRepository.isLocationServiceEnabled(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/use_cases/location/lib/src/use_cases/is_location_service_enabled.dart: -------------------------------------------------------------------------------- 1 | abstract class IsLocationServiceEnabled { 2 | Future call(); 3 | } 4 | -------------------------------------------------------------------------------- /packages/use_cases/location/packages/fake_adapters/lib/dependency_configurator.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:_location_use_cases/private_ports.dart'; 3 | import 'package:get_it/get_it.dart'; 4 | 5 | import 'src/repositories/location_repository_impl.dart'; 6 | 7 | class LocationFakeAdaptersDependencyConfigurator 8 | implements DependencyConfigurator { 9 | @override 10 | void configureDependencies( 11 | DependencyConfigurationContext context, 12 | GetIt getIt, 13 | ) { 14 | getIt.registerFactory(() => LocationRepositoryImpl()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/use_cases/location/packages/fake_adapters/lib/src/repositories/location_repository_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:_location_use_cases/location_use_cases.dart'; 3 | import 'package:_location_use_cases/private_ports.dart'; 4 | import 'package:either_dart/either.dart'; 5 | import 'package:latlong2/latlong.dart'; 6 | 7 | class LocationRepositoryImpl implements LocationRepository { 8 | static final _barcelonaCenter = LatLng(41.38726924427196, 2.164616602593128); 9 | 10 | @override 11 | Future isLocationServiceEnabled() { 12 | return Future.value(true); 13 | } 14 | 15 | @override 16 | Future askLocationPermission() { 17 | return Future.value(LocationPermission.always); 18 | } 19 | 20 | @override 21 | Future> getCurrentLocation( 22 | Accuracy accuracy) { 23 | return Future.value(Right(_barcelonaCenter)); 24 | } 25 | 26 | @override 27 | Future> getLastKnowLocation() { 28 | return Future.value(Right(_barcelonaCenter)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/use_cases/location/packages/fake_adapters/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _core: 5 | dependency: "direct main" 6 | description: 7 | path: "../../../../shared/all/core" 8 | relative: true 9 | source: path 10 | version: "0.0.0" 11 | _location_use_cases: 12 | dependency: "direct main" 13 | description: 14 | path: "../.." 15 | relative: true 16 | source: path 17 | version: "0.0.0" 18 | async: 19 | dependency: transitive 20 | description: 21 | name: async 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "2.9.0" 25 | clock: 26 | dependency: transitive 27 | description: 28 | name: clock 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.1.0" 32 | collection: 33 | dependency: transitive 34 | description: 35 | name: collection 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.15.0" 39 | either_dart: 40 | dependency: transitive 41 | description: 42 | name: either_dart 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "0.1.4" 46 | flutter_lints: 47 | dependency: "direct dev" 48 | description: 49 | name: flutter_lints 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.0.4" 53 | get_it: 54 | dependency: transitive 55 | description: 56 | name: get_it 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "7.2.0" 60 | intl: 61 | dependency: transitive 62 | description: 63 | name: intl 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "0.17.0" 67 | latlong2: 68 | dependency: transitive 69 | description: 70 | name: latlong2 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "0.8.1" 74 | lints: 75 | dependency: transitive 76 | description: 77 | name: lints 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "1.0.1" 81 | meta: 82 | dependency: transitive 83 | description: 84 | name: meta 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "1.7.0" 88 | path: 89 | dependency: transitive 90 | description: 91 | name: path 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "1.8.2" 95 | sdks: 96 | dart: ">=2.16.2 <3.0.0" 97 | -------------------------------------------------------------------------------- /packages/use_cases/location/packages/fake_adapters/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _location_use_cases_fake_adapters 2 | description: Location use cases fake adapters 3 | 4 | publish_to: 'none' 5 | 6 | environment: 7 | sdk: ">=2.16.2 <3.0.0" 8 | 9 | dependencies: 10 | _core: 11 | path: ../../../../shared/all/core 12 | _location_use_cases: 13 | path: ../.. 14 | 15 | dev_dependencies: 16 | flutter_lints: ^1.0.0 17 | -------------------------------------------------------------------------------- /packages/use_cases/location/packages/real_adapters/lib/dependency_configurator.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:_location_use_cases/private_ports.dart'; 3 | import 'package:get_it/get_it.dart'; 4 | 5 | import 'src/repositories/location_repository_impl.dart'; 6 | 7 | class LocationRealAdaptersDependencyConfigurator 8 | implements DependencyConfigurator { 9 | @override 10 | void configureDependencies( 11 | DependencyConfigurationContext context, 12 | GetIt getIt, 13 | ) { 14 | getIt.registerFactory(() => LocationRepositoryImpl()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/use_cases/location/packages/real_adapters/lib/src/mappers/accuracy_mapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:_location_use_cases/location_use_cases.dart'; 2 | import 'package:geolocator/geolocator.dart'; 3 | 4 | extension AccuracyMapper on Accuracy { 5 | LocationAccuracy toLocationAccuracy() { 6 | switch (this) { 7 | case Accuracy.high: 8 | return LocationAccuracy.best; 9 | case Accuracy.low: 10 | return LocationAccuracy.low; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/use_cases/location/packages/real_adapters/lib/src/mappers/location_permission_mapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:_location_use_cases/location_use_cases.dart'; 2 | import 'package:geolocator/geolocator.dart' as geo; 3 | 4 | extension LocationPermissionMapper on geo.LocationPermission { 5 | LocationPermission toLocationPermission() { 6 | switch (this) { 7 | case geo.LocationPermission.denied: 8 | return LocationPermission.denied; 9 | case geo.LocationPermission.deniedForever: 10 | return LocationPermission.deniedForever; 11 | case geo.LocationPermission.whileInUse: 12 | return LocationPermission.whileInUse; 13 | case geo.LocationPermission.always: 14 | return LocationPermission.always; 15 | case geo.LocationPermission.unableToDetermine: 16 | return LocationPermission.deniedForever; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/use_cases/location/packages/real_adapters/lib/src/mappers/position_mapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:geolocator/geolocator.dart'; 2 | import 'package:latlong2/latlong.dart'; 3 | 4 | extension PositionMapper on Position { 5 | LatLng toLatLng() { 6 | return LatLng( 7 | latitude, 8 | longitude, 9 | ); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/use_cases/location/packages/real_adapters/lib/src/repositories/location_repository_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:_core/core.dart'; 2 | import 'package:_location_use_cases/location_use_cases.dart'; 3 | import 'package:_location_use_cases/private_ports.dart'; 4 | import 'package:either_dart/either.dart'; 5 | import 'package:geolocator/geolocator.dart' as geo; 6 | import 'package:latlong2/latlong.dart'; 7 | 8 | import '../mappers/accuracy_mapper.dart'; 9 | import '../mappers/location_permission_mapper.dart'; 10 | import '../mappers/position_mapper.dart'; 11 | 12 | class LocationRepositoryImpl implements LocationRepository { 13 | @override 14 | Future isLocationServiceEnabled() { 15 | return geo.Geolocator.isLocationServiceEnabled(); 16 | } 17 | 18 | @override 19 | Future askLocationPermission() { 20 | return geo.Geolocator.requestPermission() 21 | .then((permission) => permission.toLocationPermission()); 22 | } 23 | 24 | @override 25 | Future> getCurrentLocation( 26 | Accuracy accuracy) async { 27 | try { 28 | final position = await geo.Geolocator.getCurrentPosition( 29 | desiredAccuracy: accuracy.toLocationAccuracy()); 30 | return Right(position.toLatLng()); 31 | } on geo.LocationServiceDisabledException catch (e) { 32 | return Left(PermissionFailure(e)); 33 | } on geo.PermissionDeniedException catch (e) { 34 | return Left(PermissionFailure(e)); 35 | } 36 | } 37 | 38 | @override 39 | Future> getLastKnowLocation() async { 40 | try { 41 | final position = await geo.Geolocator.getLastKnownPosition(); 42 | return Right(position?.toLatLng()); 43 | } on geo.LocationServiceDisabledException catch (e) { 44 | return Left(PermissionFailure(e)); 45 | } on geo.PermissionDeniedException catch (e) { 46 | return Left(PermissionFailure(e)); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/use_cases/location/packages/real_adapters/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _core: 5 | dependency: "direct main" 6 | description: 7 | path: "../../../../shared/all/core" 8 | relative: true 9 | source: path 10 | version: "0.0.0" 11 | _location_use_cases: 12 | dependency: "direct main" 13 | description: 14 | path: "../.." 15 | relative: true 16 | source: path 17 | version: "0.0.0" 18 | async: 19 | dependency: transitive 20 | description: 21 | name: async 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "2.8.2" 25 | boolean_selector: 26 | dependency: transitive 27 | description: 28 | name: boolean_selector 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "2.1.0" 32 | characters: 33 | dependency: transitive 34 | description: 35 | name: characters 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.2.0" 39 | charcode: 40 | dependency: transitive 41 | description: 42 | name: charcode 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.3.1" 46 | clock: 47 | dependency: transitive 48 | description: 49 | name: clock 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.1.0" 53 | collection: 54 | dependency: transitive 55 | description: 56 | name: collection 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.15.0" 60 | either_dart: 61 | dependency: transitive 62 | description: 63 | name: either_dart 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "0.1.4" 67 | fake_async: 68 | dependency: transitive 69 | description: 70 | name: fake_async 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "1.2.0" 74 | flutter: 75 | dependency: "direct main" 76 | description: flutter 77 | source: sdk 78 | version: "0.0.0" 79 | flutter_lints: 80 | dependency: "direct dev" 81 | description: 82 | name: flutter_lints 83 | url: "https://pub.dartlang.org" 84 | source: hosted 85 | version: "1.0.4" 86 | flutter_test: 87 | dependency: "direct dev" 88 | description: flutter 89 | source: sdk 90 | version: "0.0.0" 91 | flutter_web_plugins: 92 | dependency: transitive 93 | description: flutter 94 | source: sdk 95 | version: "0.0.0" 96 | geolocator: 97 | dependency: "direct main" 98 | description: 99 | name: geolocator 100 | url: "https://pub.dartlang.org" 101 | source: hosted 102 | version: "8.2.1" 103 | geolocator_android: 104 | dependency: transitive 105 | description: 106 | name: geolocator_android 107 | url: "https://pub.dartlang.org" 108 | source: hosted 109 | version: "3.1.8" 110 | geolocator_apple: 111 | dependency: transitive 112 | description: 113 | name: geolocator_apple 114 | url: "https://pub.dartlang.org" 115 | source: hosted 116 | version: "2.1.4" 117 | geolocator_platform_interface: 118 | dependency: transitive 119 | description: 120 | name: geolocator_platform_interface 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "4.0.5" 124 | geolocator_web: 125 | dependency: transitive 126 | description: 127 | name: geolocator_web 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "2.1.5" 131 | geolocator_windows: 132 | dependency: transitive 133 | description: 134 | name: geolocator_windows 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "0.1.1" 138 | get_it: 139 | dependency: transitive 140 | description: 141 | name: get_it 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "7.2.0" 145 | intl: 146 | dependency: transitive 147 | description: 148 | name: intl 149 | url: "https://pub.dartlang.org" 150 | source: hosted 151 | version: "0.17.0" 152 | js: 153 | dependency: transitive 154 | description: 155 | name: js 156 | url: "https://pub.dartlang.org" 157 | source: hosted 158 | version: "0.6.3" 159 | latlong2: 160 | dependency: transitive 161 | description: 162 | name: latlong2 163 | url: "https://pub.dartlang.org" 164 | source: hosted 165 | version: "0.8.1" 166 | lints: 167 | dependency: transitive 168 | description: 169 | name: lints 170 | url: "https://pub.dartlang.org" 171 | source: hosted 172 | version: "1.0.1" 173 | matcher: 174 | dependency: transitive 175 | description: 176 | name: matcher 177 | url: "https://pub.dartlang.org" 178 | source: hosted 179 | version: "0.12.11" 180 | material_color_utilities: 181 | dependency: transitive 182 | description: 183 | name: material_color_utilities 184 | url: "https://pub.dartlang.org" 185 | source: hosted 186 | version: "0.1.3" 187 | meta: 188 | dependency: transitive 189 | description: 190 | name: meta 191 | url: "https://pub.dartlang.org" 192 | source: hosted 193 | version: "1.7.0" 194 | path: 195 | dependency: transitive 196 | description: 197 | name: path 198 | url: "https://pub.dartlang.org" 199 | source: hosted 200 | version: "1.8.0" 201 | plugin_platform_interface: 202 | dependency: transitive 203 | description: 204 | name: plugin_platform_interface 205 | url: "https://pub.dartlang.org" 206 | source: hosted 207 | version: "2.1.2" 208 | sky_engine: 209 | dependency: transitive 210 | description: flutter 211 | source: sdk 212 | version: "0.0.99" 213 | source_span: 214 | dependency: transitive 215 | description: 216 | name: source_span 217 | url: "https://pub.dartlang.org" 218 | source: hosted 219 | version: "1.8.1" 220 | stack_trace: 221 | dependency: transitive 222 | description: 223 | name: stack_trace 224 | url: "https://pub.dartlang.org" 225 | source: hosted 226 | version: "1.10.0" 227 | stream_channel: 228 | dependency: transitive 229 | description: 230 | name: stream_channel 231 | url: "https://pub.dartlang.org" 232 | source: hosted 233 | version: "2.1.0" 234 | string_scanner: 235 | dependency: transitive 236 | description: 237 | name: string_scanner 238 | url: "https://pub.dartlang.org" 239 | source: hosted 240 | version: "1.1.0" 241 | term_glyph: 242 | dependency: transitive 243 | description: 244 | name: term_glyph 245 | url: "https://pub.dartlang.org" 246 | source: hosted 247 | version: "1.2.0" 248 | test_api: 249 | dependency: transitive 250 | description: 251 | name: test_api 252 | url: "https://pub.dartlang.org" 253 | source: hosted 254 | version: "0.4.8" 255 | typed_data: 256 | dependency: transitive 257 | description: 258 | name: typed_data 259 | url: "https://pub.dartlang.org" 260 | source: hosted 261 | version: "1.3.0" 262 | vector_math: 263 | dependency: transitive 264 | description: 265 | name: vector_math 266 | url: "https://pub.dartlang.org" 267 | source: hosted 268 | version: "2.1.1" 269 | sdks: 270 | dart: ">=2.16.2 <3.0.0" 271 | flutter: ">=2.8.0" 272 | -------------------------------------------------------------------------------- /packages/use_cases/location/packages/real_adapters/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _location_use_cases_real_adapters 2 | description: Location use cases real adapters 3 | 4 | publish_to: 'none' 5 | 6 | environment: 7 | sdk: ">=2.16.2 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | geolocator: ^8.2.1 13 | 14 | _core: 15 | path: ../../../../shared/all/core 16 | _location_use_cases: 17 | path: ../.. 18 | 19 | dev_dependencies: 20 | flutter_test: 21 | sdk: flutter 22 | flutter_lints: ^1.0.0 23 | -------------------------------------------------------------------------------- /packages/use_cases/location/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _core: 5 | dependency: "direct main" 6 | description: 7 | path: "../../shared/all/core" 8 | relative: true 9 | source: path 10 | version: "0.0.0" 11 | async: 12 | dependency: transitive 13 | description: 14 | name: async 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.8.2" 18 | clock: 19 | dependency: transitive 20 | description: 21 | name: clock 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.1.0" 25 | collection: 26 | dependency: transitive 27 | description: 28 | name: collection 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.15.0" 32 | either_dart: 33 | dependency: "direct main" 34 | description: 35 | name: either_dart 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "0.1.4" 39 | flutter_lints: 40 | dependency: "direct dev" 41 | description: 42 | name: flutter_lints 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.0.4" 46 | get_it: 47 | dependency: transitive 48 | description: 49 | name: get_it 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "7.2.0" 53 | intl: 54 | dependency: transitive 55 | description: 56 | name: intl 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "0.17.0" 60 | latlong2: 61 | dependency: "direct main" 62 | description: 63 | name: latlong2 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "0.8.1" 67 | lints: 68 | dependency: transitive 69 | description: 70 | name: lints 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "1.0.1" 74 | meta: 75 | dependency: transitive 76 | description: 77 | name: meta 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "1.7.0" 81 | path: 82 | dependency: transitive 83 | description: 84 | name: path 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "1.8.0" 88 | sdks: 89 | dart: ">=2.16.2 <3.0.0" 90 | -------------------------------------------------------------------------------- /packages/use_cases/location/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: _location_use_cases 2 | description: Location use cases 3 | 4 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 5 | 6 | environment: 7 | sdk: ">=2.16.2 <3.0.0" 8 | 9 | dependencies: 10 | either_dart: ^0.1.4 11 | latlong2: ^0.8.1 12 | 13 | _core: 14 | path: ../../shared/all/core 15 | 16 | dev_dependencies: 17 | flutter_lints: ^1.0.0 18 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _bike_services_features: 5 | dependency: "direct main" 6 | description: 7 | path: "packages/features/bike_services" 8 | relative: true 9 | source: path 10 | version: "0.0.0" 11 | _bike_services_use_cases: 12 | dependency: "direct main" 13 | description: 14 | path: "packages/use_cases/bike_services" 15 | relative: true 16 | source: path 17 | version: "0.0.0" 18 | _bike_services_use_cases_real_adapters: 19 | dependency: "direct main" 20 | description: 21 | path: "packages/use_cases/bike_services/packages/real_adapters" 22 | relative: true 23 | source: path 24 | version: "0.0.0" 25 | _bike_stations_features: 26 | dependency: "direct main" 27 | description: 28 | path: "packages/features/bike_stations" 29 | relative: true 30 | source: path 31 | version: "0.0.0" 32 | _bike_stations_use_cases: 33 | dependency: "direct main" 34 | description: 35 | path: "packages/use_cases/bike_stations" 36 | relative: true 37 | source: path 38 | version: "0.0.0" 39 | _bike_stations_use_cases_real_adapters: 40 | dependency: "direct main" 41 | description: 42 | path: "packages/use_cases/bike_stations/packages/real_adapters" 43 | relative: true 44 | source: path 45 | version: "0.0.0" 46 | _core: 47 | dependency: "direct main" 48 | description: 49 | path: "packages/shared/all/core" 50 | relative: true 51 | source: path 52 | version: "0.0.0" 53 | _intent_launcher: 54 | dependency: "direct main" 55 | description: 56 | path: "packages/shared/features/intent_launcher" 57 | relative: true 58 | source: path 59 | version: "0.0.0" 60 | _location_use_cases: 61 | dependency: "direct main" 62 | description: 63 | path: "packages/use_cases/location" 64 | relative: true 65 | source: path 66 | version: "0.0.0" 67 | _location_use_cases_real_adapters: 68 | dependency: "direct main" 69 | description: 70 | path: "packages/use_cases/location/packages/real_adapters" 71 | relative: true 72 | source: path 73 | version: "0.0.0" 74 | async: 75 | dependency: transitive 76 | description: 77 | name: async 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "2.8.2" 81 | bloc: 82 | dependency: transitive 83 | description: 84 | name: bloc 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "8.0.3" 88 | boolean_selector: 89 | dependency: transitive 90 | description: 91 | name: boolean_selector 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "2.1.0" 95 | characters: 96 | dependency: transitive 97 | description: 98 | name: characters 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "1.2.0" 102 | charcode: 103 | dependency: transitive 104 | description: 105 | name: charcode 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "1.3.1" 109 | clock: 110 | dependency: transitive 111 | description: 112 | name: clock 113 | url: "https://pub.dartlang.org" 114 | source: hosted 115 | version: "1.1.0" 116 | collection: 117 | dependency: transitive 118 | description: 119 | name: collection 120 | url: "https://pub.dartlang.org" 121 | source: hosted 122 | version: "1.15.0" 123 | cupertino_icons: 124 | dependency: "direct main" 125 | description: 126 | name: cupertino_icons 127 | url: "https://pub.dartlang.org" 128 | source: hosted 129 | version: "1.0.4" 130 | distance: 131 | dependency: transitive 132 | description: 133 | name: distance 134 | url: "https://pub.dartlang.org" 135 | source: hosted 136 | version: "1.0.0" 137 | either_dart: 138 | dependency: transitive 139 | description: 140 | name: either_dart 141 | url: "https://pub.dartlang.org" 142 | source: hosted 143 | version: "0.1.4" 144 | fake_async: 145 | dependency: transitive 146 | description: 147 | name: fake_async 148 | url: "https://pub.dartlang.org" 149 | source: hosted 150 | version: "1.2.0" 151 | flutter: 152 | dependency: "direct main" 153 | description: flutter 154 | source: sdk 155 | version: "0.0.0" 156 | flutter_bloc: 157 | dependency: transitive 158 | description: 159 | name: flutter_bloc 160 | url: "https://pub.dartlang.org" 161 | source: hosted 162 | version: "8.0.1" 163 | flutter_lints: 164 | dependency: "direct dev" 165 | description: 166 | name: flutter_lints 167 | url: "https://pub.dartlang.org" 168 | source: hosted 169 | version: "1.0.4" 170 | flutter_test: 171 | dependency: "direct dev" 172 | description: flutter 173 | source: sdk 174 | version: "0.0.0" 175 | flutter_web_plugins: 176 | dependency: transitive 177 | description: flutter 178 | source: sdk 179 | version: "0.0.0" 180 | freezed_annotation: 181 | dependency: transitive 182 | description: 183 | name: freezed_annotation 184 | url: "https://pub.dartlang.org" 185 | source: hosted 186 | version: "2.0.3" 187 | geolocator: 188 | dependency: transitive 189 | description: 190 | name: geolocator 191 | url: "https://pub.dartlang.org" 192 | source: hosted 193 | version: "8.2.1" 194 | geolocator_android: 195 | dependency: transitive 196 | description: 197 | name: geolocator_android 198 | url: "https://pub.dartlang.org" 199 | source: hosted 200 | version: "3.1.8" 201 | geolocator_apple: 202 | dependency: transitive 203 | description: 204 | name: geolocator_apple 205 | url: "https://pub.dartlang.org" 206 | source: hosted 207 | version: "2.1.4" 208 | geolocator_platform_interface: 209 | dependency: transitive 210 | description: 211 | name: geolocator_platform_interface 212 | url: "https://pub.dartlang.org" 213 | source: hosted 214 | version: "4.0.5" 215 | geolocator_web: 216 | dependency: transitive 217 | description: 218 | name: geolocator_web 219 | url: "https://pub.dartlang.org" 220 | source: hosted 221 | version: "2.1.5" 222 | geolocator_windows: 223 | dependency: transitive 224 | description: 225 | name: geolocator_windows 226 | url: "https://pub.dartlang.org" 227 | source: hosted 228 | version: "0.1.1" 229 | get_it: 230 | dependency: "direct main" 231 | description: 232 | name: get_it 233 | url: "https://pub.dartlang.org" 234 | source: hosted 235 | version: "7.2.0" 236 | http: 237 | dependency: transitive 238 | description: 239 | name: http 240 | url: "https://pub.dartlang.org" 241 | source: hosted 242 | version: "0.13.4" 243 | http_parser: 244 | dependency: transitive 245 | description: 246 | name: http_parser 247 | url: "https://pub.dartlang.org" 248 | source: hosted 249 | version: "4.0.1" 250 | intl: 251 | dependency: transitive 252 | description: 253 | name: intl 254 | url: "https://pub.dartlang.org" 255 | source: hosted 256 | version: "0.17.0" 257 | js: 258 | dependency: transitive 259 | description: 260 | name: js 261 | url: "https://pub.dartlang.org" 262 | source: hosted 263 | version: "0.6.3" 264 | json_annotation: 265 | dependency: transitive 266 | description: 267 | name: json_annotation 268 | url: "https://pub.dartlang.org" 269 | source: hosted 270 | version: "4.5.0" 271 | latlong2: 272 | dependency: transitive 273 | description: 274 | name: latlong2 275 | url: "https://pub.dartlang.org" 276 | source: hosted 277 | version: "0.8.1" 278 | lints: 279 | dependency: transitive 280 | description: 281 | name: lints 282 | url: "https://pub.dartlang.org" 283 | source: hosted 284 | version: "1.0.1" 285 | matcher: 286 | dependency: transitive 287 | description: 288 | name: matcher 289 | url: "https://pub.dartlang.org" 290 | source: hosted 291 | version: "0.12.11" 292 | material_color_utilities: 293 | dependency: transitive 294 | description: 295 | name: material_color_utilities 296 | url: "https://pub.dartlang.org" 297 | source: hosted 298 | version: "0.1.3" 299 | meta: 300 | dependency: transitive 301 | description: 302 | name: meta 303 | url: "https://pub.dartlang.org" 304 | source: hosted 305 | version: "1.7.0" 306 | nested: 307 | dependency: transitive 308 | description: 309 | name: nested 310 | url: "https://pub.dartlang.org" 311 | source: hosted 312 | version: "1.0.0" 313 | path: 314 | dependency: transitive 315 | description: 316 | name: path 317 | url: "https://pub.dartlang.org" 318 | source: hosted 319 | version: "1.8.0" 320 | plugin_platform_interface: 321 | dependency: transitive 322 | description: 323 | name: plugin_platform_interface 324 | url: "https://pub.dartlang.org" 325 | source: hosted 326 | version: "2.1.2" 327 | provider: 328 | dependency: transitive 329 | description: 330 | name: provider 331 | url: "https://pub.dartlang.org" 332 | source: hosted 333 | version: "6.0.2" 334 | sky_engine: 335 | dependency: transitive 336 | description: flutter 337 | source: sdk 338 | version: "0.0.99" 339 | source_span: 340 | dependency: transitive 341 | description: 342 | name: source_span 343 | url: "https://pub.dartlang.org" 344 | source: hosted 345 | version: "1.8.1" 346 | stack_trace: 347 | dependency: transitive 348 | description: 349 | name: stack_trace 350 | url: "https://pub.dartlang.org" 351 | source: hosted 352 | version: "1.10.0" 353 | stream_channel: 354 | dependency: transitive 355 | description: 356 | name: stream_channel 357 | url: "https://pub.dartlang.org" 358 | source: hosted 359 | version: "2.1.0" 360 | string_scanner: 361 | dependency: transitive 362 | description: 363 | name: string_scanner 364 | url: "https://pub.dartlang.org" 365 | source: hosted 366 | version: "1.1.0" 367 | term_glyph: 368 | dependency: transitive 369 | description: 370 | name: term_glyph 371 | url: "https://pub.dartlang.org" 372 | source: hosted 373 | version: "1.2.0" 374 | test_api: 375 | dependency: transitive 376 | description: 377 | name: test_api 378 | url: "https://pub.dartlang.org" 379 | source: hosted 380 | version: "0.4.8" 381 | typed_data: 382 | dependency: transitive 383 | description: 384 | name: typed_data 385 | url: "https://pub.dartlang.org" 386 | source: hosted 387 | version: "1.3.0" 388 | vector_math: 389 | dependency: transitive 390 | description: 391 | name: vector_math 392 | url: "https://pub.dartlang.org" 393 | source: hosted 394 | version: "2.1.1" 395 | sdks: 396 | dart: ">=2.16.2 <3.0.0" 397 | flutter: ">=2.8.0" 398 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_modular_architecture 2 | description: Sample project of a modular architecture for Flutter apps 3 | 4 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 5 | 6 | version: 1.0.0+1 7 | 8 | environment: 9 | sdk: ">=2.16.2 <3.0.0" 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | cupertino_icons: ^1.0.2 15 | get_it: ^7.2.0 16 | 17 | # Shared 18 | _core: 19 | path: packages/shared/all/core 20 | _intent_launcher: 21 | path: packages/shared/features/intent_launcher 22 | 23 | # Use cases 24 | _bike_services_use_cases: 25 | path: packages/use_cases/bike_services 26 | _bike_services_use_cases_real_adapters: 27 | path: packages/use_cases/bike_services/packages/real_adapters 28 | _bike_stations_use_cases: 29 | path: packages/use_cases/bike_stations 30 | _bike_stations_use_cases_real_adapters: 31 | path: packages/use_cases/bike_stations/packages/real_adapters 32 | _location_use_cases: 33 | path: packages/use_cases/location 34 | _location_use_cases_real_adapters: 35 | path: packages/use_cases/location/packages/real_adapters 36 | 37 | # Features 38 | _bike_services_features: 39 | path: packages/features/bike_services 40 | _bike_stations_features: 41 | path: packages/features/bike_stations 42 | 43 | dev_dependencies: 44 | flutter_test: 45 | sdk: flutter 46 | flutter_lints: ^1.0.0 47 | 48 | flutter: 49 | uses-material-design: true 50 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter_modular_architecture/app.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | void main() { 12 | testWidgets('Welcome message smoke test', (WidgetTester tester) async { 13 | await tester.pumpWidget(const MyApp()); 14 | expect(find.text('Welcome to Bici!'), findsOneWidget); 15 | }); 16 | } 17 | --------------------------------------------------------------------------------