mirror of
https://github.com/hanskokx/arcane_implementations.git
synced 2026-06-09 15:16:06 +02:00
@@ -0,0 +1,55 @@
|
||||
import "package:flutter/foundation.dart";
|
||||
import "package:flutter_dotenv/flutter_dotenv.dart";
|
||||
|
||||
class AppEnv {
|
||||
/// Fetches the environment variable value for the given [EnvVar]. If the
|
||||
/// value is not set, defaults to an empty string.
|
||||
static String valueOf(EnvVar val) {
|
||||
return dotenv.maybeGet(val.key) ?? "";
|
||||
}
|
||||
|
||||
/// Returns [true] if all of the [EnvVar] variables are set.
|
||||
static bool get hasEnv =>
|
||||
dotenv.isEveryDefined(EnvVar.values.map((e) => e.key).toList());
|
||||
|
||||
static Future<void> init() async {
|
||||
await dotenv.load();
|
||||
}
|
||||
|
||||
static const FlutterMode flutterMode = kDebugMode
|
||||
? FlutterMode.debug
|
||||
: kReleaseMode
|
||||
? FlutterMode.release
|
||||
: kProfileMode
|
||||
? FlutterMode.profile
|
||||
: FlutterMode.unknown;
|
||||
}
|
||||
|
||||
enum FlutterMode {
|
||||
debug,
|
||||
profile,
|
||||
release,
|
||||
unknown,
|
||||
}
|
||||
|
||||
extension EnvVarValue on EnvVar {
|
||||
/// The value of the [EnvVar] as a string. If the environment variable is not
|
||||
/// set, returns an empty string.
|
||||
String get value => AppEnv.valueOf(this);
|
||||
}
|
||||
|
||||
enum EnvVar {
|
||||
/// The environment to use for the API calls. Returns either [dev] or [prod].
|
||||
///
|
||||
/// Example `.env` configuration:
|
||||
/// ```
|
||||
/// API_ENVIRONMENT="dev"
|
||||
/// ```
|
||||
apiEnvironment("API_ENVIRONMENT"),
|
||||
;
|
||||
|
||||
/// The environment variable to use when retrieving the value of this [EnvVar].
|
||||
final String key;
|
||||
|
||||
const EnvVar(this.key);
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart";
|
||||
import "package:flutter_gen/gen_l10n/app_localizations.dart";
|
||||
import "package:flutter_svg/flutter_svg.dart";
|
||||
import "package:go_router/go_router.dart";
|
||||
|
||||
class AppScaffold extends StatelessWidget {
|
||||
final Widget body;
|
||||
final Widget? secondaryBody;
|
||||
|
||||
const AppScaffold({
|
||||
required this.body,
|
||||
this.secondaryBody,
|
||||
super.key = const ValueKey("AppScaffold"),
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AdaptiveLayout(
|
||||
internalAnimations: false,
|
||||
body: SlotLayout(
|
||||
config: <Breakpoint, SlotLayoutConfig>{
|
||||
Breakpoints.small: SlotLayout.from(
|
||||
key: const Key("Body Small"),
|
||||
builder: (context) => secondaryBody ?? body,
|
||||
),
|
||||
Breakpoints.mediumAndUp: SlotLayout.from(
|
||||
key: const Key("Body Medium and Up"),
|
||||
builder: (context) => body,
|
||||
),
|
||||
},
|
||||
),
|
||||
secondaryBody: SlotLayout(
|
||||
config: <Breakpoint, SlotLayoutConfig>{
|
||||
Breakpoints.small: SlotLayout.from(
|
||||
key: const Key("Body Small"),
|
||||
builder: null,
|
||||
),
|
||||
Breakpoints.mediumAndUp: SlotLayout.from(
|
||||
key: const Key("Body Medium"),
|
||||
builder: secondaryBody,
|
||||
),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppScaffoldShell extends StatelessWidget {
|
||||
final StatefulNavigationShell navigationShell;
|
||||
const AppScaffoldShell({
|
||||
required this.navigationShell,
|
||||
super.key = const ValueKey("AppScaffoldShell"),
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AdaptiveScaffold(
|
||||
useDrawer: false,
|
||||
internalAnimations: false,
|
||||
selectedIndex: navigationShell.currentIndex,
|
||||
onSelectedIndexChange: onNavigationEvent,
|
||||
destinations: [
|
||||
NavigationDestination(
|
||||
label: AppLocalizations.of(context).tabHome,
|
||||
icon: const Icon(Icons.home),
|
||||
),
|
||||
NavigationDestination(
|
||||
label: AppLocalizations.of(context).tabHistory,
|
||||
icon: const Icon(Icons.change_history_rounded),
|
||||
),
|
||||
NavigationDestination(
|
||||
label: AppLocalizations.of(context).tabProfile,
|
||||
icon: const Icon(Icons.account_circle),
|
||||
),
|
||||
],
|
||||
body: (_) => navigationShell,
|
||||
);
|
||||
}
|
||||
|
||||
void onNavigationEvent(int index) {
|
||||
navigationShell.goBranch(
|
||||
index,
|
||||
initialLocation: index == navigationShell.currentIndex,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
enum Feature {
|
||||
/// Prints the current auth token to the debug log each time the API makes a
|
||||
/// request.
|
||||
debugPrintAuthToken(kDebugMode),
|
||||
;
|
||||
|
||||
/// Determines whether the given [Feature] is enabled by default when the
|
||||
/// application launches. Features can be enabled or disabled during runtime,
|
||||
/// regardless of this value.
|
||||
final bool enabledAtStartup;
|
||||
|
||||
const Feature(this.enabledAtStartup);
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import "package:arcane_framework/arcane_framework.dart";
|
||||
import "package:dio/dio.dart";
|
||||
import "package:flutter_secure_storage/flutter_secure_storage.dart";
|
||||
import "package:get_it/get_it.dart";
|
||||
|
||||
abstract class AppInjector {
|
||||
static final GetIt getIt = GetIt.I;
|
||||
|
||||
static void _registerApis() {
|
||||
getIt.registerSingleton<MyApi>(
|
||||
SetupApi(getIt<SecureStorageRepository>()),
|
||||
);
|
||||
}
|
||||
|
||||
static void _registerHelpers() {
|
||||
getIt.registerSingleton<FlutterSecureStorage>(
|
||||
const FlutterSecureStorage(
|
||||
aOptions: AndroidOptions(
|
||||
encryptedSharedPreferences: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
getIt.registerSingleton<SecureStorageRepository>(
|
||||
SecureStorageRepository(getIt<FlutterSecureStorage>()),
|
||||
);
|
||||
getIt.registerLazySingleton<AuthorizationInterceptor>(
|
||||
() => AuthorizationInterceptor(),
|
||||
);
|
||||
getIt.registerLazySingleton<Dio>(
|
||||
() => DioHelper.createDioInstance(getIt),
|
||||
);
|
||||
}
|
||||
|
||||
static Future<void> init() async {
|
||||
Arcane.log(
|
||||
"Initializing injector...",
|
||||
level: Level.info,
|
||||
);
|
||||
|
||||
await getIt.reset();
|
||||
_registerHelpers();
|
||||
_registerApis();
|
||||
await getIt.allReady();
|
||||
|
||||
await IdService.I.init();
|
||||
|
||||
Arcane.log(
|
||||
"Injector initialized.",
|
||||
level: Level.info,
|
||||
);
|
||||
}
|
||||
|
||||
static void resetDio() {
|
||||
if (getIt.isRegistered<Dio>()) {
|
||||
getIt.unregister<Dio>();
|
||||
}
|
||||
|
||||
getIt.registerLazySingleton<Dio>(
|
||||
() => DioHelper.createDioInstance(getIt),
|
||||
);
|
||||
}
|
||||
|
||||
static Future<void> configureAsDebug() async {
|
||||
Arcane.log(
|
||||
"Unregistering production APIs and replacing with Debug versions",
|
||||
level: Level.fatal,
|
||||
);
|
||||
|
||||
getIt.unregister<MyApi>();
|
||||
|
||||
await getIt.allReady();
|
||||
|
||||
getIt.registerSingleton<SecureStorageRepository>(
|
||||
DebugSecureStorageRepository(),
|
||||
);
|
||||
|
||||
final SecureStorageRepository storage = getIt<SecureStorageRepository>();
|
||||
|
||||
getIt.registerSingleton<MyApi>(DebugMyApi(storage));
|
||||
|
||||
await getIt.allReady();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import "package:arcane_framework/arcane_framework.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_bloc/flutter_bloc.dart";
|
||||
import "package:get_it/get_it.dart";
|
||||
|
||||
class AppStateProvider extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
const AppStateProvider({
|
||||
required this.child,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ValueKey key = ValueKey(
|
||||
"${context.watch<ArcaneEnvironment>().state.name}-${IdService.I.sessionId.value}",
|
||||
);
|
||||
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
key: key,
|
||||
create: (context) => MyBloc(GetIt.I<MyApi>()),
|
||||
),
|
||||
],
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user