From 515c7fb5b195c79c0f90b9ba74853dd851bb5f49 Mon Sep 17 00:00:00 2001 From: Hans Kokx Date: Tue, 13 May 2025 14:31:21 +0200 Subject: [PATCH] Fixes the notifier for feature flags and updates the example Signed-off-by: Hans Kokx --- example/lib/main.dart | 237 ++++++++++-------- .../feature_flags/feature_flags_service.dart | 20 +- 2 files changed, 138 insertions(+), 119 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index c10f960..fbf0780 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -147,42 +147,47 @@ class _ArcaneLoggingExampleState extends State { @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(16.0), - child: SizedBox( - height: 200, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Logging", - style: Theme.of(context).textTheme.headlineSmall, + return ValueListenableBuilder( + valueListenable: Arcane.features.notifier, + builder: (context, enabledFeatures, _) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: SizedBox( + height: 200, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Logging", + style: Theme.of(context).textTheme.headlineSmall, + ), + if (latestLogs.isEmpty) + Text( + "Log messages will appear here", + style: Theme.of(context).textTheme.labelSmall?.copyWith( + fontStyle: FontStyle.italic, + ), + ), + if (Feature.logging.disabled) + Text( + "Logging feature is disabled.", + style: Theme.of(context).textTheme.labelSmall?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + Expanded( + child: ListView.builder( + itemCount: latestLogs.length, + itemBuilder: (context, index) { + return Text(latestLogs[index]); + }, + ), + ), + ], ), - if (latestLogs.isEmpty) - Text( - "Log messages will appear here", - style: Theme.of(context).textTheme.labelSmall?.copyWith( - fontStyle: FontStyle.italic, - ), - ), - if (Feature.logging.disabled) - Text( - "Logging feature is disabled.", - style: Theme.of(context).textTheme.labelSmall?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - Expanded( - child: ListView.builder( - itemCount: latestLogs.length, - itemBuilder: (context, index) { - return Text(latestLogs[index]); - }, - ), - ), - ], - ), - ), + ), + ); + }, ); } } @@ -200,47 +205,52 @@ class ArcaneAuthExample extends StatelessWidget { @override Widget build(BuildContext context) { - return Card( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: ValueListenableBuilder( - valueListenable: Arcane.auth.isSignedIn, - builder: (context, isSignedIn, _) { - return Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text( - "Authentication", - style: Theme.of(context).textTheme.headlineSmall, - ), - ElevatedButton( - onPressed: Feature.authentication.enabled - ? () async { - if (isSignedIn) { - await Arcane.auth.logOut(); - } else { - await Arcane.auth.login( - input: ( - email: "email", - password: "password", - ), - ); - } - } - : null, - child: Text( - isSignedIn ? "Sign out" : "Sign in", - ), - ), - Center( - child: Text("Status: ${Arcane.auth.status.name}"), - ), - ], - ); - }, - ), - ), + return ValueListenableBuilder( + valueListenable: Arcane.features.notifier, + builder: (context, enabledFeatures, _) { + return Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: ValueListenableBuilder( + valueListenable: Arcane.auth.isSignedIn, + builder: (context, isSignedIn, _) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + "Authentication", + style: Theme.of(context).textTheme.headlineSmall, + ), + ElevatedButton( + onPressed: Feature.authentication.enabled + ? () async { + if (isSignedIn) { + await Arcane.auth.logOut(); + } else { + await Arcane.auth.login( + input: ( + email: "email", + password: "password", + ), + ); + } + } + : null, + child: Text( + isSignedIn ? "Sign out" : "Sign in", + ), + ), + Center( + child: Text("Status: ${Arcane.auth.status.name}"), + ), + ], + ); + }, + ), + ), + ); + }, ); } } @@ -414,42 +424,47 @@ class ArcaneFeatureFlagsExample extends StatelessWidget { @override Widget build(BuildContext context) { - return Card( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text( - "Feature Flags", - style: Theme.of(context).textTheme.headlineSmall, + return ValueListenableBuilder( + valueListenable: Arcane.features.notifier, + builder: (context, enabledFeatures, _) { + return Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + "Feature Flags", + style: Theme.of(context).textTheme.headlineSmall, + ), + Expanded( + child: ListView.builder( + itemCount: Feature.values.length, + itemBuilder: (context, i) { + final Feature feature = Feature.values[i]; + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(feature.name), + Switch( + value: feature.enabled, + onChanged: (_) { + feature.enabled + ? feature.disable() + : feature.enable(); + }, + ), + ], + ); + }, + ), + ), + ], ), - Expanded( - child: ListView.builder( - itemCount: Feature.values.length, - itemBuilder: (context, i) { - final Feature feature = Feature.values[i]; - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(feature.name), - Switch( - value: feature.enabled, - onChanged: (_) { - feature.enabled - ? feature.disable() - : feature.enable(); - }, - ), - ], - ); - }, - ), - ), - ], - ), - ), + ), + ); + }, ); } } diff --git a/lib/src/services/feature_flags/feature_flags_service.dart b/lib/src/services/feature_flags/feature_flags_service.dart index 99a1512..ec04657 100644 --- a/lib/src/services/feature_flags/feature_flags_service.dart +++ b/lib/src/services/feature_flags/feature_flags_service.dart @@ -70,8 +70,7 @@ class ArcaneFeatureFlags extends ArcaneService { if (_enabledFeatures.contains(feature)) return I; - _enabledFeatures.add(feature); - _notifier.value.add(feature); + _notifier.value = [..._enabledFeatures, feature]; if (Arcane.logger.initialized) { Arcane.logger.log( @@ -100,8 +99,7 @@ class ArcaneFeatureFlags extends ArcaneService { if (!I._initialized) _init(); if (!_enabledFeatures.contains(feature)) return I; - _enabledFeatures.remove(feature); - _notifier.value.remove(feature); + _notifier.value = [..._enabledFeatures]..removeWhere((i) => i == feature); if (Arcane.logger.initialized) { Arcane.logger.log( @@ -123,8 +121,9 @@ class ArcaneFeatureFlags extends ArcaneService { /// It is called automatically when enabling or disabling features if they haven't /// already been initialized. void _init() { - _enabledFeatures.clear(); - _notifier.value.clear(); + _notifier.value = []; + + notifier.addListener(_listener); I._initialized = true; notifyListeners(); @@ -135,10 +134,15 @@ class ArcaneFeatureFlags extends ArcaneService { /// This method clears all enabled features, resets notification values, /// marks the flags as uninitialized, and notifies listeners of the changes. void reset() { - _enabledFeatures.clear(); - _notifier.value.clear(); + _notifier.value = []; I._initialized = false; notifyListeners(); } + + void _listener() { + _enabledFeatures + ..clear() + ..addAll(notifier.value); + } }