Agent skill
feature-flags-altrupets-monorepo
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/feature-flags-altrupets-monorepo
SKILL.md
đźš© Skill: Feature Flags & Remote Config
đź“‹ Metadata
| Atributo | Valor |
|---|---|
| ID | flutter-feature-flags |
| Nivel | 🟡 Intermedio |
| VersiĂłn | 1.0.0 |
| Keywords | feature-flags, remote-config, firebase-remote-config, launchdarkly, ab-testing |
| Referencia | Firebase Remote Config |
🔑 Keywords para Invocación
feature-flagsremote-configfirebase-remote-configlaunchdarklyab-testingfeature-toggle@skill:feature-flags
Ejemplos de Prompts
Implementa feature-flags con firebase remote config
Setup launchdarkly para A/B testing
Configura feature toggles dinámicos
@skill:feature-flags - Sistema completo de feature flags
đź“– DescripciĂłn
Este skill cubre la implementaciĂłn de feature flags y remote configuration usando Firebase Remote Config y LaunchDarkly. Permite controlar features remotamente, hacer A/B testing, staged rollouts, y kill switches sin actualizar la app.
✅ Cuándo Usar Este Skill
- Staged rollouts de features
- A/B testing
- Kill switches de emergencia
- PersonalizaciĂłn por usuario/segmento
- Cambios de configuraciĂłn sin release
- Beta features
- Experimentos de producto
- Darkly launches
❌ Cuándo NO Usar Este Skill
- Features siempre encendidas
- No necesitas control remoto
- App completamente offline
🏗️ Estructura del Proyecto
my_app/
├── lib/
│ ├── core/
│ │ └── feature_flags/
│ │ ├── feature_flag_service.dart
│ │ ├── firebase_remote_config_service.dart
│ │ ├── feature_flag_provider.dart
│ │ └── feature_flags.dart
│ │
│ └── main.dart
│
└── test/
└── feature_flags_test.dart
📦 Dependencias
dependencies:
flutter:
sdk: flutter
# Firebase Remote Config
firebase_core: ^2.24.2
firebase_remote_config: ^4.3.8
# Provider para state management
provider: ^6.1.1
dev_dependencies:
flutter_test:
sdk: flutter
mockito: ^5.4.4
đź’» ImplementaciĂłn
1. Feature Flags Definition
// lib/core/feature_flags/feature_flags.dart
enum FeatureFlag {
newHomeScreen,
darkMode,
socialLogin,
premiumFeatures,
newCheckoutFlow,
chatSupport,
videoCall,
aiRecommendations,
}
extension FeatureFlagExtension on FeatureFlag {
String get key {
switch (this) {
case FeatureFlag.newHomeScreen:
return 'new_home_screen';
case FeatureFlag.darkMode:
return 'dark_mode_enabled';
case FeatureFlag.socialLogin:
return 'social_login_enabled';
case FeatureFlag.premiumFeatures:
return 'premium_features_enabled';
case FeatureFlag.newCheckoutFlow:
return 'new_checkout_flow';
case FeatureFlag.chatSupport:
return 'chat_support_enabled';
case FeatureFlag.videoCall:
return 'video_call_enabled';
case FeatureFlag.aiRecommendations:
return 'ai_recommendations_enabled';
}
}
bool get defaultValue {
switch (this) {
case FeatureFlag.newHomeScreen:
return false;
case FeatureFlag.darkMode:
return true;
case FeatureFlag.socialLogin:
return false;
case FeatureFlag.premiumFeatures:
return false;
case FeatureFlag.newCheckoutFlow:
return false;
case FeatureFlag.chatSupport:
return true;
case FeatureFlag.videoCall:
return false;
case FeatureFlag.aiRecommendations:
return false;
}
}
}
2. Firebase Remote Config Service
// lib/core/feature_flags/firebase_remote_config_service.dart
import 'package:firebase_remote_config/firebase_remote_config.dart';
import 'package:flutter/foundation.dart';
import 'feature_flags.dart';
class FirebaseRemoteConfigService {
static final FirebaseRemoteConfig _remoteConfig = FirebaseRemoteConfig.instance;
static bool _initialized = false;
// Initialize Remote Config
static Future<void> initialize() async {
if (_initialized) return;
try {
// Set config settings
await _remoteConfig.setConfigSettings(
RemoteConfigSettings(
fetchTimeout: const Duration(seconds: 30),
minimumFetchInterval: kDebugMode
? const Duration(minutes: 5) // Debug: 5 minutes
: const Duration(hours: 1), // Production: 1 hour
),
);
// Set default values
await _remoteConfig.setDefaults(_getDefaultValues());
// Fetch and activate
await _remoteConfig.fetchAndActivate();
// Listen to config updates (real-time)
_remoteConfig.onConfigUpdated.listen((event) async {
await _remoteConfig.activate();
print('🔄 Remote Config updated');
});
_initialized = true;
print('âś… Remote Config initialized');
} catch (e) {
print('❌ Remote Config initialization error: $e');
}
}
// Get default values map
static Map<String, dynamic> _getDefaultValues() {
return Map.fromEntries(
FeatureFlag.values.map(
(flag) => MapEntry(flag.key, flag.defaultValue),
),
);
}
// Check if feature is enabled
static bool isFeatureEnabled(FeatureFlag flag) {
try {
return _remoteConfig.getBool(flag.key);
} catch (e) {
print('Error getting feature flag ${flag.key}: $e');
return flag.defaultValue;
}
}
// Get string value
static String getString(String key, {String defaultValue = ''}) {
try {
return _remoteConfig.getString(key);
} catch (e) {
return defaultValue;
}
}
// Get int value
static int getInt(String key, {int defaultValue = 0}) {
try {
return _remoteConfig.getInt(key);
} catch (e) {
return defaultValue;
}
}
// Get double value
static double getDouble(String key, {double defaultValue = 0.0}) {
try {
return _remoteConfig.getDouble(key);
} catch (e) {
return defaultValue;
}
}
// Get JSON value
static Map<String, dynamic>? getJson(String key) {
try {
final jsonString = _remoteConfig.getString(key);
return jsonDecode(jsonString) as Map<String, dynamic>;
} catch (e) {
return null;
}
}
// Force fetch (for debugging)
static Future<void> forceFetch() async {
try {
await _remoteConfig.fetch();
await _remoteConfig.activate();
print('âś… Remote Config force fetched');
} catch (e) {
print('❌ Force fetch error: $e');
}
}
// Get all values (for debugging)
static Map<String, RemoteConfigValue> getAllValues() {
return _remoteConfig.getAll();
}
// Get fetch status
static RemoteConfigFetchStatus get fetchStatus => _remoteConfig.lastFetchStatus;
// Get last fetch time
static DateTime get lastFetchTime => _remoteConfig.lastFetchTime;
}
3. Feature Flag Service
// lib/core/feature_flags/feature_flag_service.dart
import 'package:flutter/foundation.dart';
import 'firebase_remote_config_service.dart';
import 'feature_flags.dart';
class FeatureFlagService extends ChangeNotifier {
static final FeatureFlagService _instance = FeatureFlagService._internal();
factory FeatureFlagService() => _instance;
FeatureFlagService._internal();
final Map<FeatureFlag, bool> _localOverrides = {};
bool _debugMode = kDebugMode;
// Initialize
Future<void> initialize() async {
await FirebaseRemoteConfigService.initialize();
notifyListeners();
}
// Check if feature is enabled
bool isEnabled(FeatureFlag flag) {
// Check local overrides first (for debugging)
if (_localOverrides.containsKey(flag)) {
return _localOverrides[flag]!;
}
// Check remote config
return FirebaseRemoteConfigService.isFeatureEnabled(flag);
}
// Enable feature locally (debug only)
void enableLocally(FeatureFlag flag) {
if (_debugMode) {
_localOverrides[flag] = true;
notifyListeners();
}
}
// Disable feature locally (debug only)
void disableLocally(FeatureFlag flag) {
if (_debugMode) {
_localOverrides[flag] = false;
notifyListeners();
}
}
// Clear local overrides
void clearLocalOverrides() {
_localOverrides.clear();
notifyListeners();
}
// Get all feature states
Map<FeatureFlag, bool> getAllFeatureStates() {
return Map.fromEntries(
FeatureFlag.values.map(
(flag) => MapEntry(flag, isEnabled(flag)),
),
);
}
// Refresh config
Future<void> refresh() async {
await FirebaseRemoteConfigService.forceFetch();
notifyListeners();
}
// Get config value
T getConfigValue<T>(String key, T defaultValue) {
if (T == String) {
return FirebaseRemoteConfigService.getString(key, defaultValue: defaultValue as String) as T;
} else if (T == int) {
return FirebaseRemoteConfigService.getInt(key, defaultValue: defaultValue as int) as T;
} else if (T == double) {
return FirebaseRemoteConfigService.getDouble(key, defaultValue: defaultValue as double) as T;
} else if (T == bool) {
// Custom bool config
try {
return FirebaseRemoteConfigService.isFeatureEnabled(
FeatureFlag.values.firstWhere((flag) => flag.key == key),
) as T;
} catch (e) {
return defaultValue;
}
}
return defaultValue;
}
// A/B Test variant
String getABTestVariant(String experimentKey) {
return FirebaseRemoteConfigService.getString(experimentKey, defaultValue: 'control');
}
}
4. Feature Flag Provider
// lib/core/feature_flags/feature_flag_provider.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'feature_flag_service.dart';
import 'feature_flags.dart';
class FeatureFlagProvider extends ChangeNotifierProvider<FeatureFlagService> {
FeatureFlagProvider({
Key? key,
Widget? child,
}) : super(
key: key,
create: (_) => FeatureFlagService(),
child: child,
);
// Helper to access service
static FeatureFlagService of(BuildContext context, {bool listen = false}) {
return Provider.of<FeatureFlagService>(context, listen: listen);
}
}
// Widget to conditionally show features
class FeatureGate extends StatelessWidget {
final FeatureFlag flag;
final Widget child;
final Widget? fallback;
const FeatureGate({
Key? key,
required this.flag,
required this.child,
this.fallback,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final service = FeatureFlagProvider.of(context, listen: true);
final isEnabled = service.isEnabled(flag);
return isEnabled ? child : (fallback ?? const SizedBox.shrink());
}
}
// Widget for A/B Testing
class ABTestVariant extends StatelessWidget {
final String experimentKey;
final Map<String, Widget> variants;
final Widget? defaultWidget;
const ABTestVariant({
Key? key,
required this.experimentKey,
required this.variants,
this.defaultWidget,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final service = FeatureFlagProvider.of(context, listen: true);
final variant = service.getABTestVariant(experimentKey);
return variants[variant] ?? defaultWidget ?? const SizedBox.shrink();
}
}
5. Usage Examples
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize Firebase
await Firebase.initializeApp();
// Initialize Feature Flags
final featureFlagService = FeatureFlagService();
await featureFlagService.initialize();
runApp(
FeatureFlagProvider(
child: const MyApp(),
),
);
}
// Example: Conditional UI
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home')),
body: Column(
children: [
// Show new home screen if feature is enabled
FeatureGate(
flag: FeatureFlag.newHomeScreen,
child: const NewHomeScreen(),
fallback: const LegacyHomeScreen(),
),
// Social login buttons
FeatureGate(
flag: FeatureFlag.socialLogin,
child: Column(
children: [
ElevatedButton(
onPressed: () {},
child: const Text('Login with Google'),
),
ElevatedButton(
onPressed: () {},
child: const Text('Login with Facebook'),
),
],
),
),
// Chat support
FeatureGate(
flag: FeatureFlag.chatSupport,
child: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.chat),
),
),
],
),
);
}
}
// Example: A/B Test
class ProductScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ABTestVariant(
experimentKey: 'checkout_button_experiment',
variants: {
'control': ElevatedButton(
onPressed: () {},
child: const Text('Buy Now'),
),
'variant_a': ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(backgroundColor: Colors.green),
child: const Text('Add to Cart'),
),
'variant_b': ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(backgroundColor: Colors.orange),
child: const Text('Purchase Now - 10% Off!'),
),
},
defaultWidget: ElevatedButton(
onPressed: () {},
child: const Text('Buy Now'),
),
);
}
}
// Example: Check in code
class CheckoutService {
Future<void> processCheckout() async {
final service = FeatureFlagService();
if (service.isEnabled(FeatureFlag.newCheckoutFlow)) {
// Use new checkout flow
await _newCheckout();
} else {
// Use legacy checkout
await _legacyCheckout();
}
}
Future<void> _newCheckout() async {
// Implementation
}
Future<void> _legacyCheckout() async {
// Implementation
}
}
6. Debug Screen
// lib/screens/feature_flags_debug_screen.dart
import 'package:flutter/material.dart';
class FeatureFlagsDebugScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final service = FeatureFlagProvider.of(context, listen: true);
final allStates = service.getAllFeatureStates();
return Scaffold(
appBar: AppBar(
title: const Text('Feature Flags Debug'),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: () => service.refresh(),
),
IconButton(
icon: const Icon(Icons.clear),
onPressed: () => service.clearLocalOverrides(),
),
],
),
body: ListView.builder(
itemCount: allStates.length,
itemBuilder: (context, index) {
final entry = allStates.entries.elementAt(index);
final flag = entry.key;
final isEnabled = entry.value;
return SwitchListTile(
title: Text(flag.key),
subtitle: Text('Default: ${flag.defaultValue}'),
value: isEnabled,
onChanged: (value) {
if (value) {
service.enableLocally(flag);
} else {
service.disableLocally(flag);
}
},
);
},
),
);
}
}
🎯 Mejores Prácticas
1. Default Values
âś… DO: Siempre define defaults seguros
bool get defaultValue {
// New features: false (safe default)
// Existing features: true (don't break existing)
return false;
}
2. Kill Switches
âś… DO: Implementa kill switches para features crĂticas
if (!service.isEnabled(FeatureFlag.paymentGateway)) {
// Show maintenance message
return MaintenanceScreen();
}
3. Analytics
âś… DO: Trackea uso de features
if (service.isEnabled(FeatureFlag.newFeature)) {
AnalyticsService.trackEvent('feature_used', properties: {
'feature': 'newFeature',
'variant': service.getABTestVariant('experiment'),
});
}
🚨 Troubleshooting
Config Not Updating
// Force fetch in debug
await FirebaseRemoteConfigService.forceFetch();
// Check fetch status
print('Fetch status: ${FirebaseRemoteConfigService.fetchStatus}');
print('Last fetch: ${FirebaseRemoteConfigService.lastFetchTime}');
Default Values Not Working
// Verify setDefaults was called
await _remoteConfig.setDefaults(_getDefaultValues());
📚 Recursos
VersiĂłn: 1.0.0 Ăšltima actualizaciĂłn: Diciembre 2025 Total lĂneas: 1,100+
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?