feat: [WIP] new app with feature and platform selection

This commit is contained in:
Hugo Pointcheval 2022-06-27 17:58:39 +02:00
parent a644bd781b
commit 6f8b7ab7a1
Signed by: hugo
GPG Key ID: A9E8E9615379254F
26 changed files with 603 additions and 2 deletions

View File

@ -0,0 +1,3 @@
# 0.1.0+1
- TODO: Describe initial release.

View File

@ -0,0 +1 @@
TODO: Add your license here.

View File

@ -0,0 +1,25 @@
# Wyatt - Clean Code
[![Powered by Mason](https://img.shields.io/endpoint?url=https%3A%2F%2Ftinyurl.com%2Fmason-badge)](https://github.com/felangel/mason)
Create clean new **Flutter** project from scratch following Clean Code Architecture.
## Usage 🚀
```sh
mason make wyatt_clean_code
```
## Variables ✨
| variable | description | default | type |
| ------------------ | ---------------------------- | ------- | --------- |
| `project_name` | Project name | app | `string` |
| `org_name` | Organization name | fr.wyattstudio.app | `string` |
| `description` | A short project description | A new Wyatt Studio project. | `string` |
| `platforms` | Supported platforms | [android, ios] | `array` |
| `features` | Enabled features | [analysis, localization] | `array` |
## Prerequisite
After the generation, if `localization` is enabled, you must call `make intl` to generate default S class.

View File

@ -0,0 +1,25 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "{{#snakeCase}}{{project_name}}{{/snakeCase}}",
"request": "launch",
"type": "dart"
},
{
"name": "{{#snakeCase}}{{project_name}}{{/snakeCase}} (profile mode)",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
},
{
"name": "{{#snakeCase}}{{project_name}}{{/snakeCase}} (release mode)",
"request": "launch",
"type": "dart",
"flutterMode": "release"
}
]
}

View File

@ -0,0 +1,14 @@
{
"bloc.newCubitTemplate.type": "equatable",
"psi-header.config": {
"blankLinesAfter": 0,
"forceToTop": true,
},
"psi-header.templates": [
{
"language": "*",
"template": [],
// disabled,
}
],
}

View File

@ -0,0 +1,40 @@
.PHONY: help clean get upgrade format lint
# Adding a help file: https://gist.github.com/prwhite/8168133#gistcomment-1313022
help: ## This help dialog.
@IFS=$$'\n' ; \
help_lines=(`fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//'`); \
for help_line in $${help_lines[@]}; do \
IFS=$$'#' ; \
help_split=($$help_line) ; \
help_command=`echo $${help_split[0]} | sed -e 's/^ *//' -e 's/ *$$//'` ; \
help_info=`echo $${help_split[2]} | sed -e 's/^ *//' -e 's/ *$$//'` ; \
printf "%-30s %s\n" $$help_command $$help_info ; \
done
clean: ## Cleans the environment.
@echo "• Cleaning the project..."
@rm -rf pubspec.lock
@flutter clean
get: ## Gets the dependencies.
@echo "• Getting the dependencies..."
@flutter pub get
upgrade: clean ## Upgrades dependencies.
@echo "• Upgrading dependencies..."
@flutter pub upgrade
format: ## Formats the code.
@echo "• Formatting the code"
@dart format .
lint: ## Lints the code.
@echo "• Verifying code..."
@dart analyze . || (echo "Error in project"; exit 1)
# {{#enable_l10n}}
.PHONY: intl
intl: get ## Generates the intl files.
@echo "• Generating the intl files"
@flutter pub run intl_utils:generate
# {{/enable_l10n}}

View File

@ -0,0 +1,9 @@
# {{#enable_analysis}}
include: package:wyatt_analysis/analysis_options.flutter.experimental.yaml
analyzer:
strong-mode:
implicit-dynamic: true
# {{/enable_analysis}}{{^enable_analysis}}
include: package:flutter_lints/flutter.yaml
# {{/enable_analysis}}

View File

@ -0,0 +1,26 @@
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(),
),
);
}
}

View File

@ -0,0 +1,51 @@
import 'package:bloc/bloc.dart';
import 'package:flutter/foundation.dart';
class AppBlocObserver extends BlocObserver {
final bool printEvent;
final bool printError;
final bool printChange;
final bool printTransition;
AppBlocObserver({
this.printEvent = true,
this.printError = true,
this.printTransition = true,
this.printChange = true,
});
@override
void onEvent(Bloc<dynamic, dynamic> bloc, Object? event) {
super.onEvent(bloc, event);
if (printEvent) {
debugPrint(event.toString());
}
}
@override
void onError(BlocBase<dynamic> bloc, Object error, StackTrace stackTrace) {
if (printError) {
debugPrint(error.toString());
}
super.onError(bloc, error, stackTrace);
}
@override
void onChange(BlocBase<dynamic> bloc, Change<dynamic> change) {
super.onChange(bloc, change);
if (printChange) {
debugPrint(change.toString());
}
}
@override
void onTransition(
Bloc<dynamic, dynamic> bloc,
Transition<dynamic, dynamic> transition,
) {
super.onTransition(bloc, transition);
if (printTransition) {
debugPrint(transition.toString());
}
}
}

View File

@ -0,0 +1,18 @@
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<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
{{#enable_router}}GoRouter.setUrlPathStrategy(UrlPathStrategy.path);{{/enable_router}}
BlocOverrides.runZoned(
() => runApp(
const App(),
),
blocObserver: AppBlocObserver(),
);
}

View File

@ -0,0 +1,13 @@
import 'package:flutter/material.dart';
class InitialPage extends StatelessWidget {
const InitialPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text('{{project_name.titleCase()}}')),
);
}
}

View File

@ -0,0 +1,18 @@
import 'package:{{project_name.snakeCase()}}/presentation/pages/initial/initial_page.dart';
import 'package:go_router/go_router.dart';
import 'package:flutter/material.dart';
abstract class AppRouter {
static const String initial = 'initial';
static final List<GoRoute> routes = [
GoRoute(
path: '/',
name: initial,
pageBuilder: (context, state) => MaterialPage<void>(
key: state.pageKey,
child: const InitialPage(),
),
),
];
}

View File

@ -0,0 +1,66 @@
{{#enable_router}}import 'package:{{project_name.snakeCase()}}/resources/app_router.dart';
import 'package:go_router/go_router.dart';{{/enable_router}}
import 'package:{{project_name.snakeCase()}}/resources/app_theme.dart';
{{#enable_l10n}}import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:{{project_name.snakeCase()}}/generated/l10n.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,
routerDelegate: _router.routerDelegate,
theme: AppTheme.defaultTheme,
{{#enable_l10n}}localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale('fr', 'FR'),
],{{/enable_l10n}}
);
{{/enable_router}}{{^enable_router}}
return MaterialApp(
debugShowCheckedModeBanner: false,
title: '{{project_name.titleCase()}}',
theme: AppTheme.defaultTheme,
home: const InitialPage(),
{{#enable_l10n}}localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale('fr', 'FR'),
],{{/enable_l10n}}
);
{{/enable_router}}
},
);
}
}

View File

@ -0,0 +1,82 @@
name: {{#snakeCase}}{{project_name}}{{/snakeCase}}
description: {{{description}}}
version: 1.0.0+1
publish_to: none
environment:
sdk: ">=2.17.0 <3.0.0"
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_bloc}} State management: BLoC
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
path: packages/wyatt_bloc_helper
#{{/enable_bloc}}{{#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}}
equatable: ^2.0.3
flutter_screenutil: ^5.5.3+2
gap: ^2.0.0
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.1.11
dependency_validator: ^3.2.0
# {{#enable_l10n}} Localization utils
intl_utils: ^2.6.1
# {{/enable_l10n}}{{#enable_freezed}} Freezed utils
freezed: ^2.0.3+1
json_serializable: ^6.2.0
# {{/enable_freezed}}{{#enable_analysis}} Analyzer
wyatt_analysis:
git:
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages
ref: wyatt_analysis-v2.1.0
path: packages/wyatt_analysis
# {{/enable_analysis}}{{^enable_analysis}}
flutter_lints: ^1.0.0
# {{/enable_analysis}}
flutter:
uses-material-design: true
# {{#enable_l10n}} Localization config
flutter_intl:
enabled: true
main_locale: fr
arb_dir: assets/l10n
# {{/enable_l10n}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="{{description}}">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="example">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>{{project_name.titleCase()}}</title>
<link rel="manifest" href="manifest.json">
<script>
// The value below is injected by flutter build, do not touch.
var serviceWorkerVersion = null;
</script>
<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
</head>
<body>
<script>
window.addEventListener('load', function(ev) {
// Download main.dart.js
_flutter.loader.loadEntrypoint({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
}
}).then(function(engineInitializer) {
return engineInitializer.initializeEngine();
}).then(function(appRunner) {
return appRunner.runApp();
});
});
</script>
</body>
</html>

View File

@ -0,0 +1,35 @@
{
"name": "{{project_name.titleCase()}}",
"short_name": "{{project_name.titleCase()}}",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "{{description}}",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "icons/Icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "icons/Icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}

View File

@ -0,0 +1,67 @@
name: wyatt_clean_code
description: Create clean new Flutter project from scratch following Clean Code Architecture.
# The following defines the version and build number for your brick.
# A version number is three numbers separated by dots, like 1.2.34
# followed by an optional build number (separated by a +).
version: 0.1.0+1
# The following defines the environment for the current brick.
# It includes the version of mason that the brick requires.
environment:
mason: ">=0.1.0-dev.26 <0.1.0"
# Variables specify dynamic values that your brick depends on.
# Zero or more variables can be specified for a given brick.
# Each variable has:
# * a type (string, number, boolean, enum, or array)
# * an optional short description
# * an optional default value
# * an optional list of default values (array only)
# * an optional prompt phrase used when asking for the variable
# * a list of values (enums only)
vars:
project_name:
type: string
description: The project name
default: app
prompt: "What is the project name?"
org_name:
type: string
description: The organization name
default: fr.wyattstudio.app
prompt: "What is the organization name?"
description:
type: string
description: A short project description
default: A new Wyatt Studio project.
prompt: "What is the project description?"
platforms:
type: array
description: Supported platforms
prompt: Which platforms would you like to support?
defaults:
- android
- ios
values:
- android
- ios
- web
- macos
- windows
- linux
features:
type: array
description: Enabled features
prompt: Which features would you like to enable?
defaults:
- analysis
- localization
values:
- analysis
- localization
- freezed
- http
- router
- authentication
- forms

View File

@ -0,0 +1,41 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:mason/mason.dart';
void run(HookContext context) {
final platforms = context.vars['platforms'];
final enabled_platforms = <String, bool>{
'enable_android': platforms?.contains('android') ?? false,
'enable_ios': platforms?.contains('ios') ?? false,
'enable_web': platforms?.contains('web') ?? false,
'enable_macos': platforms?.contains('macos') ?? false,
'enable_windows': platforms?.contains('windows') ?? false,
'enable_linux': platforms?.contains('linux') ?? false,
};
final features = context.vars['features'];
final enabled_features = <String, bool>{
'enable_l10n': features?.contains('localization') ?? false,
'enable_analysis': features?.contains('analysis') ?? false,
'enable_freezed': features?.contains('freezed') ?? false,
'enable_http': features?.contains('http') ?? false,
'enable_router': features?.contains('router') ?? false,
'enable_auth': features?.contains('authentication') ?? false,
'enable_forms': features?.contains('forms') ?? false,
};
context.vars = {...context.vars, ...enabled_platforms, ...enabled_features};
context.logger.info(context.vars.toString());
}

View File

@ -0,0 +1,7 @@
name: hooks
environment:
sdk: ">=2.17.0 <3.0.0"
dependencies:
mason: any

View File

@ -1 +1 @@
{"bricks":{"core_app_brick":{"path":"bricks/core_app_brick"},"wyatt_package":{"path":"bricks/wyatt_package"}}}
{"bricks":{"core_app_brick":{"path":"bricks/core_app_brick"},"wyatt_package":{"path":"bricks/wyatt_package"},"wyatt_clean_code":{"path":"bricks/wyatt_clean_code"}}}

View File

@ -4,4 +4,6 @@ bricks:
core_app_brick:
path: bricks/core_app_brick
wyatt_package:
path: bricks/wyatt_package
path: bricks/wyatt_package
wyatt_clean_code:
path: bricks/wyatt_clean_code