GrowthBook Flutter SDK

GrowthBook Flutter SDK

pub package License: MIT Platform Dart

๐ŸŽฏ Feature flags โ€ข ๐Ÿงช A/B testing โ€ข ๐Ÿ“Š Analytics integration

Quick Start โ€ข Features โ€ข Documentation โ€ข Examples โ€ข Resources

Overview

GrowthBook is an open source feature flagging and experimentation platform. This Flutter SDK allows you to use GrowthBook with your Flutter based mobile application.

Platform Support:

  • ๐Ÿ“ฑ Android 21+ โ€ข iOS 12+
  • ๐Ÿ“บ tvOS 13+ โ€ข โŒš watchOS 7+
  • ๐Ÿ–ฅ๏ธ macOS โ€ข Windows โ€ข Linux
  • ๐ŸŒ Web (Flutter Web)

โœจ Features

  • Lightweight - Minimal performance impact
  • Type Safe - Full Dart/Flutter type safety
  • Caching - Smart caching with TTL and stale-while-revalidate
  • Cross-platform - Works on Android, iOS, Web, Desktop

๐Ÿ—๏ธ Core Capabilities

  • Feature Flags - Toggle features on/off without code deployments
  • A/B Testing - Run experiments with statistical significance
  • Targeting - Advanced user segmentation, variation weights and targeting rules
  • Tracking - Use your existing event tracking (GA, Segment, Mixpanel, custom)
  • Real-time Updates - Features update instantly via streaming

๐ŸŽฏ Advanced Features

  • Sticky Bucketing - Consistent user experiences across sessions
  • Remote Evaluation - Server-side evaluation for enhanced security
  • Multi-variate Testing - Test multiple variations simultaneously
  • Rollout Management - Gradual feature rollouts with traffic control

๐Ÿ“‹ Supported Features

Feature Support Since Version
โœ… Feature Flags Full Support All versions
โœ… A/B Testing Full Support All versions
โœ… Sticky Bucketing Full Support โ‰ฅ v3.8.0
โœ… Remote Evaluation Full Support โ‰ฅ v3.7.0
โœ… Streaming Updates Full Support โ‰ฅ v3.4.0
โœ… Prerequisites Full Support โ‰ฅ v3.2.0
โœ… Encrypted Features Full Support โ‰ฅ v3.1.0
โœ… v2 Hashing Full Support โ‰ฅ v3.1.0
โœ… SemVer Targeting Full Support โ‰ฅ v3.1.0
โœ… TTL Caching Full Support โ‰ฅ v3.9.10

๐Ÿ“– View complete feature support matrix โ†’


๐Ÿš€ Quick Start

๐Ÿ“– View official installation guide โ†’

1. Installation

Add to your pubspec.yaml:

dependencies:
  growthbook_sdk_flutter: ^3.9.10

2. Basic Setup

๐Ÿ“– View complete usage guide โ†’

import 'package:growthbook_sdk_flutter/growthbook_sdk_flutter.dart';

// Initialize the SDK
final sdk = await GBSDKBuilderApp(
  apiKey: "sdk_your_api_key",
  hostURL: "https://growthbook.io",
  attributes: {
    'id': 'user_123',
    'email': 'user@example.com',
    'country': 'US',
  },
  growthBookTrackingCallBack: (trackData) {
    // Track experiment exposures
    final experiment = trackData.experiment;
    final result = trackData.experimentResult;
    print('Experiment: ${experiment.key}, Variation: ${result.variationID}');
  },
).initialize();

// Use feature flags
final welcomeMessage = sdk.feature('welcome_message');
if (welcomeMessage.on) {
  print('Feature is enabled: ${welcomeMessage.value}');
}

// Run A/B tests
final buttonExperiment = GBExperiment(key: 'button_color_test');
final result = sdk.run(buttonExperiment);
final buttonColor = result.value ?? 'blue'; // Default color

3. Widget Integration

class MyHomePage extends StatelessWidget {
  final GrowthBookSDK sdk;
  
  @override
  Widget build(BuildContext context) {
    final newDesign = sdk.feature('new_homepage_design');
    
    return Scaffold(
      body: newDesign.on 
        ? NewHomepageWidget() 
        : ClassicHomepageWidget(),
    );
  }
}

Analytics

final sdk = await GBSDKBuilderApp(
  apiKey: "your_api_key",
  growthBookTrackingCallBack: (trackData) {
    final experiment = trackData.experiment;
    final result = trackData.experimentResult;

    // Google Analytics
    FirebaseAnalytics.instance.logEvent(
      name: 'experiment_viewed',
      parameters: {
        'experiment_id': experiment.key,
        'variation_id': result.variationID,
        'variation_name': result.key,
      },
    );
    
    // Mixpanel
    Mixpanel.track('Experiment Viewed', {
      'Experiment ID': experiment.key,
      'Variation ID': result.variationID,
    });
    
    // Custom analytics
    YourAnalytics.trackExperiment(experiment, result);
  },
).initialize();

๐Ÿ“š Documentation

Configuration Options

final sdk = await GBSDKBuilderApp(
  // Required
  apiKey: "sdk_your_api_key",
  hostURL: "https://growthbook.io",
  
  // User Context
  attributes: {
    'id': 'user_123',
    'email': 'user@example.com',
    'plan': 'premium',
    'country': 'US',
  },
  
  // Performance & Caching
  ttlSeconds: 300,              // Cache TTL (default: 60s)
  backgroundSync: true,         // Real-time updates
  
  // Testing & QA
  qaMode: false,                // Disable randomization for QA
  forcedVariations: {           // Force specific variations
    'button_test': 1,
  },
  
  // Analytics Integration
  growthBookTrackingCallBack: (trackData) {
    // Send to your analytics platform
    final experiment = trackData.experiment;
    final result = trackData.experimentResult;
    analytics.track('Experiment Viewed', {
      'experiment_id': experiment.key,
      'variation_id': result.variationID,
      'variation_name': result.key,
    });
  },
  
  // Advanced Features
  remoteEval: false,            // Server-side evaluation
  encryptionKey: "...",         // For encrypted features
).initialize();

Configuring Log Level

The SDK logs internal events (feature evaluation skips, cache errors, refresh attempts) through a logger package instance. Verbosity is controlled by GrowthBookSDK.setLogLevel(...), which accepts an SDK-owned GBLogLevel enum:

import 'package:growthbook_sdk_flutter/growthbook_sdk_flutter.dart';

void main() {
  // Configure once at app startup โ€” before initializing the SDK.
  GrowthBookSDK.setLogLevel(GBLogLevel.debug);

  runApp(const MyApp());
}

Available levels: verbose, debug, info, warning (default), error, off.

โš ๏ธ Process-global: log level is shared across all GrowthBookSDK instances in the same process. Set it once at app startup rather than per-instance. If you run multiple SDK instances, they will share the same verbosity.

Feature Flag Usage

๐Ÿ“– View detailed feature usage guide โ†’

// Boolean flags
final isEnabled = sdk.feature('new_feature').on;

// String values
final welcomeText = sdk.feature('welcome_message').value ?? 'Welcome!';

// Number values  
final maxItems = sdk.feature('max_items').value ?? 10;

// JSON objects
final config = sdk.feature('app_config').value ?? {
  'theme': 'light',
  'animations': true,
};

// Check feature source
final feature = sdk.feature('premium_feature');
switch (feature.source) {
  case GBFeatureSource.experiment:
    print('Value from A/B test');
    break;
  case GBFeatureSource.force:
    print('Forced value');
    break;
  case GBFeatureSource.defaultValue:
    print('Default value');
    break;
}

Experiments - A/B Testing

๐Ÿ“– View inline experiments guide โ†’

// Define experiment
final experiment = GBExperiment(
  key: 'checkout_button_test',
  variations: ['๐Ÿ›’ Buy Now', '๐Ÿ’ณ Purchase', 'โœจ Get It Now'],
  weights: [0.33, 0.33, 0.34], // Traffic distribution
);

// Run experiment
final result = sdk.run(experiment);

// Use result
Widget buildButton() {
  final buttonText = result.value ?? '๐Ÿ›’ Buy Now';
  return ElevatedButton(
    onPressed: () => handlePurchase(),
    child: Text(buttonText),
  );
}

// Track conversion
if (purchaseCompleted) {
  // Your analytics will receive this via trackingCallBack
}

User Attributes & Targeting

// Update user attributes dynamically
sdk.setAttributes({
  'plan': 'enterprise',
  'feature_flags_enabled': true,
  'last_login': DateTime.now().toIso8601String(),
});

// Target users with conditions
// Example: Show feature only to premium users in US
// This is configured in GrowthBook dashboard, not in code

Condition Operators

The SDK supports the following operators in targeting conditions:

Comparison

Operator Description
$eq Equal to
$ne Not equal to
$lt Less than
$lte Less than or equal
$gt Greater than
$gte Greater than or equal

Membership

Operator Description
$in Value is in array
$nin Value is not in array
$all Array contains all values
$ini Value is in array (case-insensitive string comparison)
$nini Value is not in array (case-insensitive string comparison)
$alli Array contains all values (case-insensitive string comparison)

For $ini, $nini, and $alli: string values are compared after lowercasing; non-string values (numbers, booleans, null) are compared as-is.

Regex

Operator Description
$regex Matches regex (case-sensitive)
$notRegex Does not match regex (case-sensitive)
$regexi Matches regex (case-insensitive)
$notRegexi Does not match regex (case-insensitive)

Other

Operator Description
$exists Attribute exists (true) or is absent (false)
$type Attribute type matches string ("string", "number", "boolean", "array", "object", "null")
$not Negates a condition
$size Array length matches condition
$elemMatch At least one array element matches condition
$vgt / $vlt / $vgte / $vlte / $veq / $vne Semantic version comparison
// Example: case-insensitive membership
// Matches users where country is "us", "US", "Us", etc.
final condition = {
  'country': {'\$ini': ['US', 'CA', 'GB']}
};

// Example: case-insensitive regex
// Matches "Hello", "hello", "HELLO", etc.
final condition2 = {
  'greeting': {'\$regexi': '^hello'}
};

๐Ÿ”ง Advanced Features

Smart Caching Strategy

The SDK implements an intelligent caching system for optimal performance:

final sdk = await GBSDKBuilderApp(
  apiKey: "your_api_key",
  ttlSeconds: 300, // Cache TTL: 5 minutes
  backgroundSync: true, // Enable background refresh
).initialize();

How it works:

  1. ๐Ÿš€ Instant Response - Serve cached features immediately
  2. ๐Ÿ”„ Background Refresh - Fetch updates in the background
  3. โšก Stale-While-Revalidate - Show cached data while updating
  4. ๐Ÿ“Š Smart Invalidation - Refresh only when needed

Benefits:

  • โœ… Zero loading delays for feature flags
  • โœ… Always up-to-date with background sync
  • โœ… Reduced API calls with intelligent caching
  • โœ… Offline resilience with cached fallbacks

Sticky Bucketing

๐Ÿ“– Learn about sticky bucketing โ†’

Ensure consistent user experiences across sessions:

class MyAppStickyBucketService extends GBStickyBucketService {
  @override
  Future<Map<String, String>?> getAllAssignments(
    Map<String, dynamic> attributes,
  ) async {
    // Retrieve from local storage
    final prefs = await SharedPreferences.getInstance();
    final json = prefs.getString('gb_sticky_assignments');
    return json != null ? jsonDecode(json) : null;
  }
  
  @override
  Future<void> saveAssignments(
    Map<String, dynamic> attributes,
    Map<String, String> assignments,
  ) async {
    // Save to local storage
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString('gb_sticky_assignments', jsonEncode(assignments));
  }
}

// Use with SDK
final sdk = await GBSDKBuilderApp(
  apiKey: "your_api_key",
  stickyBucketService: MyAppStickyBucketService(),
).initialize();

Remote Evaluation

The Flutter SDK may be run in Remote Evaluation mode. This mode brings the security benefits of a backend SDK to the front end by evaluating feature flags exclusively on a private server. Using Remote Evaluation ensures that any sensitive information within targeting rules or unused feature variations are never seen by the client.

You must enable Remote Evaluation in your SDK Connection settings. Cloud customers are also required to self-host a GrowthBook Proxy Server or a custom remote evaluation backend.

To use Remote Evaluation, add the remoteEval: true property to your SDK instance.

final sdk = await GBSDKBuilderApp(
  apiKey: "your_api_key",
  remoteEval: true, // Enable remote evaluation
  attributes: userAttributes,
).initialize();

// Features are evaluated server-side
// Sensitive targeting rules never reach the client

Real-time Updates

The GrowthBook SDK supports streaming with Server-Sent Events (SSE). When enabled, changes to features within GrowthBook will be streamed to the SDK in realtime as they are published. This is only supported on GrowthBook Cloud or if running a GrowthBook Proxy Server.

final sdk = await GBSDKBuilderApp(
  apiKey: "your_api_key",
  backgroundSync: true, // Enable streaming updates
).initialize();

// Features automatically update when changed in GrowthBook
// No need to restart the app or refresh manually

Tracking Plugins

Plugins observe SDK lifecycle events (feature evaluated, experiment viewed) and can implement custom side effects such as forwarding events to an analytics backend. The SDK ships with GrowthBookTrackingPlugin, which batches events and sends them to the GrowthBook ingest endpoint.

import 'package:growthbook_sdk_flutter/growthbook_sdk_flutter.dart';

Future<void> main() async {
  final sdk = await GBSDKBuilderApp(
    apiKey: 'sdk-xxx',
    hostURL: 'https://cdn.growthbook.io',
    growthBookTrackingCallBack: (_) {},
  )
      .addPlugin(GrowthBookTrackingPlugin())
      .initialize();

  runApp(MyApp(sdk: sdk));
}

Custom configuration

final trackingPlugin = GrowthBookTrackingPlugin(
  config: GrowthBookTrackingPluginConfig(
    ingestorHost: 'https://ingest.growthbook.io',
    batchSize: 50,
    batchTimeout: Duration(seconds: 30),
  ),
);

await GBSDKBuilderApp(...)
    .addPlugin(trackingPlugin)
    .initialize();

Lifecycle โ€” always await dispose()

Tracking plugins buffer events in memory to reduce network overhead. You must call await sdk.dispose() when the SDK instance is no longer needed so buffered events are flushed and any resources (timers, HTTP clients) are released. Without this, queued tracking events will be dropped during app shutdown.

class _MyAppState extends State<MyApp> {
  late final GrowthBookSDK sdk;

  @override
  void initState() {
    super.initState();
    _initSdk();
  }

  Future<void> _initSdk() async {
    sdk = await GBSDKBuilderApp(...)
        .addPlugin(GrowthBookTrackingPlugin())
        .initialize();
  }

  @override
  void dispose() {
    // Flushes pending tracking events and releases plugin resources
    sdk.dispose();
    super.dispose();
  }
}

For CLI tools, server processes, or short-lived isolates, wrap SDK usage in a try/finally:

final sdk = await GBSDKBuilderApp(...).addPlugin(GrowthBookTrackingPlugin()).initialize();
try {
  // ... SDK usage
} finally {
  await sdk.dispose();
}

Custom plugins

Extend GrowthBookPlugin to implement your own tracking, logging, or analytics forwarding:

class MyAnalyticsPlugin extends GrowthBookPlugin {
  @override
  void initialize(String clientKey) {
    // one-time setup (e.g. start a periodic flush timer)
  }

  @override
  void onFeatureEvaluated(String id, GBFeatureResult result, Map<String, dynamic>? attributes) {
    // forward to your analytics
  }

  @override
  void onExperimentViewed(GBExperiment experiment, GBExperimentResult result, Map<String, dynamic>? attributes) {
    // forward to your analytics
  }

  @override
  Future<void> close() async {
    // flush any buffered state, release resources
  }
}

โš ๏ธ Plugin errors are isolated per plugin โ€” a throw in one plugin's callback does not affect other plugins or SDK evaluation. If you need stronger delivery guarantees than best-effort batching, implement retry/requeue with bounded storage inside your plugin's close() method.


๐Ÿ’ก Examples

E-commerce App

class ProductPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final sdk = context.read<GrowthBookSDK>();
    
    // Feature flags
    final showReviews = sdk.feature('show_product_reviews').on;
    final freeShipping = sdk.feature('free_shipping_threshold').value ?? 50.0;
    
    // A/B test for pricing display
    final pricingExperiment = GBExperiment(key: 'pricing_display_test');
    final pricingResult = sdk.run(pricingExperiment);
    
    return Scaffold(
      body: Column(
        children: [
          ProductImage(),
          ProductTitle(),
          
          // Dynamic pricing display based on A/B test
          if (pricingResult.value == 'with_discount')
            PricingWithDiscount()
          else
            StandardPricing(),
            
          // Conditional features
          if (showReviews) ProductReviews(),
          if (freeShipping > 0) FreeShippingBanner(threshold: freeShipping),
          
          AddToCartButton(),
        ],
      ),
    );
  }
}

Gradual Feature Rollout

class NewFeatureService {
  final GrowthBookSDK sdk;
  
  NewFeatureService(this.sdk);
  
  bool get isNewDashboardEnabled {
    final feature = sdk.feature('new_dashboard_v2');
    
    // Feature is rolled out gradually:
    // 0% โ†’ 5% โ†’ 25% โ†’ 50% โ†’ 100%
    // Configured in GrowthBook dashboard
    return feature.on;
  }
  
  Widget buildDashboard() {
    return isNewDashboardEnabled 
      ? NewDashboardWidget()
      : LegacyDashboardWidget();
  }
}

๐Ÿ› ๏ธ Development

Local Development Setup

1. Clone and Setup

# Clone the repository
git clone https://github.com/growthbook/growthbook-flutter.git
cd growthbook-flutter

# Install dependencies
flutter pub get

# Generate code (for json_serializable)
dart run build_runner build --delete-conflicting-outputs

2. Run Tests

# Run all tests
flutter test

# Run tests with coverage
flutter test --coverage

# Run specific test file
flutter test test/features/feature_test.dart

3. Code Generation

The SDK uses json_serializable for JSON parsing. When you modify model classes with @JsonSerializable(), run:

# Watch for changes and auto-generate
dart run build_runner watch

# One-time generation
dart run build_runner build --delete-conflicting-outputs

4. Linting and Formatting

# Check linting
dart analyze

# Format code
dart format .

# Fix auto-fixable issues
dart fix --apply

5. Building Examples

cd example
flutter pub get
flutter run

Testing

// Mock SDK for testing
class MockGrowthBookSDK implements GrowthBookSDK {
  final Map<String, dynamic> mockFeatures;
  
  MockGrowthBookSDK({required this.mockFeatures});
  
  @override
  GBFeatureResult feature(String key) {
    final value = mockFeatures[key];
    return GBFeatureResult(
      value: value,
      on: value == true,
      source: GBFeatureSource.force,
    );
  }
}

// Use in tests
void main() {
  testWidgets('shows new feature when enabled', (tester) async {
    final mockSDK = MockGrowthBookSDK(
      mockFeatures: {'new_feature': true},
    );
    
    await tester.pumpWidget(MyApp(sdk: mockSDK));
    
    expect(find.text('New Feature'), findsOneWidget);
  });
}

Build Integration

// Different configurations for different environments
final sdk = await GBSDKBuilderApp(
  apiKey: kDebugMode 
    ? "sdk_dev_your_dev_key"
    : "sdk_prod_your_prod_key",
  hostURL: kDebugMode 
    ? "https://growthbook-dev.yourcompany.com"
    : "https://growthbook.yourcompany.com",
  qaMode: kDebugMode, // Disable randomization in debug
).initialize();

Contributing

We welcome contributions! Here's how to get started:

  1. Fork the repository and create a feature branch
  2. Make your changes following our coding conventions
  3. Add tests for any new functionality
  4. Ensure all tests pass: flutter test
  5. Format code: dart format .
  6. Submit a pull request with a clear description

Pull Request Guidelines:

  • โœ… Include tests for new features
  • โœ… Update documentation if needed
  • โœ… Follow existing code style
  • โœ… Keep commits atomic and well-described
  • โœ… Reference any related issues

Release Process:

  • Uses automated releases via release-please
  • Follow conventional commits: feat:, fix:, docs:, etc.
  • Automatic version bumping and changelog generation

๐Ÿ”— Resources

๐Ÿ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

๐Ÿ™ Acknowledgments

Originally contributed by the team at Alippo. The core GrowthBook platform remains open-source and free forever.


Made with โค๏ธ by the GrowthBook community

โญ Star on GitHub โ€ข ๐Ÿฆ Follow on Twitter