Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions lib/app/view/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class _AppState extends State<App> {
late final AppBloc appBloc;
late final AppRouter appRouter;

ThemeSettings? _lastHostThemeSettings;

@override
void initState() {
super.initState();
Expand Down Expand Up @@ -104,6 +106,16 @@ class _AppState extends State<App> {
initialTabResolver,
featureAccess.checker,
);

// A host (the configurator's realtime preview) supplies its theme through the
// tree; push it into the AppBloc so AppState stays the single source of truth.
// The theme mode is not pushed here: it comes from featureAccess
// (supportedConfig.themeMode), which build() already treats as authoritative.
final hostThemeSettings = context.watch<ThemeSettings?>();
if (hostThemeSettings != null && hostThemeSettings != _lastHostThemeSettings) {
_lastHostThemeSettings = hostThemeSettings;
appBloc.add(AppThemeSettingsChanged(hostThemeSettings));
}
}

@override
Expand Down
29 changes: 23 additions & 6 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import 'package:webtrit_phone/environment_config.dart';
import 'package:webtrit_phone/models/models.dart';
import 'package:webtrit_phone/repositories/repositories.dart';
import 'package:webtrit_phone/services/services.dart';
import 'package:webtrit_phone/theme/theme.dart';
import 'package:webtrit_phone/utils/utils.dart';

void main() {
Expand Down Expand Up @@ -66,10 +67,20 @@ void _onRootLogRecord(LogRecord record) {
}

class RootApp extends StatelessWidget {
const RootApp({super.key, required this.instanceRegistry});
const RootApp({super.key, required this.instanceRegistry, this.themeSettings, this.featureAccess});

final InstanceRegistry instanceRegistry;

/// Optional config supplied by an external host (the configurator's realtime
/// preview) and provided down the tree so the app renders it instead of its
/// own. Null in a normal run, where the app uses its bootstrap-built defaults.
///
/// The theme mode is intentionally not a separate parameter: a host drives it
/// through [featureAccess] (`supportedConfig.themeMode`), which the app already
/// treats as the authoritative mode, so there is a single source for it.
final ThemeSettings? themeSettings;
final FeatureAccess? featureAccess;

@override
Widget build(BuildContext context) {
return MultiProvider(
Expand All @@ -85,11 +96,17 @@ class RootApp extends StatelessWidget {
// Provides reactive [FeatureAccess] configuration synchronized with [SystemInfoRepository] and [RemoteConfigService].
//
// Initializes with bootstrap data and updates whenever system information or remote configuration changes.
StreamProvider<FeatureAccess>(
initialData: instanceRegistry.get<FeatureAccess>(),
create: (_) => instanceRegistry.get<FeatureAccessStreamFactory>().create(),
updateShouldNotify: (previous, next) => previous != next,
),
// A host (the configurator's realtime preview) overrides it with a plain value it re-supplies on edits.
if (featureAccess != null)
Provider<FeatureAccess>.value(value: featureAccess!)
else
StreamProvider<FeatureAccess>(
initialData: instanceRegistry.get<FeatureAccess>(),
create: (_) => instanceRegistry.get<FeatureAccessStreamFactory>().create(),
updateShouldNotify: (previous, next) => previous != next,
),
// Optional host-supplied theme; null in a normal run (App falls back to its AppBloc theme).
Provider<ThemeSettings?>.value(value: themeSettings),
Provider<SecureStorage>(create: (_) => instanceRegistry.get()),
Provider<AppPermissions>(create: (_) => instanceRegistry.get()),
Provider<AppLogger>(create: (_) => instanceRegistry.get()),
Expand Down
Loading