feat: add apps folder
This commit is contained in:
parent
97b70d065a
commit
3a79bcde7c
47
apps/wyatt_clean_code/.gitignore
vendored
Normal file
47
apps/wyatt_clean_code/.gitignore
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
**/doc/api/
|
||||||
|
**/ios/Flutter/.last_build_id
|
||||||
|
.dart_tool/
|
||||||
|
.flutter-plugins
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
.packages
|
||||||
|
.pub-cache/
|
||||||
|
.pub/
|
||||||
|
/build/
|
||||||
|
|
||||||
|
# Web related
|
||||||
|
lib/generated_plugin_registrant.dart
|
||||||
|
|
||||||
|
# Symbolication related
|
||||||
|
app.*.symbols
|
||||||
|
|
||||||
|
# Obfuscation related
|
||||||
|
app.*.map.json
|
||||||
|
|
||||||
|
# Android Studio will place build artifacts here
|
||||||
|
/android/app/debug
|
||||||
|
/android/app/profile
|
||||||
|
/android/app/release
|
30
apps/wyatt_clean_code/.metadata
Normal file
30
apps/wyatt_clean_code/.metadata
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
||||||
|
channel: stable
|
||||||
|
|
||||||
|
project_type: app
|
||||||
|
|
||||||
|
# Tracks metadata for the flutter migrate command
|
||||||
|
migration:
|
||||||
|
platforms:
|
||||||
|
- platform: root
|
||||||
|
create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
||||||
|
base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
||||||
|
- platform: android
|
||||||
|
create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
||||||
|
base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
||||||
|
|
||||||
|
# User provided section
|
||||||
|
|
||||||
|
# List of Local paths (relative to this file) that should be
|
||||||
|
# ignored by the migrate tool.
|
||||||
|
#
|
||||||
|
# Files that are not part of the templates will be ignored by default.
|
||||||
|
unmanaged_files:
|
||||||
|
- 'lib/main.dart'
|
||||||
|
- 'ios/Runner.xcodeproj/project.pbxproj'
|
125
apps/wyatt_clean_code/.vscode/launch.json
vendored
Normal file
125
apps/wyatt_clean_code/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Launch development",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"program": "lib/main_development.dart",
|
||||||
|
"args": [
|
||||||
|
"--flavor",
|
||||||
|
"development",
|
||||||
|
"--target",
|
||||||
|
"lib/main_development.dart"
|
||||||
|
],
|
||||||
|
"flutterMode": "debug"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Launch development in profile mode",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"program": "lib/main_development.dart",
|
||||||
|
"args": [
|
||||||
|
"--flavor",
|
||||||
|
"development",
|
||||||
|
"--target",
|
||||||
|
"lib/main_development.dart"
|
||||||
|
],
|
||||||
|
"flutterMode": "profile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Launch development in release mode",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"program": "lib/main_development.dart",
|
||||||
|
"args": [
|
||||||
|
"--flavor",
|
||||||
|
"development",
|
||||||
|
"--target",
|
||||||
|
"lib/main_development.dart"
|
||||||
|
],
|
||||||
|
"flutterMode": "release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Launch staging",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"program": "lib/main_staging.dart",
|
||||||
|
"args": [
|
||||||
|
"--flavor",
|
||||||
|
"staging",
|
||||||
|
"--target",
|
||||||
|
"lib/main_staging.dart"
|
||||||
|
],
|
||||||
|
"flutterMode": "debug"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Launch staging in profile mode",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"program": "lib/main_staging.dart",
|
||||||
|
"args": [
|
||||||
|
"--flavor",
|
||||||
|
"staging",
|
||||||
|
"--target",
|
||||||
|
"lib/main_staging.dart"
|
||||||
|
],
|
||||||
|
"flutterMode": "profile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Launch staging in release mode",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"program": "lib/main_staging.dart",
|
||||||
|
"args": [
|
||||||
|
"--flavor",
|
||||||
|
"staging",
|
||||||
|
"--target",
|
||||||
|
"lib/main_staging.dart"
|
||||||
|
],
|
||||||
|
"flutterMode": "release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Launch production",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"program": "lib/main_production.dart",
|
||||||
|
"args": [
|
||||||
|
"--flavor",
|
||||||
|
"production",
|
||||||
|
"--target",
|
||||||
|
"lib/main_production.dart"
|
||||||
|
],
|
||||||
|
"flutterMode": "debug"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Launch production in profile mode",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"program": "lib/main_production.dart",
|
||||||
|
"args": [
|
||||||
|
"--flavor",
|
||||||
|
"production",
|
||||||
|
"--target",
|
||||||
|
"lib/main_production.dart"
|
||||||
|
],
|
||||||
|
"flutterMode": "profile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Launch production in release mode",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"program": "lib/main_production.dart",
|
||||||
|
"args": [
|
||||||
|
"--flavor",
|
||||||
|
"production",
|
||||||
|
"--target",
|
||||||
|
"lib/main_production.dart"
|
||||||
|
],
|
||||||
|
"flutterMode": "release"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
15
apps/wyatt_clean_code/.vscode/settings.json
vendored
Normal file
15
apps/wyatt_clean_code/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"dart.flutterSdkPath": ".fvm/flutter_sdk",
|
||||||
|
"bloc.newCubitTemplate.type": "equatable",
|
||||||
|
"psi-header.config": {
|
||||||
|
"blankLinesAfter": 0,
|
||||||
|
"forceToTop": true,
|
||||||
|
},
|
||||||
|
"psi-header.templates": [
|
||||||
|
{
|
||||||
|
"language": "*",
|
||||||
|
"template": [],
|
||||||
|
// disabled,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
54
apps/wyatt_clean_code/Makefile
Normal file
54
apps/wyatt_clean_code/Makefile
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
.PHONY: help clean get upgrade format lint gen watch run-dev run-stg run-prod
|
||||||
|
|
||||||
|
# Adding a help file: https://gist.github.com/prwhite/8168133#gistcomment-1313022
|
||||||
|
help: ## This help dialog.
|
||||||
|
@IFS=$$'\n' ; \
|
||||||
|
help_lines=(`fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//'`); \
|
||||||
|
for help_line in $${help_lines[@]}; do \
|
||||||
|
IFS=$$'#' ; \
|
||||||
|
help_split=($$help_line) ; \
|
||||||
|
help_command=`echo $${help_split[0]} | sed -e 's/^ *//' -e 's/ *$$//'` ; \
|
||||||
|
help_info=`echo $${help_split[2]} | sed -e 's/^ *//' -e 's/ *$$//'` ; \
|
||||||
|
printf "%-30s %s\n" $$help_command $$help_info ; \
|
||||||
|
done
|
||||||
|
|
||||||
|
clean: ## Cleans the environment.
|
||||||
|
@echo "• Cleaning the project..."
|
||||||
|
@rm -rf pubspec.lock
|
||||||
|
@flutter clean
|
||||||
|
|
||||||
|
get: ## Gets the dependencies.
|
||||||
|
@echo "• Getting the dependencies..."
|
||||||
|
@flutter pub get
|
||||||
|
|
||||||
|
upgrade: clean ## Upgrades dependencies.
|
||||||
|
@echo "• Upgrading dependencies..."
|
||||||
|
@flutter pub upgrade
|
||||||
|
|
||||||
|
format: ## Formats the code.
|
||||||
|
@echo "• Formatting the code"
|
||||||
|
@dart format . --fix
|
||||||
|
|
||||||
|
lint: ## Lints the code.
|
||||||
|
@echo "• Verifying code..."
|
||||||
|
@dart analyze . || (echo "Error in project"; exit 1)
|
||||||
|
|
||||||
|
gen: get ## Run build_runner build (Freezed, Fluttergen, Hive etc...)
|
||||||
|
@echo "• build_runner build"
|
||||||
|
@flutter pub run build_runner build
|
||||||
|
|
||||||
|
watch: get ## Run build_runner watch (Freezed, Fluttergen, Hive etc...)
|
||||||
|
@echo "• build_runner watch"
|
||||||
|
@flutter pub run build_runner watch
|
||||||
|
|
||||||
|
run-dev: ## Run app in development mode
|
||||||
|
@echo "• Running the app (development)"
|
||||||
|
@flutter run --flavor development --target lib/main_development.dart
|
||||||
|
|
||||||
|
run-stg: ## Run app in staging mode
|
||||||
|
@echo "• Running the app (staging)"
|
||||||
|
@flutter run --flavor staging --target lib/main_staging.dart
|
||||||
|
|
||||||
|
run-prod: ## Run app in production mode
|
||||||
|
@echo "• Running the app (production)"
|
||||||
|
@flutter run --flavor production --target lib/main_production.dart
|
16
apps/wyatt_clean_code/README.md
Normal file
16
apps/wyatt_clean_code/README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# wyatt_clean_code
|
||||||
|
|
||||||
|
A new Flutter project.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
This project is a starting point for a Flutter application.
|
||||||
|
|
||||||
|
A few resources to get you started if this is your first Flutter project:
|
||||||
|
|
||||||
|
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
||||||
|
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
||||||
|
|
||||||
|
For help getting started with Flutter development, view the
|
||||||
|
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
||||||
|
samples, guidance on mobile development, and a full API reference.
|
35
apps/wyatt_clean_code/analysis_options.yaml
Normal file
35
apps/wyatt_clean_code/analysis_options.yaml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# This file configures the analyzer, which statically analyzes Dart code to
|
||||||
|
# check for errors, warnings, and lints.
|
||||||
|
#
|
||||||
|
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||||
|
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||||
|
# invoked from the command line by running `flutter analyze`.
|
||||||
|
|
||||||
|
# The following line activates a set of recommended lints for Flutter by
|
||||||
|
# Wyatt Studio, for apps packages, and plugins designed to
|
||||||
|
# encourage good coding practices.
|
||||||
|
include: package:wyatt_analysis/analysis_options.flutter.yaml
|
||||||
|
|
||||||
|
analyzer:
|
||||||
|
exclude:
|
||||||
|
- '**/*.g.dart'
|
||||||
|
- '**/*.freezed.dart'
|
||||||
|
|
||||||
|
linter:
|
||||||
|
# The lint rules applied to this project can be customized in the
|
||||||
|
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||||
|
# included above or to enable additional rules. A list of all available lints
|
||||||
|
# and their documentation is published at
|
||||||
|
# https://dart-lang.github.io/linter/lints/index.html.
|
||||||
|
#
|
||||||
|
# Instead of disabling a lint rule for the entire project in the
|
||||||
|
# section below, it can also be suppressed for a single line of code
|
||||||
|
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||||
|
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||||
|
# producing the lint.
|
||||||
|
rules:
|
||||||
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
13
apps/wyatt_clean_code/android/.gitignore
vendored
Normal file
13
apps/wyatt_clean_code/android/.gitignore
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
gradle-wrapper.jar
|
||||||
|
/.gradle
|
||||||
|
/captures/
|
||||||
|
/gradlew
|
||||||
|
/gradlew.bat
|
||||||
|
/local.properties
|
||||||
|
GeneratedPluginRegistrant.java
|
||||||
|
|
||||||
|
# Remember to never publicly share your keystore.
|
||||||
|
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
|
||||||
|
key.properties
|
||||||
|
**/*.keystore
|
||||||
|
**/*.jks
|
117
apps/wyatt_clean_code/android/app/build.gradle
Normal file
117
apps/wyatt_clean_code/android/app/build.gradle
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
def localProperties = new Properties()
|
||||||
|
def localPropertiesFile = rootProject.file('local.properties')
|
||||||
|
if (localPropertiesFile.exists()) {
|
||||||
|
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||||
|
localProperties.load(reader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||||
|
if (flutterRoot == null) {
|
||||||
|
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||||
|
if (flutterVersionCode == null) {
|
||||||
|
flutterVersionCode = '1'
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||||
|
if (flutterVersionName == null) {
|
||||||
|
flutterVersionName = '1.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
def keystoreProperties = new Properties()
|
||||||
|
def keystorePropertiesFile = rootProject.file('key.properties')
|
||||||
|
if (keystorePropertiesFile.exists()) {
|
||||||
|
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion flutter.compileSdkVersion
|
||||||
|
ndkVersion flutter.ndkVersion
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = '1.8'
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main.java.srcDirs += 'src/main/kotlin'
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
|
applicationId "com.example.wyatt_clean_code"
|
||||||
|
// You can update the following values to match your application needs.
|
||||||
|
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
|
||||||
|
minSdkVersion flutter.minSdkVersion
|
||||||
|
targetSdkVersion flutter.targetSdkVersion
|
||||||
|
versionCode flutterVersionCode.toInteger()
|
||||||
|
versionName flutterVersionName
|
||||||
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
if (System.getenv("ANDROID_KEYSTORE_PATH")) {
|
||||||
|
release {
|
||||||
|
storeFile file(System.getenv("ANDROID_KEYSTORE_PATH"))
|
||||||
|
keyAlias System.getenv("ANDROID_KEYSTORE_ALIAS")
|
||||||
|
keyPassword System.getenv("ANDROID_KEYSTORE_PRIVATE_KEY_PASSWORD")
|
||||||
|
storePassword System.getenv("ANDROID_KEYSTORE_PASSWORD")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
release {
|
||||||
|
keyAlias keystoreProperties['keyAlias']
|
||||||
|
keyPassword keystoreProperties['keyPassword']
|
||||||
|
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
|
||||||
|
storePassword keystoreProperties['storePassword']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flavorDimensions "default"
|
||||||
|
productFlavors {
|
||||||
|
production {
|
||||||
|
dimension "default"
|
||||||
|
applicationIdSuffix ""
|
||||||
|
manifestPlaceholders = [appName: "Wyatt Demo"]
|
||||||
|
}
|
||||||
|
staging {
|
||||||
|
dimension "default"
|
||||||
|
applicationIdSuffix ".stg"
|
||||||
|
manifestPlaceholders = [appName: "[STG] Wyatt Demo"]
|
||||||
|
}
|
||||||
|
development {
|
||||||
|
dimension "default"
|
||||||
|
applicationIdSuffix ".dev"
|
||||||
|
manifestPlaceholders = [appName: "[DEV] Wyatt Demo"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
signingConfig signingConfigs.release
|
||||||
|
minifyEnabled true
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt')
|
||||||
|
}
|
||||||
|
debug {
|
||||||
|
signingConfig signingConfigs.debug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flutter {
|
||||||
|
source '../..'
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.example.wyatt_clean_code">
|
||||||
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
|
the Flutter tool needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
@ -0,0 +1,34 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.example.wyatt_clean_code">
|
||||||
|
<application
|
||||||
|
android:label="wyatt_clean_code"
|
||||||
|
android:name="${applicationName}"
|
||||||
|
android:icon="@mipmap/ic_launcher">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:theme="@style/LaunchTheme"
|
||||||
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:windowSoftInputMode="adjustResize">
|
||||||
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
|
the Android process has started. This theme is visible to the user
|
||||||
|
while the Flutter UI initializes. After that, this theme continues
|
||||||
|
to determine the Window background behind the Flutter UI. -->
|
||||||
|
<meta-data
|
||||||
|
android:name="io.flutter.embedding.android.NormalTheme"
|
||||||
|
android:resource="@style/NormalTheme"
|
||||||
|
/>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<!-- Don't delete the meta-data below.
|
||||||
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
|
<meta-data
|
||||||
|
android:name="flutterEmbedding"
|
||||||
|
android:value="2" />
|
||||||
|
</application>
|
||||||
|
</manifest>
|
@ -0,0 +1,6 @@
|
|||||||
|
package com.example.wyatt_clean_code
|
||||||
|
|
||||||
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
|
||||||
|
class MainActivity: FlutterActivity() {
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Modify this file to customize your launch splash screen -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="?android:colorBackground" />
|
||||||
|
|
||||||
|
<!-- You can insert your own image assets here -->
|
||||||
|
<!-- <item>
|
||||||
|
<bitmap
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@mipmap/launch_image" />
|
||||||
|
</item> -->
|
||||||
|
</layer-list>
|
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Modify this file to customize your launch splash screen -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@android:color/white" />
|
||||||
|
|
||||||
|
<!-- You can insert your own image assets here -->
|
||||||
|
<!-- <item>
|
||||||
|
<bitmap
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@mipmap/launch_image" />
|
||||||
|
</item> -->
|
||||||
|
</layer-list>
|
Binary file not shown.
After Width: | Height: | Size: 544 B |
Binary file not shown.
After Width: | Height: | Size: 442 B |
Binary file not shown.
After Width: | Height: | Size: 721 B |
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
|
the Flutter engine draws its first frame -->
|
||||||
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
|
the Flutter engine draws its first frame -->
|
||||||
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
@ -0,0 +1,8 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.example.wyatt_clean_code">
|
||||||
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
|
the Flutter tool needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
31
apps/wyatt_clean_code/android/build.gradle
Normal file
31
apps/wyatt_clean_code/android/build.gradle
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
buildscript {
|
||||||
|
ext.kotlin_version = '1.6.10'
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:7.1.2'
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.buildDir = '../build'
|
||||||
|
subprojects {
|
||||||
|
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||||
|
}
|
||||||
|
subprojects {
|
||||||
|
project.evaluationDependsOn(':app')
|
||||||
|
}
|
||||||
|
|
||||||
|
task clean(type: Delete) {
|
||||||
|
delete rootProject.buildDir
|
||||||
|
}
|
3
apps/wyatt_clean_code/android/gradle.properties
Normal file
3
apps/wyatt_clean_code/android/gradle.properties
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
org.gradle.jvmargs=-Xmx1536M
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=true
|
6
apps/wyatt_clean_code/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
apps/wyatt_clean_code/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#Fri Jun 23 08:50:38 CEST 2017
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
|
11
apps/wyatt_clean_code/android/settings.gradle
Normal file
11
apps/wyatt_clean_code/android/settings.gradle
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
include ':app'
|
||||||
|
|
||||||
|
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
|
||||||
|
def properties = new Properties()
|
||||||
|
|
||||||
|
assert localPropertiesFile.exists()
|
||||||
|
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
|
||||||
|
|
||||||
|
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||||
|
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||||
|
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
|
61
apps/wyatt_clean_code/assets/colors.xml
Normal file
61
apps/wyatt_clean_code/assets/colors.xml
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="seedColor" type="material material-accent">#FF2196F3</color>
|
||||||
|
|
||||||
|
<color name="lightPrimary">#FF0061A6</color>
|
||||||
|
<color name="lightOnPrimary">#FFFFFFFF</color>
|
||||||
|
<color name="lightPrimaryContainer">#FFD0E4FF</color>
|
||||||
|
<color name="lightOnPrimaryContainer">#FF001D36</color>
|
||||||
|
|
||||||
|
<color name="lightSecondary">#FF535F70</color>
|
||||||
|
<color name="lightOnSecondary">#FFFFFFFF</color>
|
||||||
|
<color name="lightSecondaryContainer">#FFD6E3F7</color>
|
||||||
|
<color name="lightOnSecondaryContainer">#FF101C2B</color>
|
||||||
|
|
||||||
|
<color name="lightError">#FFBA1B1B</color>
|
||||||
|
<color name="lightOnError">#FFFFFFFF</color>
|
||||||
|
<color name="lightErrorContainer">#FFFFDAD4</color>
|
||||||
|
<color name="lightOnErrorContainer">#FF410001</color>
|
||||||
|
|
||||||
|
<color name="lightBackground">#FFFDFCFF</color>
|
||||||
|
<color name="lightOnBackground">#FF1B1B1B</color>
|
||||||
|
<color name="lightSurface">#FFFDFCFF</color>
|
||||||
|
<color name="lightOnSurface">#FF1B1B1B</color>
|
||||||
|
<color name="lightSurfaceVariant">#FFDFE2EB</color>
|
||||||
|
<color name="lightOnSurfaceVariant">#FF42474E</color>
|
||||||
|
<color name="lightOutline">#FF73777F</color>
|
||||||
|
<color name="lightShadow">#FF000000</color>
|
||||||
|
|
||||||
|
<color name="lightInverseSurface">#FF2F3033</color>
|
||||||
|
<color name="lightOnInverseSurface">#FFF1F0F4</color>
|
||||||
|
<color name="lightInversePrimary">#FF9CCAFF</color>
|
||||||
|
|
||||||
|
|
||||||
|
<color name="darkPrimary">#FF9CCAFF</color>
|
||||||
|
<color name="darkOnPrimary">#FF00325A</color>
|
||||||
|
<color name="darkPrimaryContainer">#FF00497F</color>
|
||||||
|
<color name="darkOnPrimaryContainer">#FFD0E4FF</color>
|
||||||
|
|
||||||
|
<color name="darkSecondary">#FFBBC8DB</color>
|
||||||
|
<color name="darkOnSecondary">#FF253140</color>
|
||||||
|
<color name="darkSecondaryContainer">#FF3C4858</color>
|
||||||
|
<color name="darkOnSecondaryContainer">#FFD6E3F7</color>
|
||||||
|
|
||||||
|
<color name="darkError">#FFFFB4A9</color>
|
||||||
|
<color name="darkOnError">#FF680003</color>
|
||||||
|
<color name="darkErrorContainer">#FF930006</color>
|
||||||
|
<color name="darkOnErrorContainer">#FFFFB4A9</color>
|
||||||
|
|
||||||
|
<color name="darkBackground">#FF1B1B1B</color>
|
||||||
|
<color name="darkOnBackground">#FFE2E2E6</color>
|
||||||
|
<color name="darkSurface">#FF1B1B1B</color>
|
||||||
|
<color name="darkOnSurface">#FFE2E2E6</color>
|
||||||
|
<color name="darkSurfaceVariant">#FF42474E</color>
|
||||||
|
<color name="darkOnSurfaceVariant">#FFC3C7D0</color>
|
||||||
|
<color name="darkOutline">#FF8D9199</color>
|
||||||
|
<color name="darkShadow">#FF000000</color>
|
||||||
|
|
||||||
|
<color name="darkInverseSurface">#FFE2E2E6</color>
|
||||||
|
<color name="darkOnInverseSurface">#FF2F3033</color>
|
||||||
|
<color name="darkInversePrimary">#FF0061A6</color>
|
||||||
|
</resources>
|
20
apps/wyatt_clean_code/assets/l10n/intl_fr.arb
Normal file
20
apps/wyatt_clean_code/assets/l10n/intl_fr.arb
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"@@locale": "fr",
|
||||||
|
"counterAppBarTitle": "Compteur",
|
||||||
|
"@counterAppBarTitle": {
|
||||||
|
"description": "Texte affiché dans l'AppBar de la page Compteur"
|
||||||
|
},
|
||||||
|
"youHavePushed": "Vous avez appuyé {count} fois sur le bouton !",
|
||||||
|
"@youHavePushed": {
|
||||||
|
"description": "Message affiché sur la page compteur",
|
||||||
|
"placeholders": {
|
||||||
|
"count": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"goToCounter": "Aller au Compteur",
|
||||||
|
"@goToCounter": {
|
||||||
|
"description": "Texte affiché dans le bouton ammenant vers la page Compteur"
|
||||||
|
}
|
||||||
|
}
|
4
apps/wyatt_clean_code/l10n.yaml
Normal file
4
apps/wyatt_clean_code/l10n.yaml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
arb-dir: assets/l10n
|
||||||
|
template-arb-file: intl_fr.arb
|
||||||
|
output-localization-file: app_localizations.dart
|
||||||
|
nullable-getter: false
|
46
apps/wyatt_clean_code/lib/bootstrap.dart
Normal file
46
apps/wyatt_clean_code/lib/bootstrap.dart
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/dependency_injection/get_it.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/flavors/flavor_settings.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/utils/app_bloc_observer.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/utils/wyatt_printer.dart';
|
||||||
|
|
||||||
|
Future<void> bootstrap(FutureOr<Widget> Function() builder) async {
|
||||||
|
await runZonedGuarded(
|
||||||
|
() async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
FlutterError.onError = (details) {
|
||||||
|
WyattPrinter.get().e(
|
||||||
|
'',
|
||||||
|
details,
|
||||||
|
details.stack,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
FlavorSettings.init();
|
||||||
|
GetItInitializer.run();
|
||||||
|
|
||||||
|
GoRouter.setUrlPathStrategy(UrlPathStrategy.path);
|
||||||
|
|
||||||
|
if (!kReleaseMode) {
|
||||||
|
final env = FlavorSettings.get();
|
||||||
|
WyattPrinter.get().i('Flavor : ${env.flavor.name}');
|
||||||
|
}
|
||||||
|
|
||||||
|
await BlocOverrides.runZoned(
|
||||||
|
() async => runApp(await builder()),
|
||||||
|
blocObserver: AppBlocObserver(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(error, stackTrace) => WyattPrinter.get().e(
|
||||||
|
'',
|
||||||
|
error,
|
||||||
|
stackTrace,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
1
apps/wyatt_clean_code/lib/core/constants/.gitkeep
Normal file
1
apps/wyatt_clean_code/lib/core/constants/.gitkeep
Normal file
@ -0,0 +1 @@
|
|||||||
|
# just to keep empty folder in brick generation
|
@ -0,0 +1,15 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:get_it/get_it.dart';
|
||||||
|
|
||||||
|
final getIt = GetIt.I;
|
||||||
|
|
||||||
|
abstract class GetItInitializer {
|
||||||
|
static Future<void> init() async {
|
||||||
|
// Here, register data sources
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run() {
|
||||||
|
unawaited(init());
|
||||||
|
}
|
||||||
|
}
|
2
apps/wyatt_clean_code/lib/core/design_system/colors.dart
Normal file
2
apps/wyatt_clean_code/lib/core/design_system/colors.dart
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/// Generate colors with `flutter pub run build_runner build`
|
||||||
|
export 'package:wyatt_clean_code/gen/colors.gen.dart';
|
235
apps/wyatt_clean_code/lib/core/design_system/sizing.dart
Normal file
235
apps/wyatt_clean_code/lib/core/design_system/sizing.dart
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
|
||||||
|
/// Geometric progression.
|
||||||
|
abstract class AppSizing {
|
||||||
|
/// Default to 1
|
||||||
|
static const double factor = 1;
|
||||||
|
|
||||||
|
/// SizedBox.shrink();
|
||||||
|
static const SizedBox empty = SizedBox.shrink();
|
||||||
|
|
||||||
|
/// xxs = factor * 2
|
||||||
|
static const double xxs = factor * 2;
|
||||||
|
|
||||||
|
/// xs = factor * 4
|
||||||
|
static const double xs = factor * 4;
|
||||||
|
|
||||||
|
/// s = factor * 8
|
||||||
|
static const double s = factor * 8;
|
||||||
|
|
||||||
|
/// m = factor * 16
|
||||||
|
static const double m = factor * 16;
|
||||||
|
|
||||||
|
/// l = factor * 32
|
||||||
|
static const double l = factor * 32;
|
||||||
|
|
||||||
|
/// xl = factor * 64
|
||||||
|
static const double xl = factor * 64;
|
||||||
|
|
||||||
|
/// xxl = factor * 128
|
||||||
|
static const double xxl = factor * 128;
|
||||||
|
|
||||||
|
/// xxs = factor * 2
|
||||||
|
static const Gap xxsGap = Gap(xxs);
|
||||||
|
|
||||||
|
/// xs = factor * 4
|
||||||
|
static const Gap xsGap = Gap(xs);
|
||||||
|
|
||||||
|
/// s = factor * 8
|
||||||
|
static const Gap sGap = Gap(s);
|
||||||
|
|
||||||
|
/// m = factor * 16
|
||||||
|
static const Gap mGap = Gap(m);
|
||||||
|
|
||||||
|
/// l = factor * 32
|
||||||
|
static const Gap lGap = Gap(l);
|
||||||
|
|
||||||
|
/// xl = factor * 64
|
||||||
|
static const Gap xlGap = Gap(xl);
|
||||||
|
|
||||||
|
/// xxl = factor * 128
|
||||||
|
static const Gap xxlGap = Gap(xxl);
|
||||||
|
|
||||||
|
/// xxs = factor * 2
|
||||||
|
static const Radius xxsRadius = Radius.circular(xxs);
|
||||||
|
|
||||||
|
/// xs = factor * 4
|
||||||
|
static const Radius xsRadius = Radius.circular(xs);
|
||||||
|
|
||||||
|
/// s = factor * 8
|
||||||
|
static const Radius sRadius = Radius.circular(s);
|
||||||
|
|
||||||
|
/// m = factor * 16
|
||||||
|
static const Radius mRadius = Radius.circular(m);
|
||||||
|
|
||||||
|
/// l = factor * 32
|
||||||
|
static const Radius lRadius = Radius.circular(l);
|
||||||
|
|
||||||
|
/// xl = factor * 64
|
||||||
|
static const Radius xlRadius = Radius.circular(xl);
|
||||||
|
|
||||||
|
/// xxl = factor * 128
|
||||||
|
static const Radius xxlRadius = Radius.circular(xxl);
|
||||||
|
|
||||||
|
/// xxs = factor * 2
|
||||||
|
///
|
||||||
|
/// A square inset offers indents content on all four sides.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.all(value)]*
|
||||||
|
static const EdgeInsets xxsSquareInset = EdgeInsets.all(xxs);
|
||||||
|
|
||||||
|
/// xs = factor * 4
|
||||||
|
///
|
||||||
|
/// A square inset offers indents content on all four sides.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.all(value)]*
|
||||||
|
static const EdgeInsets xsSquareInset = EdgeInsets.all(xs);
|
||||||
|
|
||||||
|
/// s = factor * 8
|
||||||
|
///
|
||||||
|
/// A square inset offers indents content on all four sides.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.all(value)]*
|
||||||
|
static const EdgeInsets sSquareInset = EdgeInsets.all(s);
|
||||||
|
|
||||||
|
/// m = factor * 16
|
||||||
|
///
|
||||||
|
/// A square inset offers indents content on all four sides.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.all(value)]*
|
||||||
|
static const EdgeInsets mSquareInset = EdgeInsets.all(m);
|
||||||
|
|
||||||
|
/// l = factor * 32
|
||||||
|
///
|
||||||
|
/// A square inset offers indents content on all four sides.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.all(value)]*
|
||||||
|
static const EdgeInsets lSquareInset = EdgeInsets.all(l);
|
||||||
|
|
||||||
|
/// xl = factor * 64
|
||||||
|
///
|
||||||
|
/// A square inset offers indents content on all four sides.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.all(value)]*
|
||||||
|
static const EdgeInsets xlSquareInset = EdgeInsets.all(xl);
|
||||||
|
|
||||||
|
/// xxl = factor * 128
|
||||||
|
///
|
||||||
|
/// A square inset offers indents content on all four sides.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.all(value)]*
|
||||||
|
static const EdgeInsets xxlSquareInset = EdgeInsets.all(xxl);
|
||||||
|
|
||||||
|
/// xxs = factor * 2
|
||||||
|
///
|
||||||
|
/// A squished inset reduces space top and bottom by 50%.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.symmetric(horizontal: value, vertical: value / 2)]*
|
||||||
|
static const EdgeInsets xxsSquishInset =
|
||||||
|
EdgeInsets.symmetric(horizontal: xxs, vertical: xxs / 2);
|
||||||
|
|
||||||
|
/// xs = factor * 4
|
||||||
|
///
|
||||||
|
/// A squished inset reduces space top and bottom by 50%.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.symmetric(horizontal: value, vertical: value / 2)]*
|
||||||
|
static const EdgeInsets xsSquishInset =
|
||||||
|
EdgeInsets.symmetric(horizontal: xs, vertical: xs / 2);
|
||||||
|
|
||||||
|
/// s = factor * 8
|
||||||
|
///
|
||||||
|
/// A squished inset reduces space top and bottom by 50%.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.symmetric(horizontal: value, vertical: value / 2)]*
|
||||||
|
static const EdgeInsets sSquishInset =
|
||||||
|
EdgeInsets.symmetric(horizontal: s, vertical: s / 2);
|
||||||
|
|
||||||
|
/// m = factor * 16
|
||||||
|
///
|
||||||
|
/// A squished inset reduces space top and bottom by 50%.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.symmetric(horizontal: value, vertical: value / 2)]*
|
||||||
|
static const EdgeInsets mSquishInset =
|
||||||
|
EdgeInsets.symmetric(horizontal: m, vertical: m / 2);
|
||||||
|
|
||||||
|
/// l = factor * 32
|
||||||
|
///
|
||||||
|
/// A squished inset reduces space top and bottom by 50%.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.symmetric(horizontal: value, vertical: value / 2)]*
|
||||||
|
static const EdgeInsets lSquishInset =
|
||||||
|
EdgeInsets.symmetric(horizontal: l, vertical: l / 2);
|
||||||
|
|
||||||
|
/// xl = factor * 64
|
||||||
|
///
|
||||||
|
/// A squished inset reduces space top and bottom by 50%.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.symmetric(horizontal: value, vertical: value / 2)]*
|
||||||
|
static const EdgeInsets xlSquishInset =
|
||||||
|
EdgeInsets.symmetric(horizontal: xl, vertical: xl / 2);
|
||||||
|
|
||||||
|
/// xxl = factor * 128
|
||||||
|
///
|
||||||
|
/// A squished inset reduces space top and bottom by 50%.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.symmetric(horizontal: value, vertical: value / 2)]*
|
||||||
|
static const EdgeInsets xxlSquishInset =
|
||||||
|
EdgeInsets.symmetric(horizontal: xxl, vertical: xxl / 2);
|
||||||
|
|
||||||
|
/// xxs = factor * 2
|
||||||
|
///
|
||||||
|
/// A stretched inset reduces space left and right by 50%.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.symmetric(vertical: value, horizontal: value / 2)]*
|
||||||
|
static const EdgeInsets xxsStretchInset =
|
||||||
|
EdgeInsets.symmetric(vertical: xxs, horizontal: xxs / 2);
|
||||||
|
|
||||||
|
/// xs = factor * 4
|
||||||
|
///
|
||||||
|
/// A stretched inset reduces space left and right by 50%.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.symmetric(vertical: value, horizontal: value / 2)]*
|
||||||
|
static const EdgeInsets xsStretchInset =
|
||||||
|
EdgeInsets.symmetric(vertical: xs, horizontal: xs / 2);
|
||||||
|
|
||||||
|
/// s = factor * 8
|
||||||
|
///
|
||||||
|
/// A stretched inset reduces space left and right by 50%.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.symmetric(vertical: value, horizontal: value / 2)]*
|
||||||
|
static const EdgeInsets sStretchInset =
|
||||||
|
EdgeInsets.symmetric(vertical: s, horizontal: s / 2);
|
||||||
|
|
||||||
|
/// m = factor * 16
|
||||||
|
///
|
||||||
|
/// A stretched inset reduces space left and right by 50%.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.symmetric(vertical: value, horizontal: value / 2)]*
|
||||||
|
static const EdgeInsets mStretchInset =
|
||||||
|
EdgeInsets.symmetric(vertical: m, horizontal: m / 2);
|
||||||
|
|
||||||
|
/// l = factor * 32
|
||||||
|
///
|
||||||
|
/// A stretched inset reduces space left and right by 50%.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.symmetric(vertical: value, horizontal: value / 2)]*
|
||||||
|
static const EdgeInsets lStretchInset =
|
||||||
|
EdgeInsets.symmetric(vertical: l, horizontal: l / 2);
|
||||||
|
|
||||||
|
/// xl = factor * 64
|
||||||
|
///
|
||||||
|
/// A stretched inset reduces space left and right by 50%.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.symmetric(vertical: value, horizontal: value / 2)]*
|
||||||
|
static const EdgeInsets xlStretchInset =
|
||||||
|
EdgeInsets.symmetric(vertical: xl, horizontal: xl / 2);
|
||||||
|
|
||||||
|
/// xxl = factor * 128
|
||||||
|
///
|
||||||
|
/// A stretched inset reduces space left and right by 50%.
|
||||||
|
///
|
||||||
|
/// *e.g [EdgeInsets.symmetric(vertical: value, horizontal: value / 2)]*
|
||||||
|
static const EdgeInsets xxlStretchInset =
|
||||||
|
EdgeInsets.symmetric(vertical: xxl, horizontal: xxl / 2);
|
||||||
|
}
|
240
apps/wyatt_clean_code/lib/core/design_system/theme.dart
Normal file
240
apps/wyatt_clean_code/lib/core/design_system/theme.dart
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/design_system/colors.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/design_system/typography.dart';
|
||||||
|
|
||||||
|
const _smallTextScaleFactor = 0.80;
|
||||||
|
const _largeTextScaleFactor = 1.20;
|
||||||
|
|
||||||
|
/// Namespace for the [ThemeData].
|
||||||
|
class AppTheme {
|
||||||
|
/// Light `ThemeData` for UI.
|
||||||
|
static ThemeData get light => ThemeData(
|
||||||
|
colorScheme: ColorScheme.fromSwatch(
|
||||||
|
primarySwatch: ColorName.seedColor,
|
||||||
|
accentColor: ColorName.seedColorAccent,
|
||||||
|
cardColor: ColorName.lightBackground,
|
||||||
|
backgroundColor: ColorName.lightBackground,
|
||||||
|
errorColor: ColorName.lightError,
|
||||||
|
),
|
||||||
|
appBarTheme: _appBarLightTheme,
|
||||||
|
elevatedButtonTheme: _elevatedButtonLightTheme,
|
||||||
|
outlinedButtonTheme: _outlinedButtonLightTheme,
|
||||||
|
textTheme: _textTheme(),
|
||||||
|
dialogTheme: _dialogLightTheme,
|
||||||
|
tooltipTheme: _tooltipLightTheme,
|
||||||
|
bottomSheetTheme: _bottomSheetLightTheme,
|
||||||
|
tabBarTheme: _tabBarLightTheme,
|
||||||
|
dividerTheme: _dividerLightTheme,
|
||||||
|
backgroundColor: ColorName.lightBackground,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// dark `ThemeData` for UI.
|
||||||
|
static ThemeData get dark => ThemeData(
|
||||||
|
colorScheme: ColorScheme.fromSwatch(
|
||||||
|
primarySwatch: ColorName.seedColor,
|
||||||
|
accentColor: ColorName.darkSecondary,
|
||||||
|
cardColor: ColorName.darkBackground,
|
||||||
|
backgroundColor: ColorName.darkBackground,
|
||||||
|
errorColor: ColorName.darkError,
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
),
|
||||||
|
appBarTheme: _appBarDarkTheme,
|
||||||
|
elevatedButtonTheme: _elevatedButtonDarkTheme,
|
||||||
|
outlinedButtonTheme: _outlinedButtonDarkTheme,
|
||||||
|
textTheme: _textTheme(isDark: true),
|
||||||
|
dialogTheme: _dialogDarkTheme,
|
||||||
|
tooltipTheme: _tooltipDarkTheme,
|
||||||
|
bottomSheetTheme: _bottomSheetDarkTheme,
|
||||||
|
tabBarTheme: _tabBarDarkTheme,
|
||||||
|
dividerTheme: _dividerDarkTheme,
|
||||||
|
backgroundColor: ColorName.darkBackground,
|
||||||
|
canvasColor: ColorName.darkBackground,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// `ThemeData` for UI for small screens.
|
||||||
|
static ThemeData get lightSmall =>
|
||||||
|
light.copyWith(textTheme: _smallTextTheme());
|
||||||
|
|
||||||
|
/// `ThemeData` for UI for medium screens.
|
||||||
|
static ThemeData get lightMedium =>
|
||||||
|
light.copyWith(textTheme: _smallTextTheme());
|
||||||
|
|
||||||
|
/// `ThemeData` for UI for large screens.
|
||||||
|
static ThemeData get lightLarge =>
|
||||||
|
light.copyWith(textTheme: _largeTextTheme());
|
||||||
|
|
||||||
|
/// `ThemeData` for UI for small screens.
|
||||||
|
static ThemeData get darkSmall =>
|
||||||
|
dark.copyWith(textTheme: _smallTextTheme(isDark: true));
|
||||||
|
|
||||||
|
/// `ThemeData` for UI for medium screens.
|
||||||
|
static ThemeData get darkMedium =>
|
||||||
|
dark.copyWith(textTheme: _smallTextTheme(isDark: true));
|
||||||
|
|
||||||
|
/// `ThemeData` for UI for large screens.
|
||||||
|
static ThemeData get darkLarge =>
|
||||||
|
dark.copyWith(textTheme: _largeTextTheme(isDark: true));
|
||||||
|
|
||||||
|
static TextTheme _textTheme({bool isDark = false}) => TextTheme(
|
||||||
|
headline1: AppTypography.headline1,
|
||||||
|
headline2: AppTypography.headline2,
|
||||||
|
headline3: AppTypography.headline3,
|
||||||
|
headline4: AppTypography.headline4,
|
||||||
|
headline5: AppTypography.headline5,
|
||||||
|
headline6: AppTypography.headline6,
|
||||||
|
subtitle1: AppTypography.subtitle1,
|
||||||
|
subtitle2: AppTypography.subtitle2,
|
||||||
|
bodyText1: AppTypography.bodyText1,
|
||||||
|
bodyText2: AppTypography.bodyText2,
|
||||||
|
caption: AppTypography.caption,
|
||||||
|
overline: AppTypography.overline,
|
||||||
|
button: AppTypography.button,
|
||||||
|
).apply(
|
||||||
|
bodyColor:
|
||||||
|
isDark ? ColorName.darkOnBackground : ColorName.lightOnBackground,
|
||||||
|
displayColor:
|
||||||
|
isDark ? ColorName.darkOnBackground : ColorName.lightOnBackground,
|
||||||
|
);
|
||||||
|
|
||||||
|
static TextTheme _smallTextTheme({bool isDark = false}) =>
|
||||||
|
_textTheme(isDark: isDark).apply(fontSizeFactor: _smallTextScaleFactor);
|
||||||
|
|
||||||
|
static TextTheme _largeTextTheme({bool isDark = false}) =>
|
||||||
|
_textTheme(isDark: isDark).apply(fontSizeFactor: _largeTextScaleFactor);
|
||||||
|
|
||||||
|
static AppBarTheme get _appBarLightTheme =>
|
||||||
|
const AppBarTheme(color: ColorName.lightPrimary);
|
||||||
|
|
||||||
|
static AppBarTheme get _appBarDarkTheme =>
|
||||||
|
const AppBarTheme(color: ColorName.darkSurfaceVariant);
|
||||||
|
|
||||||
|
static ElevatedButtonThemeData get _elevatedButtonLightTheme =>
|
||||||
|
ElevatedButtonThemeData(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
elevation: 0,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||||
|
),
|
||||||
|
primary: ColorName.lightPrimary,
|
||||||
|
fixedSize: const Size(208, 54),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
static ElevatedButtonThemeData get _elevatedButtonDarkTheme =>
|
||||||
|
ElevatedButtonThemeData(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
elevation: 0,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||||
|
),
|
||||||
|
primary: ColorName.darkPrimary,
|
||||||
|
fixedSize: const Size(208, 54),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
static OutlinedButtonThemeData get _outlinedButtonLightTheme =>
|
||||||
|
OutlinedButtonThemeData(
|
||||||
|
style: OutlinedButton.styleFrom(
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||||
|
),
|
||||||
|
side: const BorderSide(color: ColorName.lightOutline, width: 2),
|
||||||
|
primary: ColorName.lightPrimary,
|
||||||
|
fixedSize: const Size(208, 54),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
static OutlinedButtonThemeData get _outlinedButtonDarkTheme =>
|
||||||
|
OutlinedButtonThemeData(
|
||||||
|
style: OutlinedButton.styleFrom(
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||||
|
),
|
||||||
|
side: const BorderSide(color: ColorName.darkOutline, width: 2),
|
||||||
|
primary: ColorName.darkPrimary,
|
||||||
|
fixedSize: const Size(208, 54),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
static TooltipThemeData get _tooltipLightTheme => const TooltipThemeData(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorName.lightInverseSurface,
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(5)),
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.all(10),
|
||||||
|
textStyle: TextStyle(color: ColorName.lightOnInverseSurface),
|
||||||
|
);
|
||||||
|
|
||||||
|
static TooltipThemeData get _tooltipDarkTheme => const TooltipThemeData(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorName.darkInverseSurface,
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(5)),
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.all(10),
|
||||||
|
textStyle: TextStyle(color: ColorName.darkOnInverseSurface),
|
||||||
|
);
|
||||||
|
|
||||||
|
static DialogTheme get _dialogLightTheme => DialogTheme(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
static DialogTheme get _dialogDarkTheme => DialogTheme(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
static BottomSheetThemeData get _bottomSheetLightTheme =>
|
||||||
|
const BottomSheetThemeData(
|
||||||
|
backgroundColor: ColorName.lightBackground,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
static BottomSheetThemeData get _bottomSheetDarkTheme =>
|
||||||
|
const BottomSheetThemeData(
|
||||||
|
backgroundColor: ColorName.darkBackground,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
static TabBarTheme get _tabBarLightTheme => const TabBarTheme(
|
||||||
|
indicator: UnderlineTabIndicator(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
width: 2,
|
||||||
|
color: ColorName.lightPrimary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
labelColor: ColorName.lightPrimary,
|
||||||
|
unselectedLabelColor: ColorName.lightOutline,
|
||||||
|
indicatorSize: TabBarIndicatorSize.tab,
|
||||||
|
);
|
||||||
|
|
||||||
|
static TabBarTheme get _tabBarDarkTheme => const TabBarTheme(
|
||||||
|
indicator: UnderlineTabIndicator(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
width: 2,
|
||||||
|
color: ColorName.darkPrimary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
labelColor: ColorName.darkPrimary,
|
||||||
|
unselectedLabelColor: ColorName.darkOutline,
|
||||||
|
indicatorSize: TabBarIndicatorSize.tab,
|
||||||
|
);
|
||||||
|
|
||||||
|
static DividerThemeData get _dividerLightTheme => const DividerThemeData(
|
||||||
|
space: 0,
|
||||||
|
thickness: 1,
|
||||||
|
color: ColorName.lightOutline,
|
||||||
|
);
|
||||||
|
|
||||||
|
static DividerThemeData get _dividerDarkTheme => const DividerThemeData(
|
||||||
|
space: 0,
|
||||||
|
thickness: 1,
|
||||||
|
color: ColorName.darkOutline,
|
||||||
|
);
|
||||||
|
}
|
115
apps/wyatt_clean_code/lib/core/design_system/typography.dart
Normal file
115
apps/wyatt_clean_code/lib/core/design_system/typography.dart
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
abstract class AppFontWeight {
|
||||||
|
/// FontWeight value of `w900`
|
||||||
|
static const FontWeight black = FontWeight.w900;
|
||||||
|
|
||||||
|
/// FontWeight value of `w800`
|
||||||
|
static const FontWeight extraBold = FontWeight.w800;
|
||||||
|
|
||||||
|
/// FontWeight value of `w700`
|
||||||
|
static const FontWeight bold = FontWeight.w700;
|
||||||
|
|
||||||
|
/// FontWeight value of `w600`
|
||||||
|
static const FontWeight semiBold = FontWeight.w600;
|
||||||
|
|
||||||
|
/// FontWeight value of `w500`
|
||||||
|
static const FontWeight medium = FontWeight.w500;
|
||||||
|
|
||||||
|
/// FontWeight value of `w400`
|
||||||
|
static const FontWeight regular = FontWeight.w400;
|
||||||
|
|
||||||
|
/// FontWeight value of `w300`
|
||||||
|
static const FontWeight light = FontWeight.w300;
|
||||||
|
|
||||||
|
/// FontWeight value of `w200`
|
||||||
|
static const FontWeight extraLight = FontWeight.w200;
|
||||||
|
|
||||||
|
/// FontWeight value of `w100`
|
||||||
|
static const FontWeight thin = FontWeight.w100;
|
||||||
|
}
|
||||||
|
|
||||||
|
class AppTypography {
|
||||||
|
static const TextStyle _base = TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontWeight: AppFontWeight.regular,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Headline 1 Text Style
|
||||||
|
static TextStyle get headline1 => _base.copyWith(
|
||||||
|
fontSize: 56,
|
||||||
|
fontWeight: AppFontWeight.medium,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Headline 2 Text Style
|
||||||
|
static TextStyle get headline2 => _base.copyWith(
|
||||||
|
fontSize: 30,
|
||||||
|
fontWeight: AppFontWeight.regular,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Headline 3 Text Style
|
||||||
|
static TextStyle get headline3 => _base.copyWith(
|
||||||
|
fontSize: 28,
|
||||||
|
fontWeight: AppFontWeight.regular,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Headline 4 Text Style
|
||||||
|
static TextStyle get headline4 => _base.copyWith(
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: AppFontWeight.bold,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Headline 5 Text Style
|
||||||
|
static TextStyle get headline5 => _base.copyWith(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: AppFontWeight.medium,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Headline 6 Text Style
|
||||||
|
static TextStyle get headline6 => _base.copyWith(
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: AppFontWeight.bold,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Subtitle 1 Text Style
|
||||||
|
static TextStyle get subtitle1 => _base.copyWith(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: AppFontWeight.bold,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Subtitle 2 Text Style
|
||||||
|
static TextStyle get subtitle2 => _base.copyWith(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: AppFontWeight.bold,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Body Text 1 Text Style
|
||||||
|
static TextStyle get bodyText1 => _base.copyWith(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: AppFontWeight.medium,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Body Text 2 Text Style (the default)
|
||||||
|
static TextStyle get bodyText2 => _base.copyWith(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: AppFontWeight.regular,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Caption Text Style
|
||||||
|
static TextStyle get caption => _base.copyWith(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: AppFontWeight.regular,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Overline Text Style
|
||||||
|
static TextStyle get overline => _base.copyWith(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: AppFontWeight.regular,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Button Text Style
|
||||||
|
static TextStyle get button => _base.copyWith(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: AppFontWeight.medium,
|
||||||
|
);
|
||||||
|
}
|
7
apps/wyatt_clean_code/lib/core/enums/exception_type.dart
Normal file
7
apps/wyatt_clean_code/lib/core/enums/exception_type.dart
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
enum AppExceptionType {
|
||||||
|
network,
|
||||||
|
api,
|
||||||
|
database,
|
||||||
|
cache,
|
||||||
|
assertion,
|
||||||
|
}
|
12
apps/wyatt_clean_code/lib/core/enums/flavor.dart
Normal file
12
apps/wyatt_clean_code/lib/core/enums/flavor.dart
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
enum Flavor {
|
||||||
|
development('dev', Colors.red),
|
||||||
|
staging('stg', Colors.blue),
|
||||||
|
production('prod', Colors.green);
|
||||||
|
|
||||||
|
final String short;
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
const Flavor(this.short, this.color);
|
||||||
|
}
|
29
apps/wyatt_clean_code/lib/core/errors/exceptions.dart
Normal file
29
apps/wyatt_clean_code/lib/core/errors/exceptions.dart
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/enums/exception_type.dart';
|
||||||
|
|
||||||
|
abstract class AppException extends Equatable implements Exception {
|
||||||
|
final String message;
|
||||||
|
final AppExceptionType type;
|
||||||
|
|
||||||
|
AppException(this.type, [String? message]) : message = message ?? type.name;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [message, type];
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => message;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClientException extends AppException {
|
||||||
|
ClientException(super.type, [super.message]);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'ClientException: ${super.toString()}';
|
||||||
|
}
|
||||||
|
|
||||||
|
class ServerException extends AppException {
|
||||||
|
ServerException(super.type, [super.message]);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'ServerException: ${super.toString()}';
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
export 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
extension BuildContextX on BuildContext {
|
||||||
|
AppLocalizations get l10n => AppLocalizations.of(this);
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
import 'package:wyatt_clean_code/core/utils/screen_util.dart';
|
||||||
|
|
||||||
|
extension DoubleX on double {
|
||||||
|
double get w => ScreenUtil().setWidth(this);
|
||||||
|
|
||||||
|
double get h => ScreenUtil().setHeight(this);
|
||||||
|
|
||||||
|
double get sp => ScreenUtil().setSp(this);
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
import 'package:logger/logger.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/utils/wyatt_printer.dart';
|
||||||
|
|
||||||
|
extension ObjectX on Object {
|
||||||
|
void log({Level level = Level.debug, String Function(Object obj)? wrap}) {
|
||||||
|
final msg = wrap != null ? wrap(this) : this;
|
||||||
|
WyattPrinter.get().log(level, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Log a message at level [Level.verbose].
|
||||||
|
void v({String Function(Object obj)? wrap}) =>
|
||||||
|
log(level: Level.verbose, wrap: wrap);
|
||||||
|
|
||||||
|
/// Log a message at level [Level.debug].
|
||||||
|
void d({String Function(Object obj)? wrap}) => log(wrap: wrap);
|
||||||
|
|
||||||
|
/// Log a message at level [Level.info].
|
||||||
|
void i({String Function(Object obj)? wrap}) =>
|
||||||
|
log(level: Level.info, wrap: wrap);
|
||||||
|
|
||||||
|
/// Log a message at level [Level.warning].
|
||||||
|
void w({String Function(Object obj)? wrap}) =>
|
||||||
|
log(level: Level.warning, wrap: wrap);
|
||||||
|
|
||||||
|
/// Log a message at level [Level.error].
|
||||||
|
void e({String Function(Object obj)? wrap}) =>
|
||||||
|
log(level: Level.error, wrap: wrap);
|
||||||
|
|
||||||
|
/// Log a message at level [Level.wtf].
|
||||||
|
void wtf({String Function(Object obj)? wrap}) =>
|
||||||
|
log(level: Level.wtf, wrap: wrap);
|
||||||
|
}
|
64
apps/wyatt_clean_code/lib/core/flavors/flavor_settings.dart
Normal file
64
apps/wyatt_clean_code/lib/core/flavors/flavor_settings.dart
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import 'package:wyatt_clean_code/core/enums/flavor.dart';
|
||||||
|
|
||||||
|
class FlavorSettings {
|
||||||
|
static FlavorSettings? _instance;
|
||||||
|
|
||||||
|
final Flavor flavor;
|
||||||
|
|
||||||
|
// Per flavor settings
|
||||||
|
String apiKey = '';
|
||||||
|
|
||||||
|
/// Banner are not display in release mode, whatever this value
|
||||||
|
bool displayBanner = true;
|
||||||
|
|
||||||
|
FlavorSettings._(this.flavor);
|
||||||
|
|
||||||
|
factory FlavorSettings.development() {
|
||||||
|
_instance ??= FlavorSettings._(Flavor.development);
|
||||||
|
if (_instance!.flavor != Flavor.development) {
|
||||||
|
throw Exception('Flavor already initialized in: ${_instance!.flavor}');
|
||||||
|
}
|
||||||
|
return _instance!;
|
||||||
|
}
|
||||||
|
|
||||||
|
factory FlavorSettings.staging() {
|
||||||
|
_instance ??= FlavorSettings._(Flavor.staging);
|
||||||
|
if (_instance!.flavor != Flavor.staging) {
|
||||||
|
throw Exception('Flavor already initialized in: ${_instance!.flavor}');
|
||||||
|
}
|
||||||
|
return _instance!;
|
||||||
|
}
|
||||||
|
|
||||||
|
factory FlavorSettings.production() {
|
||||||
|
_instance ??= FlavorSettings._(Flavor.production);
|
||||||
|
if (_instance!.flavor != Flavor.production) {
|
||||||
|
throw Exception('Flavor already initialized in: ${_instance!.flavor}');
|
||||||
|
}
|
||||||
|
return _instance!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns initialized [FlavorSettings], may throw if not initialized.
|
||||||
|
static FlavorSettings get() {
|
||||||
|
if (_instance == null) {
|
||||||
|
throw Exception('Flavor not initialized!');
|
||||||
|
}
|
||||||
|
return _instance!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// To call after `WidgetsFlutterBinding.ensureInitialized()`
|
||||||
|
///
|
||||||
|
/// Here you can config all the settings attributes.
|
||||||
|
static void init() {
|
||||||
|
switch (get().flavor) {
|
||||||
|
case Flavor.development:
|
||||||
|
_instance!.apiKey = 'example-dev';
|
||||||
|
break;
|
||||||
|
case Flavor.staging:
|
||||||
|
_instance!.apiKey = 'example-stg';
|
||||||
|
break;
|
||||||
|
case Flavor.production:
|
||||||
|
_instance!.apiKey = 'example-prod';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
import 'package:wyatt_clean_code/domain/data_sources/local/base_local_data_source.dart';
|
||||||
|
|
||||||
|
mixin LocalDataSource<Local extends BaseLocalDataSource> {
|
||||||
|
/// Offline data source, for debug or cache
|
||||||
|
Local get localDataSource;
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
import 'package:wyatt_clean_code/domain/data_sources/remote/base_remote_data_source.dart';
|
||||||
|
|
||||||
|
mixin RemoteDataSource<Remote extends BaseRemoteDataSource> {
|
||||||
|
/// Online data source, to provide data through API
|
||||||
|
Remote get remoteDataSource;
|
||||||
|
}
|
37
apps/wyatt_clean_code/lib/core/routes/router.dart
Normal file
37
apps/wyatt_clean_code/lib/core/routes/router.dart
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:wyatt_clean_code/presentation/features/counter/counter_page.dart';
|
||||||
|
import 'package:wyatt_clean_code/presentation/features/initial/initial_page.dart';
|
||||||
|
|
||||||
|
abstract class AppRouter {
|
||||||
|
static Page<void> defaultTransition(
|
||||||
|
BuildContext context,
|
||||||
|
GoRouterState state,
|
||||||
|
Widget child,
|
||||||
|
) =>
|
||||||
|
MaterialPage<void>(
|
||||||
|
key: state.pageKey,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
|
||||||
|
static final List<GoRoute> routes = [
|
||||||
|
GoRoute(
|
||||||
|
path: '/',
|
||||||
|
name: InitialPage.pageName,
|
||||||
|
pageBuilder: (context, state) => defaultTransition(
|
||||||
|
context,
|
||||||
|
state,
|
||||||
|
const InitialPage(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/counter',
|
||||||
|
name: CounterPage.pageName,
|
||||||
|
pageBuilder: (context, state) => defaultTransition(
|
||||||
|
context,
|
||||||
|
state,
|
||||||
|
const CounterPage(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
13
apps/wyatt_clean_code/lib/core/usecases/usecase.dart
Normal file
13
apps/wyatt_clean_code/lib/core/usecases/usecase.dart
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/errors/exceptions.dart';
|
||||||
|
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||||
|
|
||||||
|
// ignore: one_member_abstracts
|
||||||
|
abstract class UseCase<Type, Params> {
|
||||||
|
Future<Result<Type, AppException>> call(Params params);
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoParams extends Equatable {
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
56
apps/wyatt_clean_code/lib/core/utils/app_bloc_observer.dart
Normal file
56
apps/wyatt_clean_code/lib/core/utils/app_bloc_observer.dart
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:logger/logger.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/extensions/object_extension.dart';
|
||||||
|
|
||||||
|
class AppBlocObserver extends BlocObserver {
|
||||||
|
final bool printEvent;
|
||||||
|
final bool printError;
|
||||||
|
final bool printChange;
|
||||||
|
final bool printTransition;
|
||||||
|
|
||||||
|
final Logger logger = Logger(printer: SimplePrinter());
|
||||||
|
|
||||||
|
AppBlocObserver({
|
||||||
|
this.printEvent = true,
|
||||||
|
this.printError = true,
|
||||||
|
this.printTransition = true,
|
||||||
|
this.printChange = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onEvent(Bloc<dynamic, dynamic> bloc, Object? event) {
|
||||||
|
super.onEvent(bloc, event);
|
||||||
|
if (printEvent) {
|
||||||
|
event?.d(wrap: (obj) => 'onEvent $event');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onError(BlocBase<dynamic> bloc, Object error, StackTrace stackTrace) {
|
||||||
|
if (printError) {
|
||||||
|
error.e(
|
||||||
|
wrap: (obj) => 'onError(${bloc.runtimeType}, $obj, $stackTrace)',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
super.onError(bloc, error, stackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onChange(BlocBase<dynamic> bloc, Change<dynamic> change) {
|
||||||
|
super.onChange(bloc, change);
|
||||||
|
if (printChange) {
|
||||||
|
change.d(wrap: (obj) => 'onChange(${bloc.runtimeType}, $obj)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onTransition(
|
||||||
|
Bloc<dynamic, dynamic> bloc,
|
||||||
|
Transition<dynamic, dynamic> transition,
|
||||||
|
) {
|
||||||
|
super.onTransition(bloc, transition);
|
||||||
|
if (printTransition) {
|
||||||
|
transition.d(wrap: (obj) => 'onTransition $obj');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
107
apps/wyatt_clean_code/lib/core/utils/screen_util.dart
Normal file
107
apps/wyatt_clean_code/lib/core/utils/screen_util.dart
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
class ScreenUtil {
|
||||||
|
static late ScreenUtil _instance;
|
||||||
|
static const int defaultWidth = 414;
|
||||||
|
static const int defaultHeight = 896;
|
||||||
|
|
||||||
|
/// Size of the phone in UI Design ,px
|
||||||
|
late num uiWidthPx;
|
||||||
|
late num uiHeightPx;
|
||||||
|
|
||||||
|
/// allowFontScaling Specifies whether fonts should scale to respect Text
|
||||||
|
/// Size accessibility settings. The default is false.
|
||||||
|
late bool allowFontScaling;
|
||||||
|
|
||||||
|
static late double _screenWidth;
|
||||||
|
static late double _screenHeight;
|
||||||
|
static late double _pixelRatio;
|
||||||
|
static late double _statusBarHeight;
|
||||||
|
static late double _bottomBarHeight;
|
||||||
|
static late double _textScaleFactor;
|
||||||
|
|
||||||
|
factory ScreenUtil() => _instance;
|
||||||
|
|
||||||
|
ScreenUtil._();
|
||||||
|
|
||||||
|
static void init({
|
||||||
|
num width = defaultWidth,
|
||||||
|
num height = defaultHeight,
|
||||||
|
bool allowFontScaling = false,
|
||||||
|
}) {
|
||||||
|
_instance = ScreenUtil._();
|
||||||
|
_instance.uiWidthPx = width;
|
||||||
|
_instance.uiHeightPx = height;
|
||||||
|
_instance.allowFontScaling = allowFontScaling;
|
||||||
|
_pixelRatio = window.devicePixelRatio;
|
||||||
|
_screenWidth = window.physicalSize.width;
|
||||||
|
_screenHeight = window.physicalSize.height;
|
||||||
|
_statusBarHeight = window.padding.top;
|
||||||
|
_bottomBarHeight = window.padding.bottom;
|
||||||
|
_textScaleFactor = window.textScaleFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The number of font pixels for each logical pixel.
|
||||||
|
static double get textScaleFactor => _textScaleFactor;
|
||||||
|
|
||||||
|
/// The size of the media in logical pixels (e.g, the size of the screen).
|
||||||
|
static double get pixelRatio => _pixelRatio;
|
||||||
|
|
||||||
|
/// The horizontal extent of this size.
|
||||||
|
static double get screenWidth => _screenWidth / _pixelRatio;
|
||||||
|
|
||||||
|
///The vertical extent of this size. dp
|
||||||
|
static double get screenHeight => _screenHeight / _pixelRatio;
|
||||||
|
|
||||||
|
/// The vertical extent of this size. px
|
||||||
|
static double get screenWidthPx => _screenWidth;
|
||||||
|
|
||||||
|
/// The vertical extent of this size. px
|
||||||
|
static double get screenHeightPx => _screenHeight;
|
||||||
|
|
||||||
|
/// The offset from the top
|
||||||
|
static double get statusBarHeight => _statusBarHeight / _pixelRatio;
|
||||||
|
|
||||||
|
/// The offset from the top
|
||||||
|
static double get statusBarHeightPx => _statusBarHeight;
|
||||||
|
|
||||||
|
/// The offset from the bottom.
|
||||||
|
static double get bottomBarHeight => _bottomBarHeight;
|
||||||
|
|
||||||
|
/// The ratio of the actual dp to the design draft px
|
||||||
|
double get scaleWidth => screenWidth / uiWidthPx;
|
||||||
|
|
||||||
|
double get scaleHeight =>
|
||||||
|
(_screenHeight - _statusBarHeight - _bottomBarHeight) / uiHeightPx;
|
||||||
|
|
||||||
|
double get scaleText => scaleWidth;
|
||||||
|
|
||||||
|
/// Width function
|
||||||
|
///
|
||||||
|
/// Adapted to the device width of the UI Design.
|
||||||
|
/// Height can also be adapted according to this to ensure no deformation ,
|
||||||
|
/// if you want a square
|
||||||
|
double setWidth(num width) => width * scaleWidth;
|
||||||
|
|
||||||
|
/// Height function
|
||||||
|
///
|
||||||
|
/// Highly adaptable to the device according to UI Design
|
||||||
|
/// It is recommended to use this method to achieve a high degree
|
||||||
|
/// of adaptation when it is found that one screen in the UI design
|
||||||
|
/// does not match the current style effect, or if there is a difference
|
||||||
|
/// in shape.
|
||||||
|
double setHeight(num height) => height * scaleHeight;
|
||||||
|
|
||||||
|
/// FontSize function
|
||||||
|
///
|
||||||
|
/// [fontSize] The size of the font on the UI design, in px.
|
||||||
|
/// [allowFontScaling]
|
||||||
|
double setSp(num fontSize, {bool allowFontScalingSelf = false}) =>
|
||||||
|
allowFontScalingSelf
|
||||||
|
? (allowFontScalingSelf
|
||||||
|
? (fontSize * scaleText)
|
||||||
|
: ((fontSize * scaleText) / _textScaleFactor))
|
||||||
|
: (allowFontScaling
|
||||||
|
? (fontSize * scaleText)
|
||||||
|
: ((fontSize * scaleText) / _textScaleFactor));
|
||||||
|
}
|
43
apps/wyatt_clean_code/lib/core/utils/wyatt_printer.dart
Normal file
43
apps/wyatt_clean_code/lib/core/utils/wyatt_printer.dart
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:logger/logger.dart';
|
||||||
|
|
||||||
|
class WyattPrinter extends LogPrinter {
|
||||||
|
WyattPrinter({this.colors = true});
|
||||||
|
|
||||||
|
final bool colors;
|
||||||
|
|
||||||
|
static Logger? _instance;
|
||||||
|
|
||||||
|
/// Returns [Logger] instance or create it if not.
|
||||||
|
static Logger get({bool colors = true}) {
|
||||||
|
_instance ??= Logger(printer: WyattPrinter(colors: colors));
|
||||||
|
return _instance!;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<String> log(LogEvent event) {
|
||||||
|
// final classNameStr = (className != null) ? '$className ' : '';
|
||||||
|
final messageStr = _stringifyMessage(event.message);
|
||||||
|
final errorStr = event.error != null ? 'ERROR: ${event.error}' : '';
|
||||||
|
return ['${_labelFor(event.level)} $messageStr$errorStr'];
|
||||||
|
}
|
||||||
|
|
||||||
|
String _labelFor(Level level) {
|
||||||
|
final prefix = PrettyPrinter.levelEmojis[level]!;
|
||||||
|
final color = PrettyPrinter.levelColors[level]!;
|
||||||
|
|
||||||
|
return colors ? color(prefix) : prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
String _stringifyMessage(dynamic message) {
|
||||||
|
// ignore: avoid_dynamic_calls
|
||||||
|
final finalMessage = message is Function ? message() : message;
|
||||||
|
if (finalMessage is Map || finalMessage is Iterable) {
|
||||||
|
const encoder = JsonEncoder.withIndent(null);
|
||||||
|
return encoder.convert(finalMessage);
|
||||||
|
} else {
|
||||||
|
return finalMessage.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
# just to keep empty folder in brick generation
|
@ -0,0 +1 @@
|
|||||||
|
# just to keep empty folder in brick generation
|
1
apps/wyatt_clean_code/lib/data/models/.gitkeep
Normal file
1
apps/wyatt_clean_code/lib/data/models/.gitkeep
Normal file
@ -0,0 +1 @@
|
|||||||
|
# just to keep empty folder in brick generation
|
@ -0,0 +1,24 @@
|
|||||||
|
import 'package:wyatt_clean_code/core/enums/exception_type.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/errors/exceptions.dart';
|
||||||
|
import 'package:wyatt_clean_code/domain/repositories/counter_repository.dart';
|
||||||
|
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||||
|
|
||||||
|
class CounterRepositoryImpl implements CounterRepository {
|
||||||
|
Result<int, AppException> _check(int value) =>
|
||||||
|
Result.conditionalLazy<int, AppException>(
|
||||||
|
value >= 0,
|
||||||
|
() => value,
|
||||||
|
() => ClientException(
|
||||||
|
AppExceptionType.assertion,
|
||||||
|
"Counter can't be negative!",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<int, AppException>> decrement(int newState) async =>
|
||||||
|
_check(newState);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<int, AppException>> increment(int newState) async =>
|
||||||
|
_check(newState);
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
abstract class BaseDataSource {}
|
@ -0,0 +1,3 @@
|
|||||||
|
import 'package:wyatt_clean_code/domain/data_sources/base_data_source.dart';
|
||||||
|
|
||||||
|
abstract class BaseLocalDataSource extends BaseDataSource {}
|
@ -0,0 +1,3 @@
|
|||||||
|
import 'package:wyatt_clean_code/domain/data_sources/base_data_source.dart';
|
||||||
|
|
||||||
|
abstract class BaseRemoteDataSource extends BaseDataSource {}
|
1
apps/wyatt_clean_code/lib/domain/entities/.gitkeep
Normal file
1
apps/wyatt_clean_code/lib/domain/entities/.gitkeep
Normal file
@ -0,0 +1 @@
|
|||||||
|
# just to keep empty folder in brick generation
|
@ -0,0 +1 @@
|
|||||||
|
abstract class BaseRepository {}
|
@ -0,0 +1,8 @@
|
|||||||
|
import 'package:wyatt_clean_code/core/errors/exceptions.dart';
|
||||||
|
import 'package:wyatt_clean_code/domain/repositories/base_repository.dart';
|
||||||
|
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||||
|
|
||||||
|
abstract class CounterRepository extends BaseRepository {
|
||||||
|
Future<Result<int, AppException>> increment(int newState);
|
||||||
|
Future<Result<int, AppException>> decrement(int newState);
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
import 'package:wyatt_clean_code/core/errors/exceptions.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/usecases/usecase.dart';
|
||||||
|
import 'package:wyatt_clean_code/domain/repositories/counter_repository.dart';
|
||||||
|
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||||
|
|
||||||
|
class DecrementCounter extends UseCase<int, int> {
|
||||||
|
final CounterRepository counterRepository;
|
||||||
|
|
||||||
|
DecrementCounter({
|
||||||
|
required this.counterRepository,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<int, AppException>> call(int params) async =>
|
||||||
|
counterRepository.decrement(params);
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
import 'package:wyatt_clean_code/core/errors/exceptions.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/usecases/usecase.dart';
|
||||||
|
import 'package:wyatt_clean_code/domain/repositories/counter_repository.dart';
|
||||||
|
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||||
|
|
||||||
|
class IncrementCounter extends UseCase<int, int> {
|
||||||
|
final CounterRepository counterRepository;
|
||||||
|
|
||||||
|
IncrementCounter({
|
||||||
|
required this.counterRepository,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<int, AppException>> call(int params) async =>
|
||||||
|
counterRepository.increment(params);
|
||||||
|
}
|
195
apps/wyatt_clean_code/lib/gen/colors.gen.dart
Normal file
195
apps/wyatt_clean_code/lib/gen/colors.gen.dart
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
/// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
/// *****************************************************
|
||||||
|
/// FlutterGen
|
||||||
|
/// *****************************************************
|
||||||
|
|
||||||
|
// coverage:ignore-file
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: directives_ordering,unnecessary_import
|
||||||
|
|
||||||
|
import 'package:flutter/painting.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ColorName {
|
||||||
|
ColorName._();
|
||||||
|
|
||||||
|
/// Color: #FF1B1B1B
|
||||||
|
static const Color darkBackground = Color(0xFF1B1B1B);
|
||||||
|
|
||||||
|
/// Color: #FFFFB4A9
|
||||||
|
static const Color darkError = Color(0xFFFFB4A9);
|
||||||
|
|
||||||
|
/// Color: #FF930006
|
||||||
|
static const Color darkErrorContainer = Color(0xFF930006);
|
||||||
|
|
||||||
|
/// Color: #FF0061A6
|
||||||
|
static const Color darkInversePrimary = Color(0xFF0061A6);
|
||||||
|
|
||||||
|
/// Color: #FFE2E2E6
|
||||||
|
static const Color darkInverseSurface = Color(0xFFE2E2E6);
|
||||||
|
|
||||||
|
/// Color: #FFE2E2E6
|
||||||
|
static const Color darkOnBackground = Color(0xFFE2E2E6);
|
||||||
|
|
||||||
|
/// Color: #FF680003
|
||||||
|
static const Color darkOnError = Color(0xFF680003);
|
||||||
|
|
||||||
|
/// Color: #FFFFB4A9
|
||||||
|
static const Color darkOnErrorContainer = Color(0xFFFFB4A9);
|
||||||
|
|
||||||
|
/// Color: #FF2F3033
|
||||||
|
static const Color darkOnInverseSurface = Color(0xFF2F3033);
|
||||||
|
|
||||||
|
/// Color: #FF00325A
|
||||||
|
static const Color darkOnPrimary = Color(0xFF00325A);
|
||||||
|
|
||||||
|
/// Color: #FFD0E4FF
|
||||||
|
static const Color darkOnPrimaryContainer = Color(0xFFD0E4FF);
|
||||||
|
|
||||||
|
/// Color: #FF253140
|
||||||
|
static const Color darkOnSecondary = Color(0xFF253140);
|
||||||
|
|
||||||
|
/// Color: #FFD6E3F7
|
||||||
|
static const Color darkOnSecondaryContainer = Color(0xFFD6E3F7);
|
||||||
|
|
||||||
|
/// Color: #FFE2E2E6
|
||||||
|
static const Color darkOnSurface = Color(0xFFE2E2E6);
|
||||||
|
|
||||||
|
/// Color: #FFC3C7D0
|
||||||
|
static const Color darkOnSurfaceVariant = Color(0xFFC3C7D0);
|
||||||
|
|
||||||
|
/// Color: #FF8D9199
|
||||||
|
static const Color darkOutline = Color(0xFF8D9199);
|
||||||
|
|
||||||
|
/// Color: #FF9CCAFF
|
||||||
|
static const Color darkPrimary = Color(0xFF9CCAFF);
|
||||||
|
|
||||||
|
/// Color: #FF00497F
|
||||||
|
static const Color darkPrimaryContainer = Color(0xFF00497F);
|
||||||
|
|
||||||
|
/// Color: #FFBBC8DB
|
||||||
|
static const Color darkSecondary = Color(0xFFBBC8DB);
|
||||||
|
|
||||||
|
/// Color: #FF3C4858
|
||||||
|
static const Color darkSecondaryContainer = Color(0xFF3C4858);
|
||||||
|
|
||||||
|
/// Color: #FF000000
|
||||||
|
static const Color darkShadow = Color(0xFF000000);
|
||||||
|
|
||||||
|
/// Color: #FF1B1B1B
|
||||||
|
static const Color darkSurface = Color(0xFF1B1B1B);
|
||||||
|
|
||||||
|
/// Color: #FF42474E
|
||||||
|
static const Color darkSurfaceVariant = Color(0xFF42474E);
|
||||||
|
|
||||||
|
/// Color: #FFFDFCFF
|
||||||
|
static const Color lightBackground = Color(0xFFFDFCFF);
|
||||||
|
|
||||||
|
/// Color: #FFBA1B1B
|
||||||
|
static const Color lightError = Color(0xFFBA1B1B);
|
||||||
|
|
||||||
|
/// Color: #FFFFDAD4
|
||||||
|
static const Color lightErrorContainer = Color(0xFFFFDAD4);
|
||||||
|
|
||||||
|
/// Color: #FF9CCAFF
|
||||||
|
static const Color lightInversePrimary = Color(0xFF9CCAFF);
|
||||||
|
|
||||||
|
/// Color: #FF2F3033
|
||||||
|
static const Color lightInverseSurface = Color(0xFF2F3033);
|
||||||
|
|
||||||
|
/// Color: #FF1B1B1B
|
||||||
|
static const Color lightOnBackground = Color(0xFF1B1B1B);
|
||||||
|
|
||||||
|
/// Color: #FFFFFFFF
|
||||||
|
static const Color lightOnError = Color(0xFFFFFFFF);
|
||||||
|
|
||||||
|
/// Color: #FF410001
|
||||||
|
static const Color lightOnErrorContainer = Color(0xFF410001);
|
||||||
|
|
||||||
|
/// Color: #FFF1F0F4
|
||||||
|
static const Color lightOnInverseSurface = Color(0xFFF1F0F4);
|
||||||
|
|
||||||
|
/// Color: #FFFFFFFF
|
||||||
|
static const Color lightOnPrimary = Color(0xFFFFFFFF);
|
||||||
|
|
||||||
|
/// Color: #FF001D36
|
||||||
|
static const Color lightOnPrimaryContainer = Color(0xFF001D36);
|
||||||
|
|
||||||
|
/// Color: #FFFFFFFF
|
||||||
|
static const Color lightOnSecondary = Color(0xFFFFFFFF);
|
||||||
|
|
||||||
|
/// Color: #FF101C2B
|
||||||
|
static const Color lightOnSecondaryContainer = Color(0xFF101C2B);
|
||||||
|
|
||||||
|
/// Color: #FF1B1B1B
|
||||||
|
static const Color lightOnSurface = Color(0xFF1B1B1B);
|
||||||
|
|
||||||
|
/// Color: #FF42474E
|
||||||
|
static const Color lightOnSurfaceVariant = Color(0xFF42474E);
|
||||||
|
|
||||||
|
/// Color: #FF73777F
|
||||||
|
static const Color lightOutline = Color(0xFF73777F);
|
||||||
|
|
||||||
|
/// Color: #FF0061A6
|
||||||
|
static const Color lightPrimary = Color(0xFF0061A6);
|
||||||
|
|
||||||
|
/// Color: #FFD0E4FF
|
||||||
|
static const Color lightPrimaryContainer = Color(0xFFD0E4FF);
|
||||||
|
|
||||||
|
/// Color: #FF535F70
|
||||||
|
static const Color lightSecondary = Color(0xFF535F70);
|
||||||
|
|
||||||
|
/// Color: #FFD6E3F7
|
||||||
|
static const Color lightSecondaryContainer = Color(0xFFD6E3F7);
|
||||||
|
|
||||||
|
/// Color: #FF000000
|
||||||
|
static const Color lightShadow = Color(0xFF000000);
|
||||||
|
|
||||||
|
/// Color: #FFFDFCFF
|
||||||
|
static const Color lightSurface = Color(0xFFFDFCFF);
|
||||||
|
|
||||||
|
/// Color: #FFDFE2EB
|
||||||
|
static const Color lightSurfaceVariant = Color(0xFFDFE2EB);
|
||||||
|
|
||||||
|
/// MaterialColor:
|
||||||
|
/// 50: #FFFFE412FE
|
||||||
|
/// 100: #FFFFBC2DFB
|
||||||
|
/// 200: #FFFF904BF9
|
||||||
|
/// 300: #FFFF6469F7
|
||||||
|
/// 400: #FFFF428075
|
||||||
|
/// 500: #FFFF2196F3
|
||||||
|
/// 600: #FFFF1DC2114
|
||||||
|
/// 700: #FFFF181B382C
|
||||||
|
/// 800: #FFFF14296C06
|
||||||
|
/// 900: #FFFF0B432A01
|
||||||
|
static const MaterialColor seedColor = MaterialColor(
|
||||||
|
0xFFFF2196F3,
|
||||||
|
<int, Color>{
|
||||||
|
50: Color(0xFFFFE412FE),
|
||||||
|
100: Color(0xFFFFBC2DFB),
|
||||||
|
200: Color(0xFFFF904BF9),
|
||||||
|
300: Color(0xFFFF6469F7),
|
||||||
|
400: Color(0xFFFF428075),
|
||||||
|
500: Color(0xFFFF2196F3),
|
||||||
|
600: Color(0xFFFF1DC2114),
|
||||||
|
700: Color(0xFFFF181B382C),
|
||||||
|
800: Color(0xFFFF14296C06),
|
||||||
|
900: Color(0xFFFF0B432A01),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/// MaterialAccentColor:
|
||||||
|
/// 100: #FFFFFFFF
|
||||||
|
/// 200: #FFFFFFFF
|
||||||
|
/// 400: #FFFFFFFF
|
||||||
|
/// 700: #FFFFFFFF
|
||||||
|
static const MaterialAccentColor seedColorAccent = MaterialAccentColor(
|
||||||
|
0xFFFFFFFF,
|
||||||
|
<int, Color>{
|
||||||
|
100: Color(0xFFFFFFFF),
|
||||||
|
200: Color(0xFFFFFFFF),
|
||||||
|
400: Color(0xFFFFFFFF),
|
||||||
|
700: Color(0xFFFFFFFF),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
115
apps/wyatt_clean_code/lib/main.dart
Normal file
115
apps/wyatt_clean_code/lib/main.dart
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
runApp(const MyApp());
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyApp extends StatelessWidget {
|
||||||
|
const MyApp({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
// This widget is the root of your application.
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp(
|
||||||
|
title: 'Flutter Demo',
|
||||||
|
theme: ThemeData(
|
||||||
|
// This is the theme of your application.
|
||||||
|
//
|
||||||
|
// Try running your application with "flutter run". You'll see the
|
||||||
|
// application has a blue toolbar. Then, without quitting the app, try
|
||||||
|
// changing the primarySwatch below to Colors.green and then invoke
|
||||||
|
// "hot reload" (press "r" in the console where you ran "flutter run",
|
||||||
|
// or simply save your changes to "hot reload" in a Flutter IDE).
|
||||||
|
// Notice that the counter didn't reset back to zero; the application
|
||||||
|
// is not restarted.
|
||||||
|
primarySwatch: Colors.blue,
|
||||||
|
),
|
||||||
|
home: const MyHomePage(title: 'Flutter Demo Home Page'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyHomePage extends StatefulWidget {
|
||||||
|
const MyHomePage({Key? key, required this.title}) : super(key: key);
|
||||||
|
|
||||||
|
// This widget is the home page of your application. It is stateful, meaning
|
||||||
|
// that it has a State object (defined below) that contains fields that affect
|
||||||
|
// how it looks.
|
||||||
|
|
||||||
|
// This class is the configuration for the state. It holds the values (in this
|
||||||
|
// case the title) provided by the parent (in this case the App widget) and
|
||||||
|
// used by the build method of the State. Fields in a Widget subclass are
|
||||||
|
// always marked "final".
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MyHomePage> createState() => _MyHomePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MyHomePageState extends State<MyHomePage> {
|
||||||
|
int _counter = 0;
|
||||||
|
|
||||||
|
void _incrementCounter() {
|
||||||
|
setState(() {
|
||||||
|
// This call to setState tells the Flutter framework that something has
|
||||||
|
// changed in this State, which causes it to rerun the build method below
|
||||||
|
// so that the display can reflect the updated values. If we changed
|
||||||
|
// _counter without calling setState(), then the build method would not be
|
||||||
|
// called again, and so nothing would appear to happen.
|
||||||
|
_counter++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// This method is rerun every time setState is called, for instance as done
|
||||||
|
// by the _incrementCounter method above.
|
||||||
|
//
|
||||||
|
// The Flutter framework has been optimized to make rerunning build methods
|
||||||
|
// fast, so that you can just rebuild anything that needs updating rather
|
||||||
|
// than having to individually change instances of widgets.
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
// Here we take the value from the MyHomePage object that was created by
|
||||||
|
// the App.build method, and use it to set our appbar title.
|
||||||
|
title: Text(widget.title),
|
||||||
|
),
|
||||||
|
body: Center(
|
||||||
|
// Center is a layout widget. It takes a single child and positions it
|
||||||
|
// in the middle of the parent.
|
||||||
|
child: Column(
|
||||||
|
// Column is also a layout widget. It takes a list of children and
|
||||||
|
// arranges them vertically. By default, it sizes itself to fit its
|
||||||
|
// children horizontally, and tries to be as tall as its parent.
|
||||||
|
//
|
||||||
|
// Invoke "debug painting" (press "p" in the console, choose the
|
||||||
|
// "Toggle Debug Paint" action from the Flutter Inspector in Android
|
||||||
|
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
|
||||||
|
// to see the wireframe for each widget.
|
||||||
|
//
|
||||||
|
// Column has various properties to control how it sizes itself and
|
||||||
|
// how it positions its children. Here we use mainAxisAlignment to
|
||||||
|
// center the children vertically; the main axis here is the vertical
|
||||||
|
// axis because Columns are vertical (the cross axis would be
|
||||||
|
// horizontal).
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
const Text(
|
||||||
|
'You have pushed the button this many times:',
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'$_counter',
|
||||||
|
style: Theme.of(context).textTheme.headline4,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
onPressed: _incrementCounter,
|
||||||
|
tooltip: 'Increment',
|
||||||
|
child: const Icon(Icons.add),
|
||||||
|
), // This trailing comma makes auto-formatting nicer for build methods.
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
8
apps/wyatt_clean_code/lib/main_development.dart
Normal file
8
apps/wyatt_clean_code/lib/main_development.dart
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import 'package:wyatt_clean_code/bootstrap.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/flavors/flavor_settings.dart';
|
||||||
|
import 'package:wyatt_clean_code/presentation/features/app/app.dart';
|
||||||
|
|
||||||
|
void main(List<String> args) {
|
||||||
|
FlavorSettings.development();
|
||||||
|
bootstrap(App.new);
|
||||||
|
}
|
8
apps/wyatt_clean_code/lib/main_production.dart
Normal file
8
apps/wyatt_clean_code/lib/main_production.dart
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import 'package:wyatt_clean_code/bootstrap.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/flavors/flavor_settings.dart';
|
||||||
|
import 'package:wyatt_clean_code/presentation/features/app/app.dart';
|
||||||
|
|
||||||
|
void main(List<String> args) {
|
||||||
|
FlavorSettings.production();
|
||||||
|
bootstrap(App.new);
|
||||||
|
}
|
8
apps/wyatt_clean_code/lib/main_staging.dart
Normal file
8
apps/wyatt_clean_code/lib/main_staging.dart
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import 'package:wyatt_clean_code/bootstrap.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/flavors/flavor_settings.dart';
|
||||||
|
import 'package:wyatt_clean_code/presentation/features/app/app.dart';
|
||||||
|
|
||||||
|
void main(List<String> args) {
|
||||||
|
FlavorSettings.staging();
|
||||||
|
bootstrap(App.new);
|
||||||
|
}
|
71
apps/wyatt_clean_code/lib/presentation/features/app/app.dart
Normal file
71
apps/wyatt_clean_code/lib/presentation/features/app/app.dart
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/design_system/theme.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/extensions/build_context_extension.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/flavors/flavor_settings.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/routes/router.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/utils/screen_util.dart';
|
||||||
|
import 'package:wyatt_clean_code/data/repositories/counter_repository_impl.dart';
|
||||||
|
import 'package:wyatt_clean_code/domain/repositories/counter_repository.dart';
|
||||||
|
|
||||||
|
class App extends StatelessWidget {
|
||||||
|
App({super.key});
|
||||||
|
|
||||||
|
final GoRouter _router = GoRouter(
|
||||||
|
initialLocation: '/',
|
||||||
|
routes: AppRouter.routes,
|
||||||
|
debugLogDiagnostics: true,
|
||||||
|
errorBuilder: (_, __) => const ColoredBox(
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _bannerFlavor(Widget child) {
|
||||||
|
final flavorInstance = FlavorSettings.get();
|
||||||
|
if (flavorInstance.displayBanner && !kReleaseMode) {
|
||||||
|
return Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: Banner(
|
||||||
|
location: BannerLocation.topEnd,
|
||||||
|
message: flavorInstance.flavor.short,
|
||||||
|
color: flavorInstance.flavor.color,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
ScreenUtil.init();
|
||||||
|
return MultiRepositoryProvider(
|
||||||
|
providers: [
|
||||||
|
RepositoryProvider<CounterRepository>(
|
||||||
|
lazy: true,
|
||||||
|
create: (context) => CounterRepositoryImpl(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: _bannerFlavor(
|
||||||
|
MaterialApp.router(
|
||||||
|
title: 'Wyatt Demo',
|
||||||
|
theme: AppTheme.light,
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
|
routerDelegate: _router.routerDelegate,
|
||||||
|
routeInformationParser: _router.routeInformationParser,
|
||||||
|
routeInformationProvider: _router.routeInformationProvider,
|
||||||
|
localizationsDelegates: const [
|
||||||
|
AppLocalizations.delegate,
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
],
|
||||||
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:wyatt_clean_code/domain/usecases/counter/decrement_counter.dart';
|
||||||
|
import 'package:wyatt_clean_code/domain/usecases/counter/increment_counter.dart';
|
||||||
|
|
||||||
|
class CounterCubit extends Cubit<int> {
|
||||||
|
final IncrementCounter _incrementCounter;
|
||||||
|
final DecrementCounter _decrementCounter;
|
||||||
|
|
||||||
|
CounterCubit({
|
||||||
|
required IncrementCounter incrementCounter,
|
||||||
|
required DecrementCounter decrementCounter,
|
||||||
|
}) : _incrementCounter = incrementCounter,
|
||||||
|
_decrementCounter = decrementCounter,
|
||||||
|
super(0);
|
||||||
|
|
||||||
|
Future<void> increment({int by = 1}) async {
|
||||||
|
// Use `.call(...)` to get documentation, but we can
|
||||||
|
// also directly use `(...)`
|
||||||
|
final response = await _incrementCounter.call(state + by);
|
||||||
|
emit(
|
||||||
|
response.fold(
|
||||||
|
(value) => value,
|
||||||
|
(error) => state,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> decrement({int by = 1}) async {
|
||||||
|
// Use `.call(...)` to get documentation, but we can
|
||||||
|
// also directly use `(...)`
|
||||||
|
final response = await _decrementCounter.call(state - by);
|
||||||
|
emit(
|
||||||
|
response.fold(
|
||||||
|
(value) => value,
|
||||||
|
(error) => state,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:wyatt_clean_code/presentation/features/counter/state_management/counter_page_provider.dart';
|
||||||
|
|
||||||
|
class CounterPage extends StatelessWidget {
|
||||||
|
const CounterPage({super.key});
|
||||||
|
|
||||||
|
static const String pageName = 'counter';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => const CounterPageProvider();
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
|
||||||
|
import 'package:wyatt_clean_code/domain/repositories/counter_repository.dart';
|
||||||
|
import 'package:wyatt_clean_code/domain/usecases/counter/decrement_counter.dart';
|
||||||
|
import 'package:wyatt_clean_code/domain/usecases/counter/increment_counter.dart';
|
||||||
|
import 'package:wyatt_clean_code/presentation/features/counter/blocs/counter_cubit.dart';
|
||||||
|
import 'package:wyatt_clean_code/presentation/features/counter/state_management/counter_text_consumer.dart';
|
||||||
|
import 'package:wyatt_clean_code/presentation/features/counter/widgets/counter_base.dart';
|
||||||
|
|
||||||
|
class CounterPageProvider extends CubitProviderScreen<CounterCubit, int> {
|
||||||
|
const CounterPageProvider({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
CounterCubit create(BuildContext context) => CounterCubit(
|
||||||
|
decrementCounter: DecrementCounter(
|
||||||
|
counterRepository: repo<CounterRepository>(context),
|
||||||
|
),
|
||||||
|
incrementCounter: IncrementCounter(
|
||||||
|
counterRepository: repo<CounterRepository>(context),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget builder(BuildContext context) => CounterBase(
|
||||||
|
fabIncrement: () => bloc(context).increment(),
|
||||||
|
fabIncrementBy10: () => bloc(context).increment(by: 10),
|
||||||
|
fabDecrement: () => bloc(context).decrement(),
|
||||||
|
fabDecrementBy10: () => bloc(context).decrement(by: 10),
|
||||||
|
child: const CounterTextConsumer(),
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
|
||||||
|
import 'package:wyatt_clean_code/presentation/features/counter/blocs/counter_cubit.dart';
|
||||||
|
import 'package:wyatt_clean_code/presentation/features/counter/widgets/counter_text.dart';
|
||||||
|
|
||||||
|
class CounterTextConsumer extends CubitConsumerScreen<CounterCubit, int> {
|
||||||
|
const CounterTextConsumer({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget onBuild(BuildContext context, int state) => CounterText(count: state);
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/extensions/build_context_extension.dart';
|
||||||
|
import 'package:wyatt_clean_code/presentation/shared/layouts/app_default_scaffold.dart';
|
||||||
|
|
||||||
|
class CounterBase extends StatelessWidget {
|
||||||
|
const CounterBase({
|
||||||
|
required this.child,
|
||||||
|
this.fabIncrement,
|
||||||
|
this.fabIncrementBy10,
|
||||||
|
this.fabDecrement,
|
||||||
|
this.fabDecrementBy10,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final void Function()? fabIncrement;
|
||||||
|
final void Function()? fabIncrementBy10;
|
||||||
|
final void Function()? fabDecrement;
|
||||||
|
final void Function()? fabDecrementBy10;
|
||||||
|
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => AppDefaultScaffold(
|
||||||
|
title: Text(context.l10n.counterAppBarTitle),
|
||||||
|
body: Center(
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
fabChildren: [
|
||||||
|
FloatingActionButton(
|
||||||
|
heroTag: 'increment_tag',
|
||||||
|
onPressed: fabIncrement,
|
||||||
|
child: const Icon(Icons.add),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
FloatingActionButton(
|
||||||
|
heroTag: 'increment_10_tag',
|
||||||
|
onPressed: fabIncrementBy10,
|
||||||
|
child: const Text('+10'),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
FloatingActionButton(
|
||||||
|
heroTag: 'decrement_tag',
|
||||||
|
onPressed: fabDecrement,
|
||||||
|
child: const Icon(Icons.remove),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
FloatingActionButton(
|
||||||
|
heroTag: 'decrement_10_tag',
|
||||||
|
onPressed: fabDecrementBy10,
|
||||||
|
child: const Text('-10'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/extensions/build_context_extension.dart';
|
||||||
|
|
||||||
|
class CounterText extends StatelessWidget {
|
||||||
|
const CounterText({
|
||||||
|
required this.count,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final int count;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => Text(
|
||||||
|
context.l10n.youHavePushed(count),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.headline3,
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:wyatt_clean_code/core/extensions/build_context_extension.dart';
|
||||||
|
import 'package:wyatt_clean_code/presentation/features/counter/counter_page.dart';
|
||||||
|
import 'package:wyatt_clean_code/presentation/shared/layouts/app_default_scaffold.dart';
|
||||||
|
|
||||||
|
class InitialPage extends StatelessWidget {
|
||||||
|
const InitialPage({super.key});
|
||||||
|
|
||||||
|
static const String pageName = 'initial';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => AppDefaultScaffold(
|
||||||
|
title: const Text('Wyatt Demo'),
|
||||||
|
body: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
ElevatedButton(
|
||||||
|
child: Text(context.l10n.goToCounter),
|
||||||
|
onPressed: () => context.pushNamed(CounterPage.pageName),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class AppDefaultScaffold extends StatelessWidget {
|
||||||
|
const AppDefaultScaffold({
|
||||||
|
required this.body,
|
||||||
|
this.title,
|
||||||
|
this.fabChildren,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Widget? title;
|
||||||
|
final Widget body;
|
||||||
|
final List<Widget>? fabChildren;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => Scaffold(
|
||||||
|
appBar: AppBar(title: title),
|
||||||
|
body: body,
|
||||||
|
floatingActionButton: (fabChildren?.isNotEmpty ?? false)
|
||||||
|
? Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: fabChildren!,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
# just to keep empty folder in brick generation
|
126
apps/wyatt_clean_code/pubspec.yaml
Normal file
126
apps/wyatt_clean_code/pubspec.yaml
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
name: wyatt_clean_code
|
||||||
|
description: A new Flutter project.
|
||||||
|
|
||||||
|
# The following line prevents the package from being accidentally published to
|
||||||
|
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||||
|
publish_to: "none"
|
||||||
|
|
||||||
|
# The following defines the version and build number for your application.
|
||||||
|
# A version number is three numbers separated by dots, like 1.2.43
|
||||||
|
# followed by an optional build number separated by a +.
|
||||||
|
# Both the version and the builder number may be overridden in flutter
|
||||||
|
# build by specifying --build-name and --build-number, respectively.
|
||||||
|
# In Android, build-name is used as versionName while build-number used as versionCode.
|
||||||
|
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
|
||||||
|
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||||
|
# Read more about iOS versioning at
|
||||||
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
|
version: 1.0.0+1
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ">=2.17.0 <3.0.0"
|
||||||
|
flutter: ">=3.0.0"
|
||||||
|
|
||||||
|
# Dependencies specify other packages that your package needs in order to work.
|
||||||
|
# To automatically upgrade your package dependencies to the latest versions
|
||||||
|
# consider running `flutter pub upgrade --major-versions`. Alternatively,
|
||||||
|
# dependencies can be manually updated by changing the version numbers below to
|
||||||
|
# the latest version available on pub.dev. To see which dependencies have newer
|
||||||
|
# versions available, run `flutter pub outdated`.
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_localizations:
|
||||||
|
sdk: flutter
|
||||||
|
intl: ^0.17.0
|
||||||
|
go_router: ^4.1.1
|
||||||
|
equatable: ^2.0.3
|
||||||
|
freezed_annotation: ^2.1.0
|
||||||
|
json_annotation: ^4.6.0
|
||||||
|
cupertino_icons: ^1.0.5
|
||||||
|
get_it: ^7.2.0
|
||||||
|
logger: ^1.1.0
|
||||||
|
gap: ^2.0.0
|
||||||
|
flutter_bloc: ^8.0.1
|
||||||
|
wyatt_bloc_helper:
|
||||||
|
git:
|
||||||
|
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages
|
||||||
|
ref: bloc/feature/fix_and_repo
|
||||||
|
path: packages/wyatt_bloc_helper
|
||||||
|
wyatt_type_utils:
|
||||||
|
git:
|
||||||
|
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages
|
||||||
|
ref: wyatt_type_utils-v0.0.2
|
||||||
|
path: packages/wyatt_type_utils
|
||||||
|
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
dependency_validator: ^3.2.2
|
||||||
|
|
||||||
|
build_runner: ^2.2.0
|
||||||
|
flutter_gen_runner: ^4.3.0
|
||||||
|
freezed: ^2.1.0+1
|
||||||
|
json_serializable: ^6.3.1
|
||||||
|
|
||||||
|
# The "wyatt_analysis" package below contains a set of recommended lints to
|
||||||
|
# encourage good coding practices. The lint set provided by the package is
|
||||||
|
# activated in the `analysis_options.yaml` file located at the root of your
|
||||||
|
# package. See that file for information about deactivating specific lint
|
||||||
|
# rules and activating additional ones.
|
||||||
|
wyatt_analysis:
|
||||||
|
git:
|
||||||
|
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages
|
||||||
|
ref: wyatt_analysis-v2.2.1
|
||||||
|
path: packages/wyatt_analysis
|
||||||
|
|
||||||
|
# For information on the generic Dart part of this file, see the
|
||||||
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
||||||
|
# The following secion is specific to FlutterGen
|
||||||
|
flutter_gen:
|
||||||
|
colors:
|
||||||
|
inputs:
|
||||||
|
- assets/colors.xml
|
||||||
|
|
||||||
|
# The following section is specific to Flutter packages.
|
||||||
|
flutter:
|
||||||
|
# The following line ensures that the Material Icons font is
|
||||||
|
# included with your application, so that you can use the icons in
|
||||||
|
# the material Icons class.
|
||||||
|
uses-material-design: true
|
||||||
|
|
||||||
|
generate: true
|
||||||
|
|
||||||
|
# To add assets to your application, add an assets section, like this:
|
||||||
|
# assets:
|
||||||
|
# - images/a_dot_burr.jpeg
|
||||||
|
# - images/a_dot_ham.jpeg
|
||||||
|
|
||||||
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
|
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||||
|
|
||||||
|
# For details regarding adding assets from package dependencies, see
|
||||||
|
# https://flutter.dev/assets-and-images/#from-packages
|
||||||
|
|
||||||
|
# To add custom fonts to your application, add a fonts section here,
|
||||||
|
# in this "flutter" section. Each entry in this list should have a
|
||||||
|
# "family" key with the font family name, and a "fonts" key with a
|
||||||
|
# list giving the asset and other descriptors for the font. For
|
||||||
|
# example:
|
||||||
|
# fonts:
|
||||||
|
# - family: Schyler
|
||||||
|
# fonts:
|
||||||
|
# - asset: fonts/Schyler-Regular.ttf
|
||||||
|
# - asset: fonts/Schyler-Italic.ttf
|
||||||
|
# style: italic
|
||||||
|
# - family: Trajan Pro
|
||||||
|
# fonts:
|
||||||
|
# - asset: fonts/TrajanPro.ttf
|
||||||
|
# - asset: fonts/TrajanPro_Bold.ttf
|
||||||
|
# weight: 700
|
||||||
|
#
|
||||||
|
# For details regarding fonts from package dependencies,
|
||||||
|
# see https://flutter.dev/custom-fonts/#from-packages
|
30
apps/wyatt_clean_code/test/widget_test.dart
Normal file
30
apps/wyatt_clean_code/test/widget_test.dart
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// This is a basic Flutter widget test.
|
||||||
|
//
|
||||||
|
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||||
|
// utility in the flutter_test package. For example, you can send tap and scroll
|
||||||
|
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||||
|
// tree, read text, and verify that the values of widget properties are correct.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import 'package:wyatt_clean_code/main.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||||
|
// Build our app and trigger a frame.
|
||||||
|
await tester.pumpWidget(const MyApp());
|
||||||
|
|
||||||
|
// Verify that our counter starts at 0.
|
||||||
|
expect(find.text('0'), findsOneWidget);
|
||||||
|
expect(find.text('1'), findsNothing);
|
||||||
|
|
||||||
|
// Tap the '+' icon and trigger a frame.
|
||||||
|
await tester.tap(find.byIcon(Icons.add));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
// Verify that our counter has incremented.
|
||||||
|
expect(find.text('0'), findsNothing);
|
||||||
|
expect(find.text('1'), findsOneWidget);
|
||||||
|
});
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user