feat(app): upgrade app template

This commit is contained in:
Hugo Pointcheval 2023-05-03 15:45:05 +02:00
parent ebd8618043
commit a100a81acd
Signed by: hugo
GPG Key ID: 3AAC487E131E00BC
30 changed files with 546 additions and 6847 deletions

View File

@ -6,54 +6,55 @@ A short project description
* Flutter <https://flutter.dev/>
* Taskfile <https://taskfile.dev/>
* Trapeze <https://trapeze.dev/> (with `npm install` thanks to `package.json`)
### Configuration
Create `.env` file with
At the build time, the app will read the environment variables from `config.json` file.
```sh
cp .env.example .env
The important variable is `DEV_MODE` which can be `mock` , `local` or `real` .
```json
{
"DEV_MODE": "local"
}
```
> **Note** `local` can refer to a local server or a local emulator.
### Taskfile
Available tasks:
| Command | Description | Aliases |
|----|-----|-----|
| `clean` | Cleans the environment.| `cl` |
| `format` |Formats the code.| `fmt` |
| `help` |Help dialog.| `h, default` |
| `lint` |Lints the code.| `l` |
| `start-emulators` | Start needed emulators.| `emu` |
| `build:android` | Building Android APK| `build:a` |
| `build:ios` | Building iOS IPA| `build:i` |
| `gen:build` | Running build runner| `gen:b` |
| `gen:build-delete` |Running build runner with deletion of conflicting outputs| `gen:d` |
| `gen:clean` | Cleaning build runner| `gen:c` |
| `gen:intl` |Generating internationalization file| `gen:i` |
| `gen:trapeze` | Running Trapeze config| `gen:t` |
| `gen:watch` | Running build runner in watch mode| `gen:w` |
| `pub:get` | Getting latest dependencies| `pub:g` |
| `pub:outdated` |Checking for outdated dependencies| `pub:o` |
| `pub:upgrade` | Upgrading dependencies| `pub:u` |
| `pub:upgrade-major` | Upgrading dependencies| `pub:um` |
| `pub:validate` |Running dependency validator| `pub:v` |
| `run:dev` | Run app in development environment| `run:d` |
| `run:emu` | Run app in development with emulated environment| `run:e` |
| `run:logs` |Show log output for running Flutter apps| `run:l` |
| `run:mock` |Run app in development environment with mocks| `run:m` |
| `run:prod` |Run app in production environment| `run:p` |
| `run:release` | Run app in production environment and in release mode| `run:r` |
| `run:staging` | Run app in staging environment| `run:s` |
| Commande | Description | Alias |
| --- | --- | --- |
| clean | Nettoie l'environnement de travail | cl |
| format | Formate le code | fmt |
| help | Affiche la boîte de dialogue d'aide | h, default |
| lint | Vérifie la qualité du code | l |
| start-emulators | Démarre les émulateurs nécessaires | emu |
| build:android | Construit le fichier APK pour Android | build:a |
| build:ios | Construit le fichier IPA pour iOS | build:i |
| gen:build | Exécute le générateur de build | gen:b |
| gen:build-delete | Exécute le générateur de build et supprime les sorties en conflit | gen:d |
| gen:clean | Nettoie le générateur de build | gen:c |
| gen:intl | Génère un fichier d'internationalisation | gen:i |
| gen:watch | Exécute le générateur de build en mode surveillance | gen:w |
| pub:get | Obtient les dernières dépendances | pub:g |
| pub:outdated | Vérifie les dépendances obsolètes | pub:o |
| pub:upgrade | Met à jour les dépendances | pub:u |
| pub:upgrade-major | Met à jour les dépendances majeures | pub:um |
| pub:validate | Exécute le validateur de dépendances | pub:v |
| run:dev | Lance l'application en environnement de développement | run:d |
| run:logs | Affiche la sortie de journalisation pour les applications Flutter en cours d'exécution | run:l |
| run:prod | Lance l'application en environnement de production | run:p |
| run:staging | Lance l'application en environnement de pré-production | run:s |
### Flavors
### Parameters
| Flavor | Details |
|-------|--------|
| Development | Use `--dart-define="dev_mode=<MODE>"` to choose between `mock` , `emulator` and `real` |
| Staging | With a green banner. Only `real` mode available (remote data sources) |
| Production | Only `real` mode available (remote data sources) |
You can pass flutter options to the build and run commands.
> In `lib/core/flavors/flavor.dart` you can customize flavors.
```sh
task run:staging -- -d chrome
```
> **Note** The `--` is required to pass options to the command.

View File

@ -11,8 +11,8 @@ dart_code_metrics:
metrics:
cyclomatic-complexity: 20
maximum-nesting-level: 5
number-of-parameters: 4
source-lines-of-code: 50
number-of-parameters: 5
source-lines-of-code: 250
metrics-exclude:
- test/**
rules:

View File

@ -27,7 +27,7 @@ tasks:
aliases: [a]
cmds:
- echo -e "{{.GREEN}}{{.PREFIX}} Building Android APK...{{.COLOROFF}}"
- flutter build lib/main_production apk --no-pub --no-shrink
- flutter build apk --target=lib/main_production --no-pub --no-shrink
ios:
desc: Building iOS IPA
@ -35,4 +35,4 @@ tasks:
aliases: [i]
cmds:
- echo -e "{{.GREEN}}{{.PREFIX}} Building iOS IPA...{{.COLOROFF}}"
- flutter build lib/main_production ipa --no-pub
- flutter build ipa --target=lib/main_production --no-pub

View File

@ -54,10 +54,3 @@ tasks:
cmds:
- echo -e "{{.GREEN}}{{.PREFIX}} Running build runner in watch mode...{{.COLOROFF}}"
- flutter pub run build_runner watch
trapeze:
desc: Running Trapeze config
aliases: [t]
cmds:
- echo -e "{{.GREEN}}{{.PREFIX}} Running Trapeze config...{{.COLOROFF}}"
- npx trapeze run trapeze.yaml --android-project android --ios-project ios

View File

@ -16,44 +16,23 @@ tasks:
- echo -e "{{.GREEN}}{{.PREFIX}} Showing log output for running Flutter apps...{{.COLOROFF}}"
- flutter logs
mock:
desc: Run app in development environment with mocks
aliases: [m]
cmds:
- echo -e "{{.GREEN}}{{.PREFIX}} Running the app (development/debug:mocks)...{{.COLOROFF}}"
- flutter run --target lib/main_development.dart --dart-define="dev_mode=mock"
emu:
desc: Run app in development with emulated environment
aliases: [e]
cmds:
- echo -e "{{.GREEN}}{{.PREFIX}} Running the app (development/debug:emulator)...{{.COLOROFF}}"
- flutter run --target lib/main_development.dart --dart-define="dev_mode=emulator"
dev:
desc: Run app in development environment
aliases: [d]
cmds:
- echo -e "{{.GREEN}}{{.PREFIX}} Running the app (development/debug:real)...{{.COLOROFF}}"
- flutter run --target lib/main_development.dart --dart-define="dev_mode=real"
- echo -e "{{.GREEN}}{{.PREFIX}} Running the app (development)...{{.COLOROFF}}"
- flutter run --target lib/main_development.dart --dart-define-from-file=config.json {{.CLI_ARGS}}
staging:
desc: Run app in staging environment
aliases: [s]
cmds:
- echo -e "{{.GREEN}}{{.PREFIX}} Running the app (staging/debug)...{{.COLOROFF}}"
- flutter run --target lib/main_staging.dart
- echo -e "{{.GREEN}}{{.PREFIX}} Running the app (staging)...{{.COLOROFF}}"
- flutter run --target lib/main_staging.dart --dart-define-from-file=config.json {{.CLI_ARGS}}
prod:
desc: Run app in production environment
aliases: [p]
cmds:
- echo -e "{{.GREEN}}{{.PREFIX}} Running the app (production/debug)...{{.COLOROFF}}"
- flutter run --target lib/main_production.dart
release:
desc: Run app in production environment and in release mode
aliases: [r]
cmds:
- echo -e "{{.GREEN}}{{.PREFIX}} Running the app (production/release)...{{.COLOROFF}}"
- flutter run --target lib/main_production.dart --release
- echo -e "{{.GREEN}}{{.PREFIX}} Running the app (production)...{{.COLOROFF}}"
- flutter run --target lib/main_production.dart --dart-define-from-file=config.json {{.CLI_ARGS}}

View File

@ -0,0 +1,8 @@
{
"FIREBASE_EMULATOR_CLOUD_FUNCTION_PORT": 5001,
"FIREBASE_EMULATOR_FIRESTORE_PORT": 8080,
"FIREBASE_EMULATOR_AUTH_PORT": 9099,
"FIREBASE_EMULATOR_STORAGE_PORT": 919911,
"FIREBASE_EMULATOR_HOST": "localhost",
"DEV_MODE": "local"
}

View File

@ -7,12 +7,11 @@ import 'package:starting_template/core/flavors/flavor.dart';
import 'package:starting_template/core/utils/app_bloc_observer.dart';
Future<void> bootstrap(FutureOr<Widget> Function() builder) async {
final widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
// FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
WidgetsFlutterBinding.ensureInitialized();
Bloc.observer = AppBlocObserver();
debugPrint('Flavor: ${Flavor.get()}');
debugPrint('${Flavor.instance}');
await GetItInitializer.init();

View File

@ -1,14 +1,14 @@
/// Firebase Emulator constants.
///
///
/// If you don't use Firebase, it can be safely deleted.
abstract class Emulator {
static const String firebaseCloudFunctionEnvKey =
'EMULATOR_FIREBASE_CLOUD_FUNCTION_PORT';
'FIREBASE_EMULATOR_CLOUD_FUNCTION_PORT';
static const String firebaseFirestoreEnvKey =
'EMULATOR_FIREBASE_FIRESTORE_PORT';
static const String firebaseAuthEnvKey = 'EMULATOR_FIREBASE_AUTH_PORT';
static const String firebaseStorageEnvKey = 'EMULATOR_FIREBASE_STORAGE_PORT';
static const String hostEnvKey = 'EMULATOR_HOST';
'FIREBASE_EMULATOR_FIRESTORE_PORT';
static const String firebaseAuthEnvKey = 'FIREBASE_EMULATOR_AUTH_PORT';
static const String firebaseStorageEnvKey = 'FIREBASE_EMULATOR_STORAGE_PORT';
static const String hostEnvKey = 'FIREBASE_EMULATOR_HOST';
static const int defaultFirebaseCloudFunctionPort = 5001;
static const int defaultFirebaseFirestorePort = 8080;

View File

@ -1,38 +1,50 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:get_it/get_it.dart';
import 'package:starting_template/core/enums/dev_mode.dart';
import 'package:starting_template/core/flavors/flavor.dart';
import 'package:starting_template/data/data_sources/local/counter_data_source_impl.dart';
import 'package:starting_template/domain/data_sources/local/counter_data_source.dart';
import 'package:starting_template/core/utils/firebase_emulator.dart';
final getIt = GetIt.I;
/// Service and Data Source locator
abstract class GetItInitializer {
static FutureOr<void> _initCommon() async {
// Initialize common sources/services
getIt.registerLazySingleton<CounterDataSource>(
CounterDataSourceImpl.new,
);
// TODO(wyatt): Initialize common sources/services
}
static FutureOr<void> _initMocks() async {
// Initialize mocked sources/services.
static FutureOr<void> _initMock() async {
// TODO(wyatt): Initialize mocked sources/services.
}
static FutureOr<void> _initLocal() async {
// TODO(wyatt): Initialize local sources/services.
final emulator = FirebaseEmulator.fromEnv();
debugPrint('Firebase Emulator: $emulator');
}
static FutureOr<void> _initReal() async {
// Initialize real sources/services
// TODO(wyatt): Initialize real sources/services
}
static FutureOr<void> init() async {
// Initialize common sources/services
await _initCommon();
final flavor = Flavor.get();
if (flavor.devMode == DevMode.mock) {
await _initMocks();
} else {
await _initReal();
// Initialize sources/services based on flavor
switch (Flavor.instance.devMode) {
case DevMode.mock:
await _initMock();
break;
case DevMode.local:
await _initLocal();
break;
case DevMode.real:
await _initReal();
break;
case null:
throw Exception('DevMode not initialized!');
}
await getIt.allReady();

View File

@ -0,0 +1,31 @@
enum BuildMode {
/// Debug build mode. Pass `--debug` to `flutter run` or `flutter build` to
/// use this mode.
debug,
/// Release build mode. Pass `--profile` to `flutter run` or `flutter build`
/// to use this mode.
profile,
/// Release build mode. Pass `--release` to `flutter run` or `flutter build`
/// to use this mode.
release;
@override
String toString() => name;
/// Tries to parse String and returns mode. Fallback is returned if there
/// is an error during parsing.
static BuildMode fromString(
String? mode, {
BuildMode fallback = BuildMode.debug,
}) {
for (final m in values) {
if (m.name == mode) {
return m;
}
}
return fallback;
}
}

View File

@ -1,12 +1,17 @@
enum DevMode {
/// Mocked data sources and services
mock,
emulator,
/// Local data sources and services, like local database, or firebase emulator
local,
/// Real data sources and services, like firebase or other cloud services
real;
@override
String toString() => name;
/// Tries to parse String and returns mode. Fallback is returned if there
/// Tries to parse String and returns mode. Fallback is returned if there
/// is an error during parsing.
static DevMode fromString(String? mode, {DevMode fallback = DevMode.mock}) {
for (final m in values) {

View File

@ -0,0 +1,26 @@
// Copyright (C) 2023 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/>.
enum PageProtection {
/// The page can be accessed without authentication.
public,
/// The page can only be accessed with authentication.
protected,
/// The page protection is unknown, and the default one should be used.
none,
}

View File

@ -0,0 +1,53 @@
// Copyright (C) 2023 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:go_router/go_router.dart';
import 'package:starting_template/core/enums/page_protection.dart';
/// Defines if a GoRoute is public or not.
///
/// By default, all routes are in the [PageProtection.none] state.
extension GoRouteGuard on GoRoute {
static final _guard = Expando<PageProtection>();
/// Returns `true` if the route is public.
bool get isPublic => _guard[this] == PageProtection.public;
/// Returns `true` if the route is protected.
bool get isProtected => _guard[this] == PageProtection.protected;
/// Returns `true` if the route is neither public nor protected.
/// This is the default state.
bool get isNone => _guard[this] == PageProtection.none;
/// Sets the route to be public.
/// This is useful for routes that should be accessible
/// without authentication.
/// ```dart
/// GoRoute(
/// path: '/sign_in',
/// ...
/// )..setPublic(),
/// ```
void setPublic() => _guard[this] = PageProtection.public;
/// Sets the route to be protected.
/// This is useful for routes that should only be accessible
/// with authentication.
void setProtected() => _guard[this] = PageProtection.protected;
PageProtection get guard => _guard[this] ?? PageProtection.none;
}

View File

@ -1,23 +1,38 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:starting_template/core/enums/build_mode.dart';
import 'package:starting_template/core/enums/dev_mode.dart';
abstract class Flavor {
class Flavor {
Flavor._({
this.banner,
this.bannerColor = Colors.red,
this.devMode,
this.flavorName,
this.bannerColor,
}) {
// Determine build mode
buildMode = kReleaseMode
? BuildMode.release
: kProfileMode
? BuildMode.profile
: BuildMode.debug;
// Retrieve dev mode, fallback to mock
devMode = DevMode.fromString(
const String.fromEnvironment('DEV_MODE', defaultValue: 'mock'),
);
_instance = this;
}
static Flavor? _instance;
final String? banner;
final Color bannerColor;
final DevMode? devMode;
final String? flavorName;
final Color? bannerColor;
late final DevMode? devMode;
late final BuildMode? buildMode;
/// Returns [Flavor] instance.
static Flavor get() {
static Flavor get instance {
if (_instance == null) {
throw Exception('Flavor not initialized!');
}
@ -25,33 +40,26 @@ abstract class Flavor {
return _instance!;
}
String get name => flavorName ?? 'Unknown';
Color get color => bannerColor ?? Colors.grey;
static bool shouldShowBanner() => instance.buildMode != BuildMode.release;
@override
String toString() => runtimeType.toString().replaceAll('Flavor', '');
String toString() =>
'Flavor: $flavorName, DevMode: $devMode, BuildMode: $buildMode';
}
class DevelopmentFlavor extends Flavor {
factory DevelopmentFlavor() {
const modeString = String.fromEnvironment('dev_mode', defaultValue: 'mock');
final mode = DevMode.fromString(modeString);
return DevelopmentFlavor._(devMode: mode);
}
DevelopmentFlavor._({
required DevMode devMode,
}) : super._(
banner: 'Dev',
devMode: devMode,
);
DevelopmentFlavor()
: super._(flavorName: 'Development', bannerColor: Colors.red);
}
class StagingFlavor extends Flavor {
StagingFlavor()
: super._(
banner: 'Staging',
bannerColor: Colors.green,
);
StagingFlavor() : super._(flavorName: 'Staging', bannerColor: Colors.orange);
}
class ProductionFlavor extends Flavor {
ProductionFlavor() : super._();
ProductionFlavor()
: super._(flavorName: 'Production', bannerColor: Colors.green);
}

View File

@ -0,0 +1,40 @@
// Copyright (C) 2023 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 'dart:async';
import 'package:flutter/foundation.dart';
/// {@template go_router_refresh_stream}
/// A [ChangeNotifier] that notifies its listeners when a stream emits a value.
/// {@endtemplate}
class GoRouterRefreshStream extends ChangeNotifier {
/// {@macro go_router_refresh_stream}
GoRouterRefreshStream(Stream<dynamic> stream) {
notifyListeners();
_subscription = stream.asBroadcastStream().listen(
(dynamic _) => notifyListeners(),
);
}
late final StreamSubscription<dynamic> _subscription;
@override
void dispose() {
_subscription.cancel();
super.dispose();
}
}

View File

@ -1,6 +1,8 @@
import 'package:flutter/cupertino.dart';
import 'package:go_router/go_router.dart';
import 'package:starting_template/presentation/features/counter/counter.dart';
import 'package:starting_template/core/enums/page_protection.dart';
import 'package:starting_template/core/extensions/go_route_extension.dart';
import 'package:starting_template/core/routes/go_router_refresh_stream.dart';
import 'package:starting_template/presentation/features/home/home.dart';
abstract class AppRouter {
@ -10,10 +12,7 @@ abstract class AppRouter {
GoRouterState state,
Widget child,
) =>
CupertinoPage<void>(
key: state.pageKey,
child: child,
);
CupertinoPage<void>(key: state.pageKey, child: child);
/// Disable transition animation
static Page<void> noTransition(
@ -21,23 +20,7 @@ abstract class AppRouter {
GoRouterState state,
Widget child,
) =>
CustomTransitionPage<void>(
key: state.pageKey,
transitionsBuilder: (_, __, ___, child) => child,
child: child,
);
/// Defines public routes (no authentication needed).
///
/// Example:
/// ```dart
/// static final publicRoutes = [
/// '/',
/// '/sign_in',
/// '/sign_up',
/// ];
/// ```
static final List<String> publicRoutes = [];
NoTransitionPage(key: state.pageKey, child: child);
/// Defines GoRoute routes.
static final List<GoRoute> routes = [
@ -46,20 +29,32 @@ abstract class AppRouter {
name: Home.pageName,
pageBuilder: (context, state) =>
defaultTransition(context, state, const Home()),
),
GoRoute(
path: '/counter',
name: Counter.pageName,
pageBuilder: (context, state) =>
defaultTransition(context, state, const Counter()),
),
)..setPublic(),
];
/// Router
static GoRouter router = GoRouter(
initialLocation: '/',
routes: AppRouter.routes,
debugLogDiagnostics: true,
redirect: (context, state) => null,
);
static GoRouter routerOf(BuildContext context) => GoRouter(
initialLocation: '/',
routes: AppRouter.routes,
debugLogDiagnostics: true,
// TODO(wyatt): Add authentication logic
redirect: (context, state) {
/// Define the default guard
/// This is the guard that will be used if the route
/// does not have a guard set. (It is set to [PageProtection.none])
const defaultGuard = PageProtection.protected;
// Compute current route
// Compute current route
final currentRoute = AppRouter.routes.firstWhere(
(route) => route.path == state.location,
);
// Get the guard of the current route
final guard = currentRoute.guard;
return null;
},
refreshListenable: GoRouterRefreshStream(const Stream.empty()),
);
}

View File

@ -0,0 +1,61 @@
// Copyright (C) 2023 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:starting_template/core/constants/emulator.dart';
class FirebaseEmulator {
const FirebaseEmulator._({
required this.cloudFunctionPort,
required this.firestorePort,
required this.authPort,
required this.storagePort,
required this.host,
});
factory FirebaseEmulator.fromEnv() => const FirebaseEmulator._(
cloudFunctionPort: int.fromEnvironment(
Emulator.firebaseCloudFunctionEnvKey,
defaultValue: Emulator.defaultFirebaseCloudFunctionPort,
),
firestorePort: int.fromEnvironment(
Emulator.firebaseFirestoreEnvKey,
defaultValue: Emulator.defaultFirebaseFirestorePort,
),
authPort: int.fromEnvironment(
Emulator.firebaseAuthEnvKey,
defaultValue: Emulator.defaultFirebaseAuthPort,
),
storagePort: int.fromEnvironment(
Emulator.firebaseStorageEnvKey,
defaultValue: Emulator.defaultFirebaseStoragePort,
),
host: String.fromEnvironment(
Emulator.hostEnvKey,
defaultValue: Emulator.defaultHost,
),
);
final int cloudFunctionPort;
final int firestorePort;
final int authPort;
final int storagePort;
final String host;
@override
String toString() => 'FirebaseEmulator(cloudFunctionPort: '
'$cloudFunctionPort, firestorePort: $firestorePort, authPort: $authPort, '
'storagePort: $storagePort, host: $host)';
}

View File

@ -105,11 +105,6 @@ class _$_IntegerModel implements _IntegerModel {
@override
final int value;
@override
String toString() {
return 'IntegerModel(value: $value)';
}
@override
bool operator ==(dynamic other) {
return identical(this, other) ||

View File

@ -5,7 +5,7 @@
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal
// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use
import 'package:flutter/widgets.dart';
@ -84,7 +84,16 @@ class AssetGenImage {
);
}
ImageProvider provider() => AssetImage(_assetName);
ImageProvider provider({
AssetBundle? bundle,
String? package,
}) {
return AssetImage(
_assetName,
bundle: bundle,
package: package,
);
}
String get path => _assetName;

View File

@ -5,7 +5,7 @@
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal
// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use
import 'package:flutter/painting.dart';
import 'package:flutter/material.dart';

View File

@ -3,7 +3,7 @@ import 'package:starting_template/core/flavors/flavor.dart';
import 'package:starting_template/presentation/features/app/app.dart';
void main(List<String> args) {
// Define environment
// Define flavor
DevelopmentFlavor();
// Initialize environment and variables

View File

@ -3,7 +3,7 @@ import 'package:starting_template/core/flavors/flavor.dart';
import 'package:starting_template/presentation/features/app/app.dart';
void main(List<String> args) {
// Define environment
// Define flavor
ProductionFlavor();
// Initialize environment and variables

View File

@ -3,7 +3,7 @@ import 'package:starting_template/core/flavors/flavor.dart';
import 'package:starting_template/presentation/features/app/app.dart';
void main(List<String> args) {
// Define environment
// Define flavor
StagingFlavor();
// Initialize environment and variables

View File

@ -1,63 +1,47 @@
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:starting_template/core/dependency_injection/get_it.dart';
import 'package:starting_template/core/flavors/flavor.dart';
import 'package:starting_template/core/routes/router.dart';
import 'package:starting_template/data/repositories/counter_repository_impl.dart';
import 'package:starting_template/domain/repositories/counter_repository.dart';
import 'package:starting_template/gen/app_localizations.dart';
import 'package:starting_template/presentation/features/counter/blocs/counter_bloc/counter_bloc.dart';
import 'package:starting_template/presentation/shared/widgets/flavor_banner.dart';
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
class App extends StatelessWidget {
const App({super.key});
Widget _flavorBanner(Widget child) {
final flavor = Flavor.get();
if (flavor.banner != null && !kReleaseMode) {
return Directionality(
textDirection: TextDirection.ltr,
child: Banner(
location: BannerLocation.topEnd,
message: flavor.banner!,
color: flavor.bannerColor,
child: child,
),
);
}
return child;
}
@override
Widget build(BuildContext context) => MultiProvider(
repositoryProviders: [
RepositoryProvider<CounterRepository>(
create: (_) => CounterRepositoryImpl(counterDataSource: getIt()),
repositoryProviders: [
RepositoryProvider<CounterRepository>(
create: (_) => CounterRepositoryImpl(counterDataSource: getIt()),
),
],
blocProviders: [
BlocProvider<CounterBloc>(
create: (_) => CounterBloc(),
),
],
child: FlavorBanner(
child: MaterialApp.router(
title: 'Display Name',
debugShowCheckedModeBanner: false,
routerDelegate: AppRouter.routerOf(context).routerDelegate,
routeInformationParser:
AppRouter.routerOf(context).routeInformationParser,
routeInformationProvider:
AppRouter.routerOf(context).routeInformationProvider,
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: AppLocalizations.supportedLocales,
),
),
],
blocProviders: [
BlocProvider<CounterBloc>(
create: (_) => CounterBloc(),
),
],
child: _flavorBanner(
MaterialApp.router(
title: 'Display Name',
debugShowCheckedModeBanner: false,
routerDelegate: AppRouter.router.routerDelegate,
routeInformationParser: AppRouter.router.routeInformationParser,
routeInformationProvider: AppRouter.router.routeInformationProvider,
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: AppLocalizations.supportedLocales,
),
),
);
);
}

View File

@ -0,0 +1,73 @@
// Copyright (C) 2023 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:flutter/material.dart';
import 'package:starting_template/core/flavors/flavor.dart';
/// {@template flavor_banner}
/// A banner that displays the current flavor.
/// This is useful for quickly identifying which flavor is currently running.
///
/// When [Flavor.shouldShowBanner] is `false`, this widget will return [child].
/// You can also override this behavior by setting [visible] to `true`.
///
/// Wrap your [MaterialApp] with this widget to display the banner.
///
/// Example:
///
/// ```dart
/// FlavorBanner(
/// visible: true, // or null to use Flavor.shouldShowBanner()
/// child: const MaterialApp(
/// title: 'Starting Template',
/// ...
/// ),
/// );
/// ```
/// {@endtemplate}
class FlavorBanner extends StatelessWidget {
const FlavorBanner({
this.child,
this.visible,
super.key,
});
final Widget? child;
/// Defaults to [Flavor.shouldShowBanner].
final bool? visible;
@override
Widget build(BuildContext context) {
final visible = this.visible ?? Flavor.shouldShowBanner();
if (!visible) {
return child ?? const SizedBox.shrink();
}
final flavor = Flavor.instance;
return Directionality(
textDirection: TextDirection.ltr,
child: Banner(
location: BannerLocation.topEnd,
message: flavor.name,
color: flavor.color,
child: child,
),
);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +0,0 @@
{
"dependencies": {
"@trapezedev/configure": "^7.0.5"
}
}

View File

@ -1,90 +1,82 @@
name: starting_template
description: A short project description
publish_to: "none"
version: 1.0.0+1
environment:
sdk: ">=2.18.0 <3.0.0"
flutter: ">=3.0.0"
dependencies:
flutter: { sdk: flutter }
flutter_localizations: { sdk: flutter }
intl: ^0.17.0
go_router: ^6.0.1
equatable: ^2.0.5
freezed_annotation: ^2.2.0
json_annotation: ^4.8.0
publish_to: none
description: A short project description
environment:
sdk: '>=2.18.0 <3.0.0'
flutter: '>=3.0.0'
dependencies:
cupertino_icons: ^1.0.5
get_it: ^7.2.0
flutter_dotenv: ^5.0.2
gap: ^2.0.1
equatable: ^2.0.5
flutter_bloc: ^8.1.1
flutter_native_splash: ^2.2.15
freezed_annotation: ^2.2.0
gap: ^2.0.1
get_it: ^7.2.0
go_router: ^6.0.1
intl: ^0.17.0
json_annotation: ^4.8.0
url_launcher: ^6.1.7
uuid: ^3.0.7
flutter_native_splash: ^2.2.15
wyatt_architecture:
hosted:
url: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
wyatt_architecture:
version: ^0.2.0
hosted:
name: wyatt_architecture
version: 0.1.0+1
wyatt_bloc_helper:
hosted:
url: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
wyatt_bloc_helper:
version: ^2.0.1
hosted:
name: wyatt_bloc_helper
version: 2.0.0
wyatt_type_utils:
hosted:
url: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
wyatt_type_utils:
version: ^0.0.5
hosted:
name: wyatt_type_utils
version: 0.0.4
dev_dependencies:
flutter_test: { sdk: flutter }
dependency_validator: ^3.2.2
url: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
dev_dependencies:
build_runner: ^2.3.3
dart_code_metrics: ^5.4.0
dependency_validator: ^3.2.2
flutter_gen_runner: ^5.1.0+1
flutter_launcher_icons: ^0.11.0
freezed: ^2.3.2
json_serializable: ^6.6.0
flutter_launcher_icons: ^0.11.0
dart_code_metrics: ^5.4.0
pubspec_dependency_sorter: ^1.0.3
rename: ^2.1.1
wyatt_analysis:
hosted:
url: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
flutter_test:
sdk: flutter
wyatt_analysis:
version: ^2.5.0
hosted:
name: wyatt_analysis
version: 2.4.1
flutter:
uses-material-design: true
generate: true
assets:
- .env
url: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
flutter:
assets:
- assets/images/
flutter_gen:
uses-material-design: true
flutter_gen:
output: lib/gen/
integrations:
integrations:
flutter_svg: true
flare_flutter: true
rive: true
lottie: true
colors:
inputs:
colors:
inputs:
- assets/colors.xml
flutter_icons:
android: "launcher_icon"
ios: true
image_path: "assets/images/wyatt_logo.jpeg"
adaptive_icon_background: "#FFFFFF"
flutter_native_splash:
image: "assets/images/wyatt_logo.jpeg"
color: "#FFFFFF"
android: true
flutter_icons:
android: launcher_icon
image_path: assets/images/wyatt_logo.jpeg
adaptive_icon_background: '#FFFFFF'
ios: true
flutter_native_splash:
image: assets/images/wyatt_logo.jpeg
color: '#FFFFFF'
android_gravity: fill
ios_content_mode: scaleAspectFit
android: true
ios: true

View File

@ -1,36 +0,0 @@
vars:
PACKAGE_NAME:
default: starting_template
BUNDLE_ID:
default: io.wyattapp.start
APP_NAME:
default: Display Name
platforms:
android:
appName: $APP_NAME
packageName: $BUNDLE_ID
# Uncomment to add some permissions
# manifest:
# - file: AndroidManifest.xml
# target: manifest/application
# inject:
# <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
ios:
targets:
Runner:
bundleId: $BUNDLE_ID
productName: $APP_NAME
displayName: $APP_NAME
entitlements:
replace: true
# Workaround for https://stackoverflow.com/questions/55167611/flutter-ios-app-submission-issue-warning-missing-push-notification-entitlement
entries:
- aps-environment: development
# Uncomment to add some permissions
# plist:
# - replace: true
# entries:
# - UISupportedInterfaceOrientations:
# - UIInterfaceOrientationPortrait