appfuse 0.0.1
appfuse: ^0.0.1 copied to clipboard
orchestrator for Flutter apps
// ignore_for_file: avoid_print
import 'package:appfuse/appfuse.dart';
import 'package:flutter/material.dart';
void main() {
// Wrap your entire application with the AppFuseScope widget.
// This widget handles all the initialization and provides state to your app.
runApp(
AppFuseScope(
// The list of environment configurations your app can use.
configs: App.configs,
// A map of themes for light/dark mode.
themes: App.themes,
// A map of supported languages for localization.
supportedLanguages: App.supportedLanguages,
// Your app's localization delegates.
localizationsDelegates: App.localizationsDelegates,
// The class that defines your app's asynchronous initialization steps.
init: Dependencies(),
// A widget to show while dependencies are being initialized.
placeholder: const SplashScreen(),
// The main widget of your app, displayed after initialization is complete.
app: const App(),
),
);
}
/// Create a base configuration class for your app.
/// This extends one of the built-in config types, like `JsonAssetConfig`.
class AppConfig extends JsonAssetConfig {
AppConfig({required super.path, required super.name, required super.color, super.showBanner});
/// Define typed getters for your configuration values.
/// This provides type safety and a single source of truth for config keys.
String get appName => getString('APP_NAME');
}
/// Create concrete implementations for each environment.
class ProdConfig extends AppConfig {
ProdConfig() : super(name: 'prod', path: 'config/prod.json', color: Colors.green);
}
class TestConfig extends AppConfig {
TestConfig() : super(name: 'test', path: 'config/test.json', color: Colors.red);
}
// Mock classes for the example.
class A {}
class B {}
/// Define your app's dependencies and their initialization logic.
/// This class uses the `AppFuseInitialization` mixin to hook into the startup process.
class Dependencies with AppFuseInitialization {
Dependencies();
/// A static helper method for easy access to your dependencies from anywhere
/// in the widget tree.
static Dependencies of(BuildContext context) => AppFuseScope.of(context, listen: false).init as Dependencies;
// Define late final variables for your services, repositories, etc.
late final A dependencyA;
late final B dependencyB;
/// Define the asynchronous steps required to initialize your app.
/// Each key is a descriptive name for the step, which is useful for logging.
@override
Map<String, InitializationStep> get steps => {
'initialize dependency A': (state) async {
// Perform async work like opening a database or initializing a service.
await Future.delayed(const Duration(seconds: 1));
dependencyA = A();
},
'initialize dependency B': (state) async {
final c = state.config as AppConfig;
c.appName;
final d = state.init as Dependencies;
d.dependencyA;
await Future.delayed(const Duration(seconds: 1));
dependencyB = B();
},
};
}
/// The root widget of your application.
class App extends StatelessWidget {
// Define your environment configurations, app's themes and languages.
/// AppFuse will automatically load the last used config or the first in the list.
static final configs = [TestConfig(), ProdConfig()];
/// The `themes` map defines the `ThemeData` for different brightness levels.
/// AppFuse uses this to automatically switch between light and dark themes.
/// You must provide at least a `Brightness.light` theme.
static final themes = <Brightness, ThemeData>{Brightness.light: ThemeData.light()};
/// The `appLanguages` map connects a `Locale` object to its human-readable name.
/// This is used to build a language selector UI. The key is the `Locale`
/// that Flutter uses, and the value is the string you would display to the user.
static final supportedLanguages = <Locale, String>{const Locale('en'): 'English'};
/// The `localizationsDelegates` list is a standard Flutter concept.
/// It contains the delegates needed to load translated strings and format dates/numbers
/// for the current locale. AppFuse passes this list directly to the `MaterialApp`.
/// You typically get this list from packages like `flutter_localizations`.
static const localizationsDelegates = <LocalizationsDelegate<dynamic>>[];
const App({super.key});
@override
Widget build(BuildContext context) => MaterialApp(
// Use the context extensions to get state from AppFuse.
// `watchSettings` rebuilds the widget when the value changes.
// `readSettings` gets the value once without subscribing to changes.
locale: context.currentLocale,
supportedLocales: context.readFuseState.supportedLocales,
localizationsDelegates: context.readFuseState.localizationsDelegates,
themeMode: context.watchFuseState.themeMode,
theme: context.readFuseState.lightTheme,
darkTheme: context.readFuseState.darkTheme,
home: const HomeScreen(),
);
}
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
@override
void initState() {
super.initState();
// Access any part of the AppFuse state.
// Here, we read a custom setting that might have been saved.
final onBoardingComplete = context.readFuseState.getCustomSetting<bool>('onboarding');
print('Onboarding complete: $onBoardingComplete'); // if not found, prints null
// You can also access your initialized dependencies anywhere.
final dependencyA = Dependencies.of(context).dependencyA;
print('Accessed dependency: $dependencyA');
}
@override
Widget build(BuildContext context) {
// Get the current environment configuration and its values.
// It's good practice to provide a default value in case the config is null.
final appName = context.readFuseState.getCurrentConfig<AppConfig>()?.appName ?? 'No Name';
return Scaffold(
appBar: AppBar(title: Text(appName)),
body: const Center(child: Text('Home Screen')),
);
}
}
/// A simple placeholder widget shown during initialization.
class SplashScreen extends StatelessWidget {
const SplashScreen({super.key});
@override
Widget build(BuildContext context) => const Center(child: CircularProgressIndicator());
}