GeniusArchitecture
This is a boilerplate project created in flutter using Provider, Firebase, Dio, and some fundamentals of Robert C Martin’s Clean Architecture.
Getting Started
The boilerplate is a minimal implementation that can be used to create a new project or library. It comes with a variety of basic components such as an app architecture, a theme, and necessary dependencies. The repository code can be used as an initializer to quickly build new projects and reduce the time it takes to develop them.
How to Use
Step 1:
Download or clone this repo by using the link below:
https://github.com/GeniusCrew-B-V/genius-architecture-boilerplate.git
Step 2:
Go to project root and execute the following command in console to get the required dependencies:
flutter pub get
Step 3: Change the package name by just doing a project wide search and replacing “com.baseprojectsrl.baseproject” to the one you desire
Step 4:
This project uses code generation for domain/network models and app localization, execute the following command to generate files:
flutter packages pub run build_runner build --delete-conflicting-outputs
or watch command in order to keep the source code synced automatically:
flutter packages pub run build_runner watch
Step 5:
Go to Firebase, create a new project, configure it and place the GoogleServices-Info.plist and google-services.json in the correct positions, if you skip this step the app will not run
Hide Generated Files
In-order to hide generated files, navigate to Android Studio
-> Preferences
-> Editor
-> File Types
and paste the below lines under ignore files and folders
section:
*.inject.summary;*.inject.dart;*.g.dart;
In Visual Studio Code, navigate to Preferences
-> Settings
and search for Files:Exclude
. Add the following patterns:
**/*.inject.summary
**/*.inject.dart
**/*.g.dart
Boilerplate Features:
- Splash
- Lazy loaded lists
- Login
- Home
- Routing
- Theme
- Dio
- Database
- Provider (State Management)
- Validation
- Code Generation
- Dependency Injection
- Dark Theme Support
- Multilingual Support
Folder Structure
Here is the folder structure we have been using in this project
lib/
|- dev/
|- main.dart
|- i18n/
|- prod/
|- main.dart
|- resources/
|- src/
|- base
|- home_page
|- login
|- post_detail
|- profile_page
|- signup
|- start
|- updates
|- app.dart
Now, lets dive into the lib folder which has the main code for the application.
1- dev - This is where the starting point of the application in case we want to work in a hipotetycal
development enviroment
2- i18n - Here we have all the .yaml files for each localization that we would need
3- prod - This is where the starting point of the application in case we want to work in a hipotetycal
production enviroment
4- resources - Here we have all the possible resources needed (Sizes,Image references etc.)
5- src - This is where we develop the app itself
1- base - Base feature is where we manage the whole app which isn't specifically feature-related, and
instead it's app related. It contains all the non-feature releated data
2- home_page - Home page feature
3- login - Login page feature
4- post_detail - Post detail page feature
5- profile_page - Profile page feature
6- signup - Signup page feature
7- start - Start feature is the first feature always launcher, it cointains basic navigation and auth
capabilities
8- updates- Updates page feature
9- app.dart - This is the real starting point of the application, where we inject all the
dependencies and run the app
Feature structure
Each feature follow the main principles of the Clean Architecture paths.
feature/
|- data
|- di
|- domain
|- ui
Data
The Data layer consists of data source code such as Rest APIs, Access to local/remote databases, or any other data source. Here we follow a system of abstract classes in order to follow the signature of the methods defined in the Domain layer. This will complicate a bit the things, but allows us to have a robust code and will help also in the test/debugs faces.
data/
|- source/
|- feature_remote_data_source.dart
|- feature_remote_data_source_impl.dart
|- feature_repository_impl.dart
Di (Dependency injection)
Here is where we make a class independent of its own dependencies. This will enable us to separate different parts of the app in a more manageable way because each class can make calls to any dependency, it needs at any given time.
di/
|- feature_providers.dart
Domain
The Domain folder contains the models which help draw and structure our data and the signature for the functions implemented in the data layer. This basically makes the domain layer a kind of ‘glue’ between Data and the UI/logic.
di/
|- model/
|- request_model.dart
|- response_model.dart
|- feature_repository.dart
Ui
In the UI Folder we manage Pages/ Navigation/ Widgets/ State Management (ViewModel).
-
The Pages consist of the pages included in the feature we are working on. Which is built from smaller widgets regarding this feature.
-
Navigator is where we add the logic of the navigation within that feature which allows us to move through the pages.
-
The ViewModel is where we manage the state of these pages and the whole feature and the business logic of course.
ui/
|- model
|- navigator
|- pages
|- viewmodel
|- widget
App.dart
This is the starting point of the application. All the application level configurations are defined in this file i.e, theme, title, orientation etc. The behavior of the app such as flavour, be endpoint etc. can be configured by using the class AppConfig. In this case we use dev/main.dart and prod/main.dart for handling such things.
dev/main.dart
import 'package:flutter/material.dart';
import '../src/app.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
mainCommon(
host: "https://31cae3b4-9771-4151-bdb2-41437d3b17ec.mock.pstmn.io",
isProd: false,
);
}
app.dart
import 'dart:io';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_displaymode/flutter_displaymode.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../resources/app_config.dart';
import 'base/settings/di/theme_providers.dart';
import 'base/settings/ui/viewmodel/theme_viewmodel.dart';
import 'base/token/di/token_providers.dart';
import 'base/widget/ui/custom_circular_progress_indicator.dart';
import 'start/di/start_page_providers.dart';
import 'start/ui/navigator/start_page_navigator.dart';
void mainCommon({required String host, required bool isProd, required String onesignalAppID}) async {
await WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
if(Platform.isAndroid) {
await FlutterDisplayMode.setHighRefreshRate();
}
final prefs = await SharedPreferences.getInstance();
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]).then(
(value) => runApp(
MultiProvider(
providers: themePageProviders(prefs),
builder: (context, __) {
final themeViewModel = context.watch<ThemeViewModel>();
return App(
host: host,
isProd: isProd,
themeViewModel: themeViewModel,
);
},
),
),
);
}
GeniusCrew B.V
Van Coothplein 53
4811 ND, Breda
The Netherlands