diff --git a/apps/wyatt_clean_code/generate.sh b/apps/wyatt_clean_code/generate.sh index 58ddeb1..ac3f503 100755 --- a/apps/wyatt_clean_code/generate.sh +++ b/apps/wyatt_clean_code/generate.sh @@ -3,6 +3,7 @@ make clean rm -rf .idea rm -f wyatt_clean_code.iml +rm .fvm/flutter_sdk cd ../../ echo "Brick generator..." dart tools/brick_generator/bin/brick_generator.dart wyatt_clean_code wyatt_clean_code wyatt-clean-code "Wyatt Demo" com.example.wyatt_clean_code @@ -17,4 +18,5 @@ mv -f bricks/wyatt_clean_code/__brick__/ios/ bricks/wyatt_clean_code/__brick__/{ mkdir bricks/wyatt_clean_code/__brick__/{{#enable_web}}web\{\{ mv -f bricks/wyatt_clean_code/__brick__/web/ bricks/wyatt_clean_code/__brick__/{{#enable_web}}web{{/enable_web}} -rm bricks/wyatt_clean_code/__brick__/generate.sh \ No newline at end of file +rm bricks/wyatt_clean_code/__brick__/generate.sh +rm -rf bricks/wyatt_clean_code/__brick__/.fvm/flutter_sdk \ No newline at end of file diff --git a/bricks/wyatt_clean_code/__brick__/.gitignore b/bricks/wyatt_clean_code/__brick__/.gitignore new file mode 100644 index 0000000..a8e938c --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/.gitignore @@ -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 diff --git a/bricks/wyatt_clean_code/__brick__/.metadata b/bricks/wyatt_clean_code/__brick__/.metadata new file mode 100644 index 0000000..2112298 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/.metadata @@ -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: web + 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' diff --git a/bricks/wyatt_clean_code/__brick__/.vscode/launch.json b/bricks/wyatt_clean_code/__brick__/.vscode/launch.json index 96763cd..40a2104 100644 --- a/bricks/wyatt_clean_code/__brick__/.vscode/launch.json +++ b/bricks/wyatt_clean_code/__brick__/.vscode/launch.json @@ -5,21 +5,121 @@ "version": "0.2.0", "configurations": [ { - "name": "{{#snakeCase}}{{project_name}}{{/snakeCase}}", - "request": "launch", - "type": "dart" - }, - { - "name": "{{#snakeCase}}{{project_name}}{{/snakeCase}} (profile mode)", + "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": "{{#snakeCase}}{{project_name}}{{/snakeCase}} (release mode)", + "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" + }, ] } \ No newline at end of file diff --git a/bricks/wyatt_clean_code/__brick__/.vscode/settings.json b/bricks/wyatt_clean_code/__brick__/.vscode/settings.json index 855293b..fa6f612 100644 --- a/bricks/wyatt_clean_code/__brick__/.vscode/settings.json +++ b/bricks/wyatt_clean_code/__brick__/.vscode/settings.json @@ -1,4 +1,5 @@ { + "dart.flutterSdkPath": ".fvm/flutter_sdk", "bloc.newCubitTemplate.type": "equatable", "psi-header.config": { "blankLinesAfter": 0, diff --git a/bricks/wyatt_clean_code/__brick__/Makefile b/bricks/wyatt_clean_code/__brick__/Makefile index f46b81c..a9a3ef9 100644 --- a/bricks/wyatt_clean_code/__brick__/Makefile +++ b/bricks/wyatt_clean_code/__brick__/Makefile @@ -1,4 +1,4 @@ -.PHONY: help clean get upgrade format lint gen +.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. @@ -33,6 +33,22 @@ lint: ## Lints the code. @echo "• Verifying code..." @dart analyze . || (echo "Error in project"; exit 1) -gen: get ## Run build_runner (Freezed, Fluttergen, etc...) - @echo "• Running build_runner scripts" - @flutter pub run build_runner build \ No newline at end of file +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 \ No newline at end of file diff --git a/bricks/wyatt_clean_code/__brick__/README.md b/bricks/wyatt_clean_code/__brick__/README.md new file mode 100644 index 0000000..4136204 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/README.md @@ -0,0 +1,16 @@ +# {{#snakeCase}}{{project_name}}{{/snakeCase}} + +{{{description}}} + +## 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. diff --git a/bricks/wyatt_clean_code/__brick__/analysis_options.yaml b/bricks/wyatt_clean_code/__brick__/analysis_options.yaml index d00274d..9bdb566 100644 --- a/bricks/wyatt_clean_code/__brick__/analysis_options.yaml +++ b/bricks/wyatt_clean_code/__brick__/analysis_options.yaml @@ -1,9 +1,35 @@ -# {{#enable_analysis}} -include: package:wyatt_analysis/analysis_options.flutter.experimental.yaml +# 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`. -analyzer: - strong-mode: - implicit-dynamic: true -# {{/enable_analysis}}{{^enable_analysis}} -include: package:flutter_lints/flutter.yaml -# {{/enable_analysis}} \ No newline at end of file +# 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 diff --git a/bricks/wyatt_clean_code/__brick__/assets/colors.xml b/bricks/wyatt_clean_code/__brick__/assets/colors.xml new file mode 100644 index 0000000..f5e9c9a --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/assets/colors.xml @@ -0,0 +1,61 @@ + + + #FF2196F3 + + #FF0061A6 + #FFFFFFFF + #FFD0E4FF + #FF001D36 + + #FF535F70 + #FFFFFFFF + #FFD6E3F7 + #FF101C2B + + #FFBA1B1B + #FFFFFFFF + #FFFFDAD4 + #FF410001 + + #FFFDFCFF + #FF1B1B1B + #FFFDFCFF + #FF1B1B1B + #FFDFE2EB + #FF42474E + #FF73777F + #FF000000 + + #FF2F3033 + #FFF1F0F4 + #FF9CCAFF + + + #FF9CCAFF + #FF00325A + #FF00497F + #FFD0E4FF + + #FFBBC8DB + #FF253140 + #FF3C4858 + #FFD6E3F7 + + #FFFFB4A9 + #FF680003 + #FF930006 + #FFFFB4A9 + + #FF1B1B1B + #FFE2E2E6 + #FF1B1B1B + #FFE2E2E6 + #FF42474E + #FFC3C7D0 + #FF8D9199 + #FF000000 + + #FFE2E2E6 + #FF2F3033 + #FF0061A6 + \ No newline at end of file diff --git a/bricks/wyatt_clean_code/__brick__/assets/colors/dark.xml b/bricks/wyatt_clean_code/__brick__/assets/colors/dark.xml deleted file mode 100644 index 76afbe8..0000000 --- a/bricks/wyatt_clean_code/__brick__/assets/colors/dark.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - #FFFFFF - #000000 - #EEEEEE - #979797 - #CF2A2A - #DF9527 - \ No newline at end of file diff --git a/bricks/wyatt_clean_code/__brick__/assets/fonts/.gitkeep b/bricks/wyatt_clean_code/__brick__/assets/fonts/.gitkeep index e69de29..f94cb6f 100644 --- a/bricks/wyatt_clean_code/__brick__/assets/fonts/.gitkeep +++ b/bricks/wyatt_clean_code/__brick__/assets/fonts/.gitkeep @@ -0,0 +1 @@ +# just to keep empty folder in brick generation \ No newline at end of file diff --git a/bricks/wyatt_clean_code/__brick__/assets/images/.gitkeep b/bricks/wyatt_clean_code/__brick__/assets/images/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/bricks/wyatt_clean_code/__brick__/assets/images/wyatt-studio-logo.png b/bricks/wyatt_clean_code/__brick__/assets/images/wyatt-studio-logo.png new file mode 100644 index 0000000..322633e Binary files /dev/null and b/bricks/wyatt_clean_code/__brick__/assets/images/wyatt-studio-logo.png differ diff --git a/bricks/wyatt_clean_code/__brick__/assets/{{#enable_l10n}}l10n{{/enable_l10n}}/intl_fr.arb b/bricks/wyatt_clean_code/__brick__/assets/l10n/intl_fr.arb similarity index 54% rename from bricks/wyatt_clean_code/__brick__/assets/{{#enable_l10n}}l10n{{/enable_l10n}}/intl_fr.arb rename to bricks/wyatt_clean_code/__brick__/assets/l10n/intl_fr.arb index 1a6db0f..b95f76d 100644 --- a/bricks/wyatt_clean_code/__brick__/assets/{{#enable_l10n}}l10n{{/enable_l10n}}/intl_fr.arb +++ b/bricks/wyatt_clean_code/__brick__/assets/l10n/intl_fr.arb @@ -4,6 +4,15 @@ "@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" diff --git a/bricks/wyatt_clean_code/__brick__/lib/app.dart b/bricks/wyatt_clean_code/__brick__/lib/app.dart deleted file mode 100644 index cb21a0e..0000000 --- a/bricks/wyatt_clean_code/__brick__/lib/app.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:{{project_name.snakeCase()}}/widget_tree.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -{{#enable_auth}}import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';{{/enable_auth}} - -class App extends StatelessWidget { - const App({super.key}); - - @override - Widget build(BuildContext context) { - // Data providers - // ... - // return MultiRepositoryProvider( - // providers: [ - // // Repositories - // ], - // child: MultiBlocProvider( - // providers: [ - // // Toplevel Blocs and Cubits - // ], - // child: const WidgetTree(), - // ), - // ); - return const WidgetTree(); - } -} diff --git a/bricks/wyatt_clean_code/__brick__/lib/bootstrap.dart b/bricks/wyatt_clean_code/__brick__/lib/bootstrap.dart new file mode 100644 index 0000000..55dec5e --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/bootstrap.dart @@ -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:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/dependency_injection/get_it.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/flavors/flavor_settings.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/utils/app_bloc_observer.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/utils/wyatt_printer.dart'; + +Future bootstrap(FutureOr 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, + ), + ); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/constants/.gitkeep b/bricks/wyatt_clean_code/__brick__/lib/core/constants/.gitkeep new file mode 100644 index 0000000..f94cb6f --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/core/constants/.gitkeep @@ -0,0 +1 @@ +# just to keep empty folder in brick generation \ No newline at end of file diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/dependency_injection/get_it.dart b/bricks/wyatt_clean_code/__brick__/lib/core/dependency_injection/get_it.dart new file mode 100644 index 0000000..7180ebb --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/core/dependency_injection/get_it.dart @@ -0,0 +1,15 @@ +import 'dart:async'; + +import 'package:get_it/get_it.dart'; + +final getIt = GetIt.I; + +abstract class GetItInitializer { + static Future init() async { + // Here, register data sources + } + + static void run() { + unawaited(init()); + } +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/design_system/colors.dart b/bricks/wyatt_clean_code/__brick__/lib/core/design_system/colors.dart new file mode 100644 index 0000000..a4ea53c --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/core/design_system/colors.dart @@ -0,0 +1,2 @@ +/// Generate colors with `flutter pub run build_runner build` +export 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/gen/colors.gen.dart'; diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/design_system/sizing.dart b/bricks/wyatt_clean_code/__brick__/lib/core/design_system/sizing.dart new file mode 100644 index 0000000..1c4fdc0 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/core/design_system/sizing.dart @@ -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); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/design_system/theme.dart b/bricks/wyatt_clean_code/__brick__/lib/core/design_system/theme.dart new file mode 100644 index 0000000..4903272 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/core/design_system/theme.dart @@ -0,0 +1,240 @@ +import 'package:flutter/material.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/design_system/colors.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/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, + ); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/design_system/typography.dart b/bricks/wyatt_clean_code/__brick__/lib/core/design_system/typography.dart new file mode 100644 index 0000000..ce1b241 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/core/design_system/typography.dart @@ -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, + ); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/enums/.gitkeep b/bricks/wyatt_clean_code/__brick__/lib/core/enums/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/enums/exception_type.dart b/bricks/wyatt_clean_code/__brick__/lib/core/enums/exception_type.dart new file mode 100644 index 0000000..decb960 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/core/enums/exception_type.dart @@ -0,0 +1,7 @@ +enum AppExceptionType { + network, + api, + database, + cache, + assertion, +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/enums/flavor.dart b/bricks/wyatt_clean_code/__brick__/lib/core/enums/flavor.dart new file mode 100644 index 0000000..2cec6bb --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/core/enums/flavor.dart @@ -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); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/errors/.gitkeep b/bricks/wyatt_clean_code/__brick__/lib/core/errors/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/errors/exceptions.dart b/bricks/wyatt_clean_code/__brick__/lib/core/errors/exceptions.dart new file mode 100644 index 0000000..edda147 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/core/errors/exceptions.dart @@ -0,0 +1,29 @@ +import 'package:equatable/equatable.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/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 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()}'; +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/extensions/.gitkeep b/bricks/wyatt_clean_code/__brick__/lib/core/extensions/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/extensions/{{#enable_l10n}}app_localizations_x.dart{{/enable_l10n}} b/bricks/wyatt_clean_code/__brick__/lib/core/extensions/build_context_extension.dart similarity index 83% rename from bricks/wyatt_clean_code/__brick__/lib/core/extensions/{{#enable_l10n}}app_localizations_x.dart{{/enable_l10n}} rename to bricks/wyatt_clean_code/__brick__/lib/core/extensions/build_context_extension.dart index 17c891b..df68dfe 100644 --- a/bricks/wyatt_clean_code/__brick__/lib/core/extensions/{{#enable_l10n}}app_localizations_x.dart{{/enable_l10n}} +++ b/bricks/wyatt_clean_code/__brick__/lib/core/extensions/build_context_extension.dart @@ -3,6 +3,6 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; export 'package:flutter_gen/gen_l10n/app_localizations.dart'; -extension AppLocalizationsX on BuildContext { +extension BuildContextX on BuildContext { AppLocalizations get l10n => AppLocalizations.of(this); } diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/extensions/num_extension.dart b/bricks/wyatt_clean_code/__brick__/lib/core/extensions/num_extension.dart new file mode 100644 index 0000000..5bf9b90 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/core/extensions/num_extension.dart @@ -0,0 +1,9 @@ +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/utils/screen_util.dart'; + +extension NumX on num { + double get w => ScreenUtil().setWidth(this); + + double get h => ScreenUtil().setHeight(this); + + double get sp => ScreenUtil().setSp(this); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/extensions/object_extension.dart b/bricks/wyatt_clean_code/__brick__/lib/core/extensions/object_extension.dart new file mode 100644 index 0000000..5d1be48 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/core/extensions/object_extension.dart @@ -0,0 +1,32 @@ +import 'package:logger/logger.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/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); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/flavors/flavor_settings.dart b/bricks/wyatt_clean_code/__brick__/lib/core/flavors/flavor_settings.dart new file mode 100644 index 0000000..f1a9e1c --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/core/flavors/flavor_settings.dart @@ -0,0 +1,64 @@ +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/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; + } + } +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/mixins/local_data_source.dart b/bricks/wyatt_clean_code/__brick__/lib/core/mixins/local_data_source.dart new file mode 100644 index 0000000..ea9f71b --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/core/mixins/local_data_source.dart @@ -0,0 +1,6 @@ +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/domain/data_sources/local/base_local_data_source.dart'; + +mixin LocalDataSource { + /// Offline data source, for debug or cache + Local get localDataSource; +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/mixins/remote_data_source.dart b/bricks/wyatt_clean_code/__brick__/lib/core/mixins/remote_data_source.dart new file mode 100644 index 0000000..dad8ccd --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/core/mixins/remote_data_source.dart @@ -0,0 +1,6 @@ +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/domain/data_sources/remote/base_remote_data_source.dart'; + +mixin RemoteDataSource { + /// Online data source, to provide data through API + Remote get remoteDataSource; +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/resources/app_theme.dart b/bricks/wyatt_clean_code/__brick__/lib/core/resources/app_theme.dart deleted file mode 100644 index 347cde2..0000000 --- a/bricks/wyatt_clean_code/__brick__/lib/core/resources/app_theme.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/gen/colors.gen.dart'; -import 'package:flutter/material.dart'; - -abstract class AppTheme { - static ThemeData defaultTheme = ThemeData( - primaryColor: ColorName.crimsonRed - ); -} diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/resources/{{#enable_router}}app_router.dart{{/enable_router}} b/bricks/wyatt_clean_code/__brick__/lib/core/resources/{{#enable_router}}app_router.dart{{/enable_router}} deleted file mode 100644 index 36f16da..0000000 --- a/bricks/wyatt_clean_code/__brick__/lib/core/resources/{{#enable_router}}app_router.dart{{/enable_router}} +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:{{project_name.snakeCase()}}/presentation/pages/initial/initial_page.dart'; -import 'package:{{project_name.snakeCase()}}/presentation/pages/counter/counter_page.dart'; -import 'package:go_router/go_router.dart'; -import 'package:flutter/material.dart'; - -abstract class AppRouter { - static const String initial = 'initial'; - static const String counter = 'counter'; - - static final List routes = [ - GoRoute( - path: '/', - name: initial, - pageBuilder: (context, state) => MaterialPage( - key: state.pageKey, - child: const InitialPage(), - ), - ), - GoRoute( - path: '/counter', - name: counter, - pageBuilder: (context, state) => MaterialPage( - key: state.pageKey, - child: const CounterPage(), - ), - ), - ]; -} diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/routes/router.dart b/bricks/wyatt_clean_code/__brick__/lib/core/routes/router.dart new file mode 100644 index 0000000..0cb4ac6 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/core/routes/router.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/presentation/features/counter/counter_page.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/presentation/features/initial/initial_page.dart'; + +abstract class AppRouter { + static Page defaultTransition( + BuildContext context, + GoRouterState state, + Widget child, + ) => + MaterialPage( + key: state.pageKey, + child: child, + ); + + static final List 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(), + ), + ), + ]; +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/usecases/usecase.dart b/bricks/wyatt_clean_code/__brick__/lib/core/usecases/usecase.dart new file mode 100644 index 0000000..4dd8ab6 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/core/usecases/usecase.dart @@ -0,0 +1,13 @@ +import 'package:equatable/equatable.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/errors/exceptions.dart'; +import 'package:wyatt_type_utils/wyatt_type_utils.dart'; + +// ignore: one_member_abstracts +abstract class UseCase { + Future> call(Params params); +} + +class NoParams extends Equatable { + @override + List get props => []; +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/utils/app_bloc_observer.dart b/bricks/wyatt_clean_code/__brick__/lib/core/utils/app_bloc_observer.dart index bd22fa9..5680f35 100644 --- a/bricks/wyatt_clean_code/__brick__/lib/core/utils/app_bloc_observer.dart +++ b/bricks/wyatt_clean_code/__brick__/lib/core/utils/app_bloc_observer.dart @@ -1,5 +1,6 @@ -import 'package:bloc/bloc.dart'; -import 'package:flutter/foundation.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:logger/logger.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/extensions/object_extension.dart'; class AppBlocObserver extends BlocObserver { final bool printEvent; @@ -7,6 +8,8 @@ class AppBlocObserver extends BlocObserver { final bool printChange; final bool printTransition; + final Logger logger = Logger(printer: SimplePrinter()); + AppBlocObserver({ this.printEvent = true, this.printError = true, @@ -18,14 +21,16 @@ class AppBlocObserver extends BlocObserver { void onEvent(Bloc bloc, Object? event) { super.onEvent(bloc, event); if (printEvent) { - debugPrint(event.toString()); + event?.d(wrap: (obj) => 'onEvent $event'); } } @override void onError(BlocBase bloc, Object error, StackTrace stackTrace) { if (printError) { - debugPrint(error.toString()); + error.e( + wrap: (obj) => 'onError(${bloc.runtimeType}, $obj, $stackTrace)', + ); } super.onError(bloc, error, stackTrace); } @@ -34,7 +39,7 @@ class AppBlocObserver extends BlocObserver { void onChange(BlocBase bloc, Change change) { super.onChange(bloc, change); if (printChange) { - debugPrint(change.toString()); + change.d(wrap: (obj) => 'onChange(${bloc.runtimeType}, $obj)'); } } @@ -45,7 +50,7 @@ class AppBlocObserver extends BlocObserver { ) { super.onTransition(bloc, transition); if (printTransition) { - debugPrint(transition.toString()); + transition.d(wrap: (obj) => 'onTransition $obj'); } } -} \ No newline at end of file +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/utils/screen_util.dart b/bricks/wyatt_clean_code/__brick__/lib/core/utils/screen_util.dart new file mode 100644 index 0000000..8f5656a --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/core/utils/screen_util.dart @@ -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)); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/core/utils/wyatt_printer.dart b/bricks/wyatt_clean_code/__brick__/lib/core/utils/wyatt_printer.dart new file mode 100644 index 0000000..9ceeb38 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/core/utils/wyatt_printer.dart @@ -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 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(); + } + } +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/data/data_sources/local/.gitkeep b/bricks/wyatt_clean_code/__brick__/lib/data/data_sources/local/.gitkeep new file mode 100644 index 0000000..f94cb6f --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/data/data_sources/local/.gitkeep @@ -0,0 +1 @@ +# just to keep empty folder in brick generation \ No newline at end of file diff --git a/bricks/wyatt_clean_code/__brick__/lib/data/data_sources/remote/.gitkeep b/bricks/wyatt_clean_code/__brick__/lib/data/data_sources/remote/.gitkeep new file mode 100644 index 0000000..f94cb6f --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/data/data_sources/remote/.gitkeep @@ -0,0 +1 @@ +# just to keep empty folder in brick generation \ No newline at end of file diff --git a/bricks/wyatt_clean_code/__brick__/lib/data/models/.gitkeep b/bricks/wyatt_clean_code/__brick__/lib/data/models/.gitkeep index e69de29..f94cb6f 100644 --- a/bricks/wyatt_clean_code/__brick__/lib/data/models/.gitkeep +++ b/bricks/wyatt_clean_code/__brick__/lib/data/models/.gitkeep @@ -0,0 +1 @@ +# just to keep empty folder in brick generation \ No newline at end of file diff --git a/bricks/wyatt_clean_code/__brick__/lib/data/providers/.gitkeep b/bricks/wyatt_clean_code/__brick__/lib/data/providers/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/bricks/wyatt_clean_code/__brick__/lib/data/repositories/.gitkeep b/bricks/wyatt_clean_code/__brick__/lib/data/repositories/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/bricks/wyatt_clean_code/__brick__/lib/data/repositories/counter_repository_impl.dart b/bricks/wyatt_clean_code/__brick__/lib/data/repositories/counter_repository_impl.dart new file mode 100644 index 0000000..d6a5453 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/data/repositories/counter_repository_impl.dart @@ -0,0 +1,24 @@ +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/enums/exception_type.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/errors/exceptions.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/domain/repositories/counter_repository.dart'; +import 'package:wyatt_type_utils/wyatt_type_utils.dart'; + +class CounterRepositoryImpl implements CounterRepository { + Result _check(int value) => + Result.conditionalLazy( + value >= 0, + () => value, + () => ClientException( + AppExceptionType.assertion, + "Counter can't be negative!", + ), + ); + + @override + Future> decrement(int newState) async => + _check(newState); + + @override + Future> increment(int newState) async => + _check(newState); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/domain/data_sources/base_data_source.dart b/bricks/wyatt_clean_code/__brick__/lib/domain/data_sources/base_data_source.dart new file mode 100644 index 0000000..05f1202 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/domain/data_sources/base_data_source.dart @@ -0,0 +1 @@ +abstract class BaseDataSource {} diff --git a/bricks/wyatt_clean_code/__brick__/lib/domain/data_sources/local/base_local_data_source.dart b/bricks/wyatt_clean_code/__brick__/lib/domain/data_sources/local/base_local_data_source.dart new file mode 100644 index 0000000..05473f8 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/domain/data_sources/local/base_local_data_source.dart @@ -0,0 +1,3 @@ +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/domain/data_sources/base_data_source.dart'; + +abstract class BaseLocalDataSource extends BaseDataSource {} diff --git a/bricks/wyatt_clean_code/__brick__/lib/domain/data_sources/remote/base_remote_data_source.dart b/bricks/wyatt_clean_code/__brick__/lib/domain/data_sources/remote/base_remote_data_source.dart new file mode 100644 index 0000000..ffb2f05 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/domain/data_sources/remote/base_remote_data_source.dart @@ -0,0 +1,3 @@ +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/domain/data_sources/base_data_source.dart'; + +abstract class BaseRemoteDataSource extends BaseDataSource {} diff --git a/bricks/wyatt_clean_code/__brick__/lib/domain/entities/.gitkeep b/bricks/wyatt_clean_code/__brick__/lib/domain/entities/.gitkeep index e69de29..f94cb6f 100644 --- a/bricks/wyatt_clean_code/__brick__/lib/domain/entities/.gitkeep +++ b/bricks/wyatt_clean_code/__brick__/lib/domain/entities/.gitkeep @@ -0,0 +1 @@ +# just to keep empty folder in brick generation \ No newline at end of file diff --git a/bricks/wyatt_clean_code/__brick__/lib/domain/repositories/.gitkeep b/bricks/wyatt_clean_code/__brick__/lib/domain/repositories/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/bricks/wyatt_clean_code/__brick__/lib/domain/repositories/base_repository.dart b/bricks/wyatt_clean_code/__brick__/lib/domain/repositories/base_repository.dart new file mode 100644 index 0000000..2b88f48 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/domain/repositories/base_repository.dart @@ -0,0 +1 @@ +abstract class BaseRepository {} diff --git a/bricks/wyatt_clean_code/__brick__/lib/domain/repositories/counter_repository.dart b/bricks/wyatt_clean_code/__brick__/lib/domain/repositories/counter_repository.dart new file mode 100644 index 0000000..1db2339 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/domain/repositories/counter_repository.dart @@ -0,0 +1,8 @@ +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/errors/exceptions.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/domain/repositories/base_repository.dart'; +import 'package:wyatt_type_utils/wyatt_type_utils.dart'; + +abstract class CounterRepository extends BaseRepository { + Future> increment(int newState); + Future> decrement(int newState); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/domain/usecases/counter/decrement_counter.dart b/bricks/wyatt_clean_code/__brick__/lib/domain/usecases/counter/decrement_counter.dart new file mode 100644 index 0000000..4a625d7 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/domain/usecases/counter/decrement_counter.dart @@ -0,0 +1,16 @@ +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/errors/exceptions.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/usecases/usecase.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/domain/repositories/counter_repository.dart'; +import 'package:wyatt_type_utils/wyatt_type_utils.dart'; + +class DecrementCounter extends UseCase { + final CounterRepository counterRepository; + + DecrementCounter({ + required this.counterRepository, + }); + + @override + Future> call(int params) async => + counterRepository.decrement(params); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/domain/usecases/counter/increment_counter.dart b/bricks/wyatt_clean_code/__brick__/lib/domain/usecases/counter/increment_counter.dart new file mode 100644 index 0000000..862eed8 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/domain/usecases/counter/increment_counter.dart @@ -0,0 +1,16 @@ +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/errors/exceptions.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/usecases/usecase.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/domain/repositories/counter_repository.dart'; +import 'package:wyatt_type_utils/wyatt_type_utils.dart'; + +class IncrementCounter extends UseCase { + final CounterRepository counterRepository; + + IncrementCounter({ + required this.counterRepository, + }); + + @override + Future> call(int params) async => + counterRepository.increment(params); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/gen/assets.gen.dart b/bricks/wyatt_clean_code/__brick__/lib/gen/assets.gen.dart new file mode 100644 index 0000000..00c169e --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/gen/assets.gen.dart @@ -0,0 +1,87 @@ +/// 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/widgets.dart'; + +class $AssetsImagesGen { + const $AssetsImagesGen(); + + /// File path: assets/images/wyatt-studio-logo.png + AssetGenImage get wyattStudioLogo => + const AssetGenImage('assets/images/wyatt-studio-logo.png'); +} + +class Assets { + Assets._(); + + static const $AssetsImagesGen images = $AssetsImagesGen(); +} + +class AssetGenImage { + const AssetGenImage(this._assetName); + + final String _assetName; + + Image image({ + Key? key, + AssetBundle? bundle, + ImageFrameBuilder? frameBuilder, + ImageErrorWidgetBuilder? errorBuilder, + String? semanticLabel, + bool excludeFromSemantics = false, + double? scale, + double? width, + double? height, + Color? color, + Animation? opacity, + BlendMode? colorBlendMode, + BoxFit? fit, + AlignmentGeometry alignment = Alignment.center, + ImageRepeat repeat = ImageRepeat.noRepeat, + Rect? centerSlice, + bool matchTextDirection = false, + bool gaplessPlayback = false, + bool isAntiAlias = false, + String? package, + FilterQuality filterQuality = FilterQuality.low, + int? cacheWidth, + int? cacheHeight, + }) { + return Image.asset( + _assetName, + key: key, + bundle: bundle, + frameBuilder: frameBuilder, + errorBuilder: errorBuilder, + semanticLabel: semanticLabel, + excludeFromSemantics: excludeFromSemantics, + scale: scale, + width: width, + height: height, + color: color, + opacity: opacity, + colorBlendMode: colorBlendMode, + fit: fit, + alignment: alignment, + repeat: repeat, + centerSlice: centerSlice, + matchTextDirection: matchTextDirection, + gaplessPlayback: gaplessPlayback, + isAntiAlias: isAntiAlias, + package: package, + filterQuality: filterQuality, + cacheWidth: cacheWidth, + cacheHeight: cacheHeight, + ); + } + + String get path => _assetName; + + String get keyName => _assetName; +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/gen/colors.gen.dart b/bricks/wyatt_clean_code/__brick__/lib/gen/colors.gen.dart new file mode 100644 index 0000000..5693c5c --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/gen/colors.gen.dart @@ -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, + { + 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, + { + 100: Color(0xFFFFFFFF), + 200: Color(0xFFFFFFFF), + 400: Color(0xFFFFFFFF), + 700: Color(0xFFFFFFFF), + }, + ); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/main.dart b/bricks/wyatt_clean_code/__brick__/lib/main.dart deleted file mode 100644 index 61831dd..0000000 --- a/bricks/wyatt_clean_code/__brick__/lib/main.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:{{project_name.snakeCase()}}/app.dart'; -import 'package:{{project_name.snakeCase()}}/core/utils/app_bloc_observer.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -{{#enable_router}}import 'package:go_router/go_router.dart';{{/enable_router}} - -Future main() async { - WidgetsFlutterBinding.ensureInitialized(); - - {{#enable_router}}GoRouter.setUrlPathStrategy(UrlPathStrategy.path);{{/enable_router}} - - BlocOverrides.runZoned( - () => runApp( - const App(), - ), - blocObserver: AppBlocObserver(), - ); -} diff --git a/bricks/wyatt_clean_code/__brick__/lib/main_development.dart b/bricks/wyatt_clean_code/__brick__/lib/main_development.dart new file mode 100644 index 0000000..e665c91 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/main_development.dart @@ -0,0 +1,8 @@ +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/bootstrap.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/flavors/flavor_settings.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/presentation/features/app/app.dart'; + +void main(List args) { + FlavorSettings.development(); + bootstrap(App.new); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/main_production.dart b/bricks/wyatt_clean_code/__brick__/lib/main_production.dart new file mode 100644 index 0000000..cb80a75 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/main_production.dart @@ -0,0 +1,8 @@ +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/bootstrap.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/flavors/flavor_settings.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/presentation/features/app/app.dart'; + +void main(List args) { + FlavorSettings.production(); + bootstrap(App.new); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/main_staging.dart b/bricks/wyatt_clean_code/__brick__/lib/main_staging.dart new file mode 100644 index 0000000..6be7658 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/main_staging.dart @@ -0,0 +1,8 @@ +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/bootstrap.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/flavors/flavor_settings.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/presentation/features/app/app.dart'; + +void main(List args) { + FlavorSettings.staging(); + bootstrap(App.new); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/presentation/bloc/counter_cubit/counter_cubit.dart b/bricks/wyatt_clean_code/__brick__/lib/presentation/bloc/counter_cubit/counter_cubit.dart deleted file mode 100644 index 70bd952..0000000 --- a/bricks/wyatt_clean_code/__brick__/lib/presentation/bloc/counter_cubit/counter_cubit.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:bloc/bloc.dart'; - -class CounterCubit extends Cubit { - CounterCubit() : super(0); - - void increment() => emit(state + 1); - void decrement() => emit(state - 1); -} diff --git a/bricks/wyatt_clean_code/__brick__/lib/presentation/features/app/app.dart b/bricks/wyatt_clean_code/__brick__/lib/presentation/features/app/app.dart new file mode 100644 index 0000000..67811c0 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/presentation/features/app/app.dart @@ -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:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/design_system/theme.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/extensions/build_context_extension.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/flavors/flavor_settings.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/routes/router.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/utils/screen_util.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/data/repositories/counter_repository_impl.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/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( + lazy: true, + create: (context) => CounterRepositoryImpl(), + ), + ], + child: _bannerFlavor( + MaterialApp.router( + title: '{{#titleCase}}{{project_name}}{{/titleCase}}', + 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, + ), + ), + ); + } +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/presentation/features/counter/blocs/counter_cubit.dart b/bricks/wyatt_clean_code/__brick__/lib/presentation/features/counter/blocs/counter_cubit.dart new file mode 100644 index 0000000..ee727f6 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/presentation/features/counter/blocs/counter_cubit.dart @@ -0,0 +1,39 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/domain/usecases/counter/decrement_counter.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/domain/usecases/counter/increment_counter.dart'; + +class CounterCubit extends Cubit { + final IncrementCounter _incrementCounter; + final DecrementCounter _decrementCounter; + + CounterCubit({ + required IncrementCounter incrementCounter, + required DecrementCounter decrementCounter, + }) : _incrementCounter = incrementCounter, + _decrementCounter = decrementCounter, + super(0); + + Future 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 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, + ), + ); + } +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/presentation/features/counter/counter_page.dart b/bricks/wyatt_clean_code/__brick__/lib/presentation/features/counter/counter_page.dart new file mode 100644 index 0000000..e13e08d --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/presentation/features/counter/counter_page.dart @@ -0,0 +1,11 @@ +import 'package:flutter/material.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/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(); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/presentation/features/counter/state_management/counter_page_provider.dart b/bricks/wyatt_clean_code/__brick__/lib/presentation/features/counter/state_management/counter_page_provider.dart new file mode 100644 index 0000000..d7e8019 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/presentation/features/counter/state_management/counter_page_provider.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/domain/repositories/counter_repository.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/domain/usecases/counter/decrement_counter.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/domain/usecases/counter/increment_counter.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/presentation/features/counter/blocs/counter_cubit.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/presentation/features/counter/state_management/counter_text_consumer.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/presentation/features/counter/widgets/counter_base.dart'; + +class CounterPageProvider extends CubitProviderScreen { + const CounterPageProvider({super.key}); + + @override + CounterCubit create(BuildContext context) => CounterCubit( + decrementCounter: DecrementCounter( + counterRepository: repo(context), + ), + incrementCounter: IncrementCounter( + counterRepository: repo(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(), + ); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/presentation/features/counter/state_management/counter_text_consumer.dart b/bricks/wyatt_clean_code/__brick__/lib/presentation/features/counter/state_management/counter_text_consumer.dart new file mode 100644 index 0000000..0c42db3 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/presentation/features/counter/state_management/counter_text_consumer.dart @@ -0,0 +1,11 @@ +import 'package:flutter/widgets.dart'; +import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/presentation/features/counter/blocs/counter_cubit.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/presentation/features/counter/widgets/counter_text.dart'; + +class CounterTextConsumer extends CubitConsumerScreen { + const CounterTextConsumer({super.key}); + + @override + Widget onBuild(BuildContext context, int state) => CounterText(count: state); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/presentation/features/counter/widgets/counter_base.dart b/bricks/wyatt_clean_code/__brick__/lib/presentation/features/counter/widgets/counter_base.dart new file mode 100644 index 0000000..a392410 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/presentation/features/counter/widgets/counter_base.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/extensions/build_context_extension.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/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'), + ), + ], + ); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/presentation/features/counter/widgets/counter_text.dart b/bricks/wyatt_clean_code/__brick__/lib/presentation/features/counter/widgets/counter_text.dart new file mode 100644 index 0000000..fee1643 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/presentation/features/counter/widgets/counter_text.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/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, + ); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/presentation/features/initial/initial_page.dart b/bricks/wyatt_clean_code/__brick__/lib/presentation/features/initial/initial_page.dart new file mode 100644 index 0000000..2e65908 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/presentation/features/initial/initial_page.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/design_system/sizing.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/extensions/build_context_extension.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/core/extensions/num_extension.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/gen/assets.gen.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/presentation/features/counter/counter_page.dart'; +import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/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('{{#titleCase}}{{project_name}}{{/titleCase}}'), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Assets.images.wyattStudioLogo.image(width: 150.w), + AppSizing.lGap, + ElevatedButton( + child: Text(context.l10n.goToCounter), + onPressed: () => context.pushNamed(CounterPage.pageName), + ), + ], + ), + ), + ); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/presentation/pages/counter/counter_page.dart b/bricks/wyatt_clean_code/__brick__/lib/presentation/pages/counter/counter_page.dart deleted file mode 100644 index 91be000..0000000 --- a/bricks/wyatt_clean_code/__brick__/lib/presentation/pages/counter/counter_page.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/presentation/bloc/counter_cubit/counter_cubit.dart'; -import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/presentation/shared/state_management/counter_state_management.dart'; -{{#enable_l10n}}import 'package:{{project_name.snakeCase()}}/core/extensions/app_localizations_x.dart';{{/enable_l10n}} -import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/presentation/pages/counter/widgets/counter_text.dart'; - -class CounterPage extends StatelessWidget { - const CounterPage({super.key}); - - @override - Widget build(BuildContext context) { - return CounterStateManagement( - builder: (BuildContext context, int state) { - return Scaffold( - {{#enable_l10n}}appBar: AppBar(title: Text(context.l10n.counterAppBarTitle)),{{/enable_l10n}} - {{^enable_l10n}}appBar: AppBar(title: Text('Counter')),{{/enable_l10n}} - body: const Center(child: CounterText()), - floatingActionButton: Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - FloatingActionButton( - heroTag: 'increment_tag', - onPressed: () => context.read().increment(), - child: const Icon(Icons.add), - ), - const SizedBox(height: 8), - FloatingActionButton( - heroTag: 'decrement_tag', - onPressed: () => context.read().decrement(), - child: const Icon(Icons.remove), - ), - ], - ), - ); - } - ); - } -} diff --git a/bricks/wyatt_clean_code/__brick__/lib/presentation/pages/counter/widgets/counter_text.dart b/bricks/wyatt_clean_code/__brick__/lib/presentation/pages/counter/widgets/counter_text.dart deleted file mode 100644 index c0e2345..0000000 --- a/bricks/wyatt_clean_code/__brick__/lib/presentation/pages/counter/widgets/counter_text.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/presentation/bloc/counter_cubit/counter_cubit.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -class CounterText extends StatelessWidget { - const CounterText({super.key}); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - final count = context.select((CounterCubit cubit) => cubit.state); - return Text('$count', style: theme.textTheme.headline1); - } -} diff --git a/bricks/wyatt_clean_code/__brick__/lib/presentation/pages/initial/initial_page.dart b/bricks/wyatt_clean_code/__brick__/lib/presentation/pages/initial/initial_page.dart deleted file mode 100644 index 0f7f0b6..0000000 --- a/bricks/wyatt_clean_code/__brick__/lib/presentation/pages/initial/initial_page.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter/material.dart'; -{{#enable_router}}import 'package:go_router/go_router.dart'; -import 'package:{{project_name.snakeCase()}}/core/resources/app_router.dart';{{/enable_router}} -{{^enable_router}}import 'package:{{project_name.snakeCase()}}/presentation/pages/counter/counter_page.dart';{{/enable_router}} -{{#enable_l10n}}import 'package:{{project_name.snakeCase()}}/core/extensions/app_localizations_x.dart';{{/enable_l10n}} - -class InitialPage extends StatelessWidget { - const InitialPage({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Column( - children: [ - const Center(child: Text('{{project_name.titleCase()}}'),), - Center(child: ElevatedButton( - {{#enable_router}}onPressed: () => context.goNamed(AppRouter.counter),{{/enable_router}} - {{^enable_router}}onPressed: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => CounterPage())),{{/enable_router}} - {{#enable_l10n}}child: Text(context.l10n.goToCounter),{{/enable_l10n}} - {{^enable_l10n}}child: Text('Go to counter'),{{/enable_l10n}} - ),) - ], - ), - ); - } -} diff --git a/bricks/wyatt_clean_code/__brick__/lib/presentation/shared/layouts/.gitkeep b/bricks/wyatt_clean_code/__brick__/lib/presentation/shared/layouts/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/bricks/wyatt_clean_code/__brick__/lib/presentation/shared/layouts/app_default_scaffold.dart b/bricks/wyatt_clean_code/__brick__/lib/presentation/shared/layouts/app_default_scaffold.dart new file mode 100644 index 0000000..1d21aa1 --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/presentation/shared/layouts/app_default_scaffold.dart @@ -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? 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, + ); +} diff --git a/bricks/wyatt_clean_code/__brick__/lib/presentation/shared/state_management/.gitkeep b/bricks/wyatt_clean_code/__brick__/lib/presentation/shared/state_management/.gitkeep new file mode 100644 index 0000000..f94cb6f --- /dev/null +++ b/bricks/wyatt_clean_code/__brick__/lib/presentation/shared/state_management/.gitkeep @@ -0,0 +1 @@ +# just to keep empty folder in brick generation \ No newline at end of file diff --git a/bricks/wyatt_clean_code/__brick__/lib/presentation/shared/state_management/counter_state_management.dart b/bricks/wyatt_clean_code/__brick__/lib/presentation/shared/state_management/counter_state_management.dart deleted file mode 100644 index 56abbbc..0000000 --- a/bricks/wyatt_clean_code/__brick__/lib/presentation/shared/state_management/counter_state_management.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:{{#snakeCase}}{{project_name}}{{/snakeCase}}/presentation/bloc/counter_cubit/counter_cubit.dart'; -import 'package:flutter/material.dart'; -import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; - -class CounterStateManagement extends CubitScreen { - const CounterStateManagement({ - super.key, - this.init, - required this.builder, - }); - - final void Function(CounterCubit cubit)? init; - final Widget Function(BuildContext context, int state) builder; - - @override - CounterCubit create(BuildContext context) { - final cubit = CounterCubit(); - init?.call(cubit); - return cubit; - } - - @override - Widget onBuild(BuildContext context, int state) { - return builder.call(context, state); - } -} diff --git a/bricks/wyatt_clean_code/__brick__/lib/presentation/shared/widgets/.gitkeep b/bricks/wyatt_clean_code/__brick__/lib/presentation/shared/widgets/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/bricks/wyatt_clean_code/__brick__/lib/widget_tree.dart b/bricks/wyatt_clean_code/__brick__/lib/widget_tree.dart deleted file mode 100644 index 2d30095..0000000 --- a/bricks/wyatt_clean_code/__brick__/lib/widget_tree.dart +++ /dev/null @@ -1,63 +0,0 @@ -{{#enable_router}}import 'package:{{project_name.snakeCase()}}/core/resources/app_router.dart'; -import 'package:go_router/go_router.dart';{{/enable_router}} -import 'package:{{project_name.snakeCase()}}/core/resources/app_theme.dart'; -{{#enable_l10n}}import 'package:flutter_localizations/flutter_localizations.dart'; -import 'package:{{project_name.snakeCase()}}/core/extensions/app_localizations_x.dart';{{/enable_l10n}} -import 'package:{{project_name.snakeCase()}}/presentation/pages/initial/initial_page.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; - -class WidgetTree extends StatelessWidget { - const WidgetTree({super.key}); - - @override - Widget build(BuildContext context) { - {{#enable_router}} - final GoRouter _router = GoRouter( - initialLocation: '/', - routes: AppRouter.routes, - debugLogDiagnostics: true, - errorBuilder: (_, __) => Container( - color: Colors.red, - ), - ); - {{/enable_router}} - - return ScreenUtilInit( - designSize: const Size(455, 985), - builder: (context, child) { - {{#enable_router}} - return MaterialApp.router( - debugShowCheckedModeBanner: false, - title: '{{project_name.titleCase()}}', - routeInformationParser: _router.routeInformationParser, - routeInformationProvider: _router.routeInformationProvider, - routerDelegate: _router.routerDelegate, - theme: AppTheme.defaultTheme, - {{#enable_l10n}}localizationsDelegates: const [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: AppLocalizations.supportedLocales,{{/enable_l10n}} - ); - {{/enable_router}}{{^enable_router}} - return MaterialApp( - debugShowCheckedModeBanner: false, - title: '{{project_name.titleCase()}}', - theme: AppTheme.defaultTheme, - home: const InitialPage(), - {{#enable_l10n}}localizationsDelegates: const [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: AppLocalizations.supportedLocales,{{/enable_l10n}} - ); - {{/enable_router}} - }, - ); - } -} diff --git a/bricks/wyatt_clean_code/__brick__/pubspec.yaml b/bricks/wyatt_clean_code/__brick__/pubspec.yaml index d8add2c..529d443 100644 --- a/bricks/wyatt_clean_code/__brick__/pubspec.yaml +++ b/bricks/wyatt_clean_code/__brick__/pubspec.yaml @@ -1,79 +1,125 @@ name: {{#snakeCase}}{{project_name}}{{/snakeCase}} description: {{{description}}} -version: 1.0.0+1 -publish_to: none +# 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 - #{{#enable_l10n}} Localization flutter_localizations: sdk: flutter intl: ^0.17.0 - #{{/enable_l10n}}{{#enable_auth}} Authentication - wyatt_authentication_bloc: - git: - url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages - ref: wyatt_authentication_bloc-v0.2.1 - path: packages/wyatt_authentication_bloc - #{{/enable_auth}}{{#enable_forms}} Forms - wyatt_form_bloc: - git: - url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages - ref: wyatt_form_bloc-v0.0.2 - path: packages/wyatt_form_bloc - #{{/enable_forms}}{{#enable_freezed}} Freezed: model generation - freezed_annotation: ^2.0.3 - json_annotation: ^4.5.0 - #{{/enable_freezed}}{{#enable_http}} Advanced HTTP Client - wyatt_http_client: - git: - url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages - ref: wyatt_http_client-v1.2.0 - path: packages/wyatt_http_client - #{{/enable_http}}{{#enable_router}} Router - go_router: ^4.1.0 - #{{/enable_router}} - - flutter_bloc: ^8.0.1 + go_router: ^4.1.1 equatable: ^2.0.3 - flutter_screenutil: ^5.5.3+2 + 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: wyatt_bloc_helper-v1.1.0 + 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 - build_runner: ^2.1.11 - dependency_validator: ^3.2.0 + + dependency_validator: ^3.2.2 + + build_runner: ^2.2.0 flutter_gen_runner: ^4.3.0 - #{{#enable_freezed}} Freezed utils - freezed: ^2.0.3+1 - json_serializable: ^6.2.0 - #{{/enable_freezed}}{{#enable_analysis}} Analyzer + 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.1.0 + ref: wyatt_analysis-v2.2.1 path: packages/wyatt_analysis - #{{/enable_analysis}}{{^enable_analysis}} - flutter_lints: ^1.0.0 - #{{/enable_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/dark.xml + - 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 - {{#enable_l10n}}generate: true{{/enable_l10n}} + + generate: true + + # To add assets to your application, add an assets section, like this: + assets: + - assets/images/ + + # 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 diff --git a/bricks/wyatt_clean_code/__brick__/scripts/.gitkeep b/bricks/wyatt_clean_code/__brick__/scripts/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/bricks/wyatt_clean_code/__brick__/{{#enable_android}}android{{/enable_android}}/app/build.gradle b/bricks/wyatt_clean_code/__brick__/{{#enable_android}}android{{/enable_android}}/app/build.gradle index 7b36f80..42c94ef 100644 --- a/bricks/wyatt_clean_code/__brick__/{{#enable_android}}android{{/enable_android}}/app/build.gradle +++ b/bricks/wyatt_clean_code/__brick__/{{#enable_android}}android{{/enable_android}}/app/build.gradle @@ -21,6 +21,12 @@ 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" @@ -44,7 +50,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "{{#dotCase}}{{org_name}}{{/dotCase}}.{{#snakeCase}}{{project_name}}{{/snakeCase}}" + applicationId "com.example.{{#snakeCase}}{{project_name}}{{/snakeCase}}" // 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 @@ -53,10 +59,50 @@ android { 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: "{{#titleCase}}{{project_name}}{{/titleCase}}"] + } + staging { + dimension "default" + applicationIdSuffix ".stg" + manifestPlaceholders = [appName: "[STG] {{#titleCase}}{{project_name}}{{/titleCase}}"] + } + development { + dimension "default" + applicationIdSuffix ".dev" + manifestPlaceholders = [appName: "[DEV] {{#titleCase}}{{project_name}}{{/titleCase}}"] + } + } + buildTypes { release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.release + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android.txt') + } + debug { signingConfig signingConfigs.debug } } diff --git a/bricks/wyatt_clean_code/__brick__/{{#enable_android}}android{{/enable_android}}/app/src/debug/AndroidManifest.xml b/bricks/wyatt_clean_code/__brick__/{{#enable_android}}android{{/enable_android}}/app/src/debug/AndroidManifest.xml index dfa6d61..60d9d23 100644 --- a/bricks/wyatt_clean_code/__brick__/{{#enable_android}}android{{/enable_android}}/app/src/debug/AndroidManifest.xml +++ b/bricks/wyatt_clean_code/__brick__/{{#enable_android}}android{{/enable_android}}/app/src/debug/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="com.example.{{#snakeCase}}{{project_name}}{{/snakeCase}}"> - + - {{project_name.titleCase()}} + {{#titleCase}}{{project_name}}{{/titleCase}}