Architecture - Frontend
The documentation is compatible with User App v2.7.0 and will be updated and added to gradually.
How to Run the App:
Flutter and Dart Versions:
Ensure that your development environment is using Flutter version 3.13.2 and Dart version 3.1.0. Confirm that the versions installed on your PC align with those specified in the documentation.
Back End Compatibility:
The project is designed to be compatible with Back End version 1.4.0. Ensure that your Back-End environment is set to this version.
GraphQL Schema Update:
If there are changes to the GraphQL schema on the Back End side:
Delete the existing schema:
lib/infrastructure/scheme.graphql
Fetch a new schema by running:
get-graphql-schema ENDPOINT_URL > lib/infrastructure/schema.graphql
Ensure that the endpoint URL is in full format (e.g.,
https://${ENDPOINT_URL}/graphql
).
Run Build Process:
Execute the following commands:
flutter packages pub run build_runner build --delete-conflicting-outputs flutter pub get
Launch the App:
To start the app, run the following command:
flutter run -d chrome --web-browser-flag "--disable-web-security"
By following these steps, you ensure that your development environment is properly configured, and the app is launched with the necessary dependencies and configurations.
App Structure
Introduction:
The application is meticulously crafted based on the robust principles of Domain-Driven Design (DDD) architecture. Our commitment to this architectural paradigm ensures a seamless integration of beautiful features, effortless navigation, and efficient testing capabilities.
Architecture Overview:
The architecture is thoughtfully divided into distinct layers, each serving a specific purpose. This structured approach not only enhances the clarity of the codebase but also facilitates a more organized and maintainable development process. The diagram below illustrates the clear division of layers and their respective roles in the overall architecture.
State Management:
In this project, we leverage the power of Riverpod alongside Flutter Hooks to handle state management. This dynamic combination empowers us to effortlessly access data, even within the complexity of a large-scale project. The integration of hooks has significantly reduced our reliance on Stateful widgets, streamlining our codebase and enhancing overall project efficiency. By adopting Riverpod and Flutter Hooks for state management, we ensure a responsive and scalable application that meets the demands of a sophisticated architecture. This strategic choice not only simplifies data access but also contributes to a more enjoyable and efficient development experience.
Application Structure:
At the Application level, we centralize providers and actions to streamline the management of web, mobile, and tablet applications within a single project. This unified approach ensures that all events are consolidated and stored in the 'actions' folder. This design choice serves two key purposes:
Efficiency and Maintainability:
By housing all events in a centralized 'actions' folder, we minimize the effort required to support and maintain the project. This unified structure promotes a cohesive codebase that is easier to manage and enhances overall project efficiency.
Cross-Device Consistency:
Given that our project encompasses web, mobile, and tablet applications, consolidating events at the Application level guarantees consistent execution across devices of different types and operating systems (OS). This uniformity simplifies development and testing processes, ensuring a seamless experience for end-users across various platforms.
In essence, this architectural decision reflects our commitment to a streamlined and consistent development approach, promoting ease of maintenance and a unified user experience across diverse devices and operating systems.
Infrastructure Layer:
The Infrastructure layer plays a pivotal role in our application, encompassing both the Repository and GraphQL components.
Repository Layer:
The Repository layer serves as the bridge between the Domain and Data Mapping layers. Its primary responsibilities include implementing the repository interface. By introducing this layer, we strategically isolate domain objects from the intricate details of the database access code. This not only enhances code organization but also minimizes the scattering and duplication of query code. Within this layer, we adopt a feature-oriented approach, implementing interfaces within distinct services.GraphQL Integration:
Our application interfaces with a GraphQL Back End, and this integration is facilitated through the utilization of the ferry library. This library streamlines the interaction with the GraphQL Back End by following a straightforward process:
Â
Schema Retrieval:
Developers download the GraphQL schema (schema.graphql), providing a foundational blueprint for data interactions.
Request File Creation:
Developers create request files (*.graphql files) that encapsulate specific data requests and operations.
Code Generation:
Running build_runner generates additional files, incorporating the specified GraphQL schema and request files. This automated process ensures that the application is equipped with the necessary components for efficient GraphQL interactions.
For a more comprehensive understanding of the ferry library, developers can refer to the official ferry page. In essence, this integration streamlines GraphQL communication, contributing to a robust and efficient data interaction mechanism within our application.
Presentation
In the Flutter Presentation layer, the focus is on widgets, catering to the diverse layouts required for different platforms within our extensive project. To enhance organization and ease of development, we've implemented a structured widget division scheme:
Widget Division Structure:
Components:
Elements specific to the 'Auth' module, serve common purposes within authentication. If any of these components find utility beyond 'Auth,' they're moved to a higher level for broader application.
Web:
A designated UI kit tailored for the Web layout, ensuring platform-specific optimization.
Mobile:
A dedicated UI kit catering to the nuances of the mobile layout, providing an optimized user experience on handheld devices.
Overall Project Structure:
The entire project is strategically distributed across key modules:
App:
The central module orchestrates the overall application structure.
Components:
Centralizing elements essential across the entire project, promoting code reuse and consistency.
Constants:
Housing paths to assets and color definitions for consistent theming.
Helpers:
Storing various utilities such as loaders, error handling mechanisms, and form validation tools.
Controller:
Tasked with user authorization checks and global navigation orchestration.
This meticulous organization within the Presentation layer underscores our commitment to a scalable, maintainable, and platform-aware Flutter project structure, optimizing development workflows and ensuring a cohesive user interface across various platforms.
Local Data Storage with Hive:
In our project, we leverage the power of Hive to efficiently store various data on the user's device. The primary role of the local database is to safeguard critical information, as exemplified by the storage of configuration data in https://{ENDPOINT_URL}/static/config.json.
Centralized Database:
Hive serves as the central repository for storing essential data locally on the user's device. This includes critical configurations encapsulated in https://{ENDPOINT_URL}/static/config.json
Mitigating State Loss:
The use of Hive boxes is a strategic choice to mitigate the risk of data loss due to state loss, a challenge occasionally encountered in Flutter web applications. By persistently storing data in Hive boxes, we ensure a reliable and resilient data management system.
Box Registration in main.dart:
All relevant boxes are registered and initialized in the main.dart file when Hive adapters are set up. This centralized registration ensures that the application is ready to interact with the local database across its various components.
By adhering to this structured approach, we optimize data storage, mitigate Flutter web-specific challenges, and establish a reliable foundation for handling local data within our project. Hive, as a lightweight and efficient NoSQL database, contributes to a seamless and responsive user experience across different platforms.
Assets Management:
Fonts:
The application relies on locally stored fonts located in assets/fonts.
Caution is advised when making changes to fonts; always update or add entries in pubspec.yaml
under the fonts section.
WARNING: When modifying fonts, ensure to replace or add entries in pubspec.yaml
under the fonts section to reflect the changes accurately.
Icons/Images:
All graphic assets, including *.png, *.jpg, *.svg, etc., are centralized in assets/icons
. To maintain a systematic approach, each asset's path is recorded in constant variables within lib/presentation/constants/assets_path.dart.
WARNING: When adding or removing assets, it's crucial to update assets_path.dart
with the appropriate entries. Avoid directly referencing the full path to the asset within the widget.
By adhering to these asset management guidelines, we establish a robust system for handling fonts, icons, and images. This practice not only ensures a well-organized and maintainable project structure but also streamlines the asset update process, minimizing the risk of errors and enhancing overall development efficiency.
Localization Setup:
For efficient localization, our application utilizes the easy_localization
library. The localization JSON files are stored in assets/lang
. To introduce a new locale, follow these steps:
Create a New JSON File:
Generate a new JSON file for the desired locale in the
assets/lang
directory. Ensure it contains the necessary translations for the application.
Update Supported Locales:
In
lib/presentation/helpers/capitalize.dart
, locate thesupportedLocales
list.Add the new locale to this list to declare support for the recently added JSON file.
Example:
// lib/presentation/helpers/capitalize.dart
List<Locale> supportedLocales = [
Locale('en', 'US'), // Example: English (United States)
Locale('es', 'ES'), // Example: Spanish (Spain)
// Add your new locale below:
Locale('fr', 'FR'), // Example: French (France)
];
Additional Considerations:
Ensure that the translations in the new JSON file cover all necessary elements within the application.
Follow a systematic approach to maintain consistency across different locales.
By adhering to these steps, you ensure seamless integration of new locales into the application's localization system. The use of easy_localization
simplifies the process, providing a user-friendly and efficient mechanism for supporting multiple languages within the project.
Server Deployment Configuration: https://{ENDPOINT_URL}/static/config.json
Â
Â
When deploying to the server, the DevOps team must specify the values in the configuration file. This strategic approach empowers the team to make configuration adjustments without necessitating a rebuild of the application.
Key Benefits:
Flexibility:
By allowing DevOps to input configuration values in the file, the deployment process gains flexibility. Changes can be seamlessly implemented without requiring developers to rebuild the application.
Dynamic Configuration:
This approach enables dynamic configuration updates on the server side, facilitating rapid adjustments to various parameters based on operational needs.
Procedure:
Config File Modification:
The designated configuration file serves as the repository for key parameters. DevOps can modify these values directly in the file.
Server Restart:
After updating the configuration file, a server restart ensures that the changes take effect promptly without the need for a time-consuming application rebuild.Customisation.md
All customization and changes that differentiate the custom version of the product from the master branch are listed here.
build.yaml
This configuration file so that build_runner knows which generators to run and where to find our GraphQL schema (watch ferry_generator).
analysis_options.yaml
Configuration file for linter. We are constantly working on the quality of the code, so we are constantly adding new rules to make the code more qualitative and productive.
.gitlab-ci.yml
Setting up CI/CD for GitLab.
Dockerfile
Configuration file for building a docker image. IMPORTANT! If a different version of Flutter will be used for development, you need to replace it here as well, so that there are no problems during deployment.
.gitignore
A list of files that should not be stored in the repository.