GrowthBook SDK for flutter

Overview

GrowthBook SDK for flutter.

Overview

GrowthBook is an open source feature flagging and experimentation platform that makes it easy to adjust what features are shown users, and run A/B tests, without deploying new code. There are two parts to GrowthBook, the GrowthBook Application, and the SDKs which implement this functionality to your code base. This Flutter SDK allows you to use GrowthBook with your Flutter based mobile application.

  • Lightweight and fast
  • Supports
    • Android
    • iOS
    • Mac
    • Windows
  • Use your existing event tracking (GA, Segment, Mixpanel, custom)
  • Adjust variation weights and targeting without deploying new code

Installation

  1. Add GrowthBook SDK as dependency in your pubspec.yaml file.
growthbook_sdk_flutter: ^1.0.0

Integration

Integration is super easy:

  1. Create a GrowthBook API key from the GrowthBook App.
  2. Initialize the SDK at the start of your app using the API key, as below.

Now you can start/stop tests, adjust coverage and variation weights, and apply a winning variation to 100% of traffic, all within the Growth Book App without deploying code changes to your site.

", attributes: { /// Specify attributes. }, growthBookTrackingCallBack: (gbExperiment, gbExperimentResult) {}, hostURL: '', ).initialize(); ">
final GrowthBookSDK sdkInstance = GBSDKBuilderApp(
  apiKey: "",
  attributes: {
    /// Specify attributes.
  },
  growthBookTrackingCallBack: (gbExperiment, gbExperimentResult) {},
  hostURL: '',
).initialize();

There are additional properties which can be setup at the time of initialization

", attributes: { /// Specify user attributes. }, growthBookTrackingCallBack: (gbExperiment, gbExperimentResult) {}, hostURL: '', ).setNetworkDispatcher( // set network dispatcher. ) .setForcedVariations({}) . // Set forced variations setQAMode(true)// Set qamode .initialize(); ">
    final GrowthBookSDK newSdkInstance = GBSDKBuilderApp(
    apiKey: "",
    attributes: {
     /// Specify user attributes.
    },
    growthBookTrackingCallBack: (gbExperiment, gbExperimentResult) {},
    hostURL: '',
).setNetworkDispatcher(
  // set network dispatcher.
)
   .setForcedVariations({})
   . // Set forced variations
   setQAMode(true)// Set qamode
   .initialize();

Usage

  • Initialization returns SDK instance - GrowthBookSDK

    Use sdkInstance to consume below features -
  • The feature method takes a single string argument, which is the unique identifier for the feature and returns a FeatureResult object.

      GBFeatureResult feature(String id) 
  • The run method takes an Experiment object and returns an ExperimentResult

    GBExperimentResult run(GBExperiment experiment)   
  • Get Context
    GBContext getGBContext()
  • Get Features
    GBFeatures getFeatures()  

Models

/// Defines the GrowthBook context.
class GBContext {
  GBContext({
    this.apiKey,
    this.hostURL,
    this.enabled,
    this.attributes,
    this.forcedVariation,
    this.qaMode,
    this.trackingCallBack,
  });

  /// Registered API key for GrowthBook SDK.
  String? apiKey;

  /// Host URL for GrowthBook
  String? hostURL;

  /// Switch to globally disable all experiments. Default true.
  bool? enabled;

  /// Map of user attributes that are used to assign variations
  Map<String, dynamic>? attributes;

  /// Force specific experiments to always assign a specific variation (used for QA).
  Map<String, dynamic>? forcedVariation;

  /// If true, random assignment is disabled and only explicitly forced variations are used.
  bool? qaMode;

  /// A function that takes experiment and result as arguments.
  TrackingCallBack? trackingCallBack;

  /// Keys are unique identifiers for the features and the values are Feature objects.
  /// Feature definitions - To be pulled from API / Cache
  GBFeatures features = <String, GBFeature>{};
}
? variations; /// The globally unique tracking key for the experiment (default to the feature key) String? key; /// How to weight traffic between variations. Must add to 1. List? weights; /// A tuple that contains the namespace identifier, plus a range of coverage for the experiment. List? nameSpace; /// What user attribute should be used to assign variations (defaults to id) String? hashAttribute; factory GBFeatureRule.fromMap(Map mappedData) { return GBFeatureRule( nameSpace: mappedData['namespace'], condition: mappedData['condition'], coverage: (mappedData['coverage'] as num?)?.toDouble(), variations: mappedData['variations'] != null ? List.from(mappedData['variations'].map((e) => e)) : null, key: mappedData['key'], weights: mappedData['weights'] != null ? List.from(mappedData['weights']) : null, force: mappedData['force'], hashAttribute: mappedData["hashAttribute"], ); } } /// Enum For defining feature value source. @EnhancedEnum() enum GBFeatureSource { /// Queried Feature doesn't exist in GrowthBook. @EnhancedEnumValue(name: 'unknownFeature') unknownFeature, /// Default Value for the Feature is being processed. @EnhancedEnumValue(name: 'defaultValue') defaultValue, /// Forced Value for the Feature is being processed. @EnhancedEnumValue(name: 'force') force, /// Experiment Value for the Feature is being processed. @EnhancedEnumValue(name: 'experiment') experiment } /// Result for Feature class GBFeatureResult { GBFeatureResult({ this.value, this.on, this.off, this.source, this.experiment, this.experimentResult, }); /// The assigned value of the feature dynamic value; /// The assigned value cast to a boolean bool? on = false; /// The assigned value cast to a boolean and then negated bool? off = true; /// One of "unknownFeature", "defaultValue", "force", or "experiment" GBFeatureSource? source; /// When source is "experiment", this will be the Experiment object used GBExperiment? experiment; ///When source is "experiment", this will be an ExperimentResult object GBExperimentResult? experimentResult; }">
/// A Feature object consists of possible values plus rules for how to assign values to users.
class GBFeature {
  GBFeature({
    this.rules,
    this.defaultValue,
  });

  /// The default value (should use null if not specified)
  ///2 Array of Rule objects that determine when and how the defaultValue gets overridden
  List<GBFeatureRule>? rules;

  ///  The default value (should use null if not specified)
  dynamic defaultValue;

  factory GBFeature.fromMap(Map<String, dynamic> dataMap) {
    return GBFeature(
      rules: dataMap['rules'] != null
          ? List<GBFeatureRule>.from(
              (dataMap['rules'] as List).map(
                (e) => GBFeatureRule.fromMap(e),
              ),
            )
          : null,
      defaultValue: dataMap["defaultValue"],
    );
  }
}


/// Rule object consists of various definitions to apply to calculate feature value

class GBFeatureRule {
  GBFeatureRule({
    this.condition,
    this.coverage,
    this.force,
    this.variations,
    this.key,
    this.weights,
    this.nameSpace,
    this.hashAttribute,
  });

  /// Optional targeting condition
  GBCondition? condition;

  /// What percent of users should be included in the experiment (between 0 and 1, inclusive)
  double? coverage;

  /// Immediately force a specific value (ignore every other option besides condition and coverage)
  dynamic force;

  /// Run an experiment (A/B test) and randomly choose between these variations
  List<dynamic>? variations;

  /// The globally unique tracking key for the experiment (default to the feature key)
  String? key;

  /// How to weight traffic between variations. Must add to 1.
  List<double>? weights;

  /// A tuple that contains the namespace identifier, plus a range of coverage for the experiment.
  List? nameSpace;

  /// What user attribute should be used to assign variations (defaults to id)
  String? hashAttribute;

  factory GBFeatureRule.fromMap(Map<String, dynamic> mappedData) {
    return GBFeatureRule(
      nameSpace: mappedData['namespace'],
      condition: mappedData['condition'],
      coverage: (mappedData['coverage'] as num?)?.toDouble(),
      variations: mappedData['variations'] != null
          ? List<dynamic>.from(mappedData['variations'].map((e) => e))
          : null,
      key: mappedData['key'],
      weights: mappedData['weights'] != null
          ? List<double>.from(mappedData['weights'])
          : null,
      force: mappedData['force'],
      hashAttribute: mappedData["hashAttribute"],
    );
  }
}


/// Enum For defining feature value source.
@EnhancedEnum()
enum GBFeatureSource {
  /// Queried Feature doesn't exist in GrowthBook.
  @EnhancedEnumValue(name: 'unknownFeature')
  unknownFeature,

  /// Default Value for the Feature is being processed.
  @EnhancedEnumValue(name: 'defaultValue')
  defaultValue,

  /// Forced Value for the Feature is being processed.
  @EnhancedEnumValue(name: 'force')
  force,

  /// Experiment Value for the Feature is being processed.
  @EnhancedEnumValue(name: 'experiment')
  experiment
}

/// Result for Feature
class GBFeatureResult {
  GBFeatureResult({
    this.value,
    this.on,
    this.off,
    this.source,
    this.experiment,
    this.experimentResult,
  });

  /// The assigned value of the feature
  dynamic value;

  /// The assigned value cast to a boolean
  bool? on = false;

  /// The assigned value cast to a boolean and then negated
  bool? off = true;

  /// One of "unknownFeature", "defaultValue", "force", or "experiment"

  GBFeatureSource? source;

  /// When source is "experiment", this will be the Experiment object used
  GBExperiment? experiment;

  ///When source is "experiment", this will be an ExperimentResult object
  GBExperimentResult? experimentResult;
}
/// Defines a single experiment

class GBExperiment {
  GBExperiment({
    this.key,
    this.variations,
    this.namespace,
    this.condition,
    this.hashAttribute,
    this.weights,
    this.active = true,
    this.coverage,
    this.force,
  });

  factory GBExperiment.fromMap(Map<String, dynamic> map) => GBExperiment(
      key: map['key'],
      namespace: map['namespace'],
      variations: map['variations'],
      hashAttribute: map['hashAttribute'],
      weights: map['weights'],
      active: map['active'] ?? true,
      coverage: map['coverage'],
      force: map['force'],
      condition: map['condition']);

  /// The globally unique tracking key for the experiment
  String? key;

  /// The different variations to choose between
  List? variations = [];

  /// A tuple that contains the namespace identifier, plus a range of coverage for the experiment
  List? namespace;

  /// All users included in the experiment will be forced into the specific variation index
  String? hashAttribute;

  /// How to weight traffic between variations. Must add to 1.
  List? weights;

  /// If set to false, always return the control (first variation)
  bool active;

  /// What percent of users should be included in the experiment (between 0 and 1, inclusive)
  double? coverage;

  /// Optional targeting condition
  GBCondition? condition;

  /// All users included in the experiment will be forced into the specific variation index
  int? force;

  ///Check if experiment is not active.
  bool get deactivated => !active;
}

/// The result of running an Experiment given a specific Context
class GBExperimentResult {
  GBExperimentResult({
    this.inExperiment,
    this.variationID,
    this.value,
    this.hasAttributes,
    this.hashValue,
  });

  /// Whether or not the user is part of the experiment
  bool? inExperiment;

  /// The array index of the assigned variation
  int? variationID;

  /// The array value of the assigned variation
  dynamic value;

  /// The user attribute used to assign a variation
  String? hasAttributes;

  /// The value of that attribute
  String? hashValue;
}

License

This project uses the MIT license. The core GrowthBook app will always remain open and free, although we may add some commercial enterprise add-ons in the future.

Comments
  • features all always empty

    features all always empty

    I'm getting always the features map empty, Any advice?

    This is how I have implemented it

    User? user = await getUser();
      var attributes = {
        'user_id': user?.id ?? '',
        'user_name': user?.name ?? '',
        'user_email': user?.email ?? '',
        'user_phone': user?.phone ?? '',
      };
      gb = GBSDKBuilderApp(
        apiKey: AppConstants.GROWTHBOOK_API_KEY,
        attributes: attributes,
        growthBookTrackingCallBack: (gbExperiment, gbExperimentResult) {},
        hostURL: AppConstants.GROWTHBOOK_HOST_URL,
      ).initialize();
      logDebug('GrowthBook initialized');
    
    opened by engmsaleh 10
  • Backward Compatibility Flutter 3.0.0

    Backward Compatibility Flutter 3.0.0

    I try to downgrade dart version and not any found errors when you use dart version for flutter 3.0.0. So, for backward compatibility I suggest to downgrade the version. Upgrade some dependencies version to use latest version. Then update the code to follow suggestion from flutter_lints latest.

    opened by chandrabezzo 2
  • Fix case in folder name

    Fix case in folder name

    The library doesn't work if you use linux because of the folder name.

    Mac ignores the case, that's why it "works" on OSX.

    The best would be to fix all of the errors that flutter tells you: https://pub.dev/packages/growthbook_sdk_flutter/score

    That's how I recognized the casing on the folder name :)

    opened by rlopzc 1
  • fix: string base comparison

    fix: string base comparison

    • Dart version 2.16.2 was upgraded to 2.17.2.
    • Removed the dependent on Enhanced Enum.
    • Modified the 'attr' type for condition assessment.
    • Added string comparison test scenario.
    • A new extension for comparing strings based on byte code has been added.
    opened by DK070202 0
  • Updated Test Cases

    Updated Test Cases

    • Add test cases for condition evaluator.
    • Updated test documents.
    • Fixed 🐛 in feature evaluator.
    • Changed methods for checking type of object.
    • Checked test coverage(98.3)*.
    opened by DK070202 0
  • refactor: SDK initialization

    refactor: SDK initialization

    Description:

    1. To make the initialization of the SDK asynchronous, to avoid empty features after calling initialize method on GBSDKBuilderApp.
    2. To add run functionality inside GBSDKBuilderApp to run an experiment.

    Tasks:

    • [x] Removed SDKBuilder from SDK.
    • [x] Make SDK initialization method async.
    • [x] Update initialization test cases
    • [x] Add run method to evaluate experiment inside SDK
    • [x] Update the changelog.
    • [x] Update documentation on both repos.

    Notes:

    This will close #28

    opened by DK070202 0
Releases(v1.1.1)
  • v1.1.1(Sep 2, 2022)

    What's Changed

    • chore: code formation & updated description by @DK070202 in https://github.com/alippo-com/GrowthBook-SDK-Flutter/pull/20
    • doc: updated docs according 1.1.0 by @DK070202 in https://github.com/alippo-com/GrowthBook-SDK-Flutter/pull/21
    • refactor: FeatureDataModel deserialization by @DK070202 in https://github.com/alippo-com/GrowthBook-SDK-Flutter/pull/19
    • fix :value for key features by @DK070202 in https://github.com/alippo-com/GrowthBook-SDK-Flutter/pull/22

    Full Changelog: https://github.com/alippo-com/GrowthBook-SDK-Flutter/compare/v1.1.0...v1.1.1

    Source code(tar.gz)
    Source code(zip)
  • v1.1.0(Aug 28, 2022)

    What's Changed

    • Fix case in folder name by @rlopzc in https://github.com/alippo-com/GrowthBook-SDK-Flutter/pull/14
    • deserialization with json-serializable. by @DK070202 in https://github.com/alippo-com/GrowthBook-SDK-Flutter/pull/13
    • fix: string base comparison by @DK070202 in https://github.com/alippo-com/GrowthBook-SDK-Flutter/pull/16
    • chore: upgraded analyzer version for dart-2.17.2 by @DK070202 in https://github.com/alippo-com/GrowthBook-SDK-Flutter/pull/17
    • doc: updated docs according by @DK070202 in https://github.com/alippo-com/GrowthBook-SDK-Flutter/pull/18

    New Contributors

    • @rlopzc made their first contribution in https://github.com/alippo-com/GrowthBook-SDK-Flutter/pull/14

    Full Changelog: https://github.com/alippo-com/GrowthBook-SDK-Flutter/commits/v1.1.0

    Source code(tar.gz)
    Source code(zip)
Owner
Alippo
Alippo
Pensil Community official Flutter SDK. Build your own community experience using Dart and Flutter.

Official flutter package for Pensil The official Dart client for Pensil communities, a service for building communites applications. This library can

Pensil Inc 6 Oct 6, 2022
A Flutter plugin for the Google Mobile Ads SDK

A Flutter plugin for the Google Mobile Ads SDK

Google Ads 251 Jan 2, 2023
P2P payment solution using Stream's Flutter SDK and Rapyd's Wallet API

Peer-to-peer payment integration to a messaging app using Flutter ?? This project shows how to integrate a peer-to-peer payment solution to your Strea

Souvik Biswas 15 Dec 8, 2022
Official plugin for using Thepeer SDK with flutter https://thepeer.co

Flutter Thepeer This package makes it easy to use the Thepeer in a flutter project. ?? Screen Shots ?? How to Use plugin ThePeer Send Launch ThepeerSe

The Peer 23 Dec 27, 2022
Hybrid App build on flutter SDK able to run on Android, IOS, web, desktop

Codeforces Visualizer APP Ready to use Flutter Application. Uses codeforces API. Useful for codeforces programmers. ScreenShots Single User Page Compa

vikas yadav 13 Dec 31, 2022
A simple chat app UI using flutter SDK project.

Chatty App A simple chat app UI using flutter SDK project. Screenshot Getting Started This project is a starting point for a Flutter application. A fe

Tengku Belmiro 5 Jul 15, 2022
A Flutter plugin that supports Pangle SDK on Android and iOS.

Thanks for non-commercial open source development authorization by JetBrains. 穿山甲 Flutter SDK `pangle_flutter`是一款集成了字节跳动穿山甲 Android 和 iOS SDK的 Flutter

null 121 Dec 2, 2022
Flutter SDK for Stripe.

Flutter Stripe The Stripe Flutter SDK allows you to build delightful payment experiences in your native Android and iOS apps using Flutter. We provide

null 633 Jan 7, 2023
Cross platform application for iOS and Android using Google's SDK Flutter.

scout Cross platform application for iOS and Android using Google's SDK Flutter. Launch screen for the application. The menu for selecting cookies. Cu

null 0 Nov 9, 2021
Official Flutter SDK for Khalti Payment systems

Khalti Payment Gateway for Flutter Use Khalti Payment Gateway solution in your app or website to simplify payment for your customers. You do not need

Khalti 16 Oct 13, 2022
This project is the HERE SDK reference application for Flutter

HERE SDK Reference Application for Flutter The reference application for the HERE SDK for Flutter (Navigate Edition) shows how a complex and release-r

HERE Technologies 32 Dec 15, 2022
A Social App Built Using FLutter SDK.

Hi ?? , I'm Faheem ??‍?? A Social App Built Using FLutter SDK. The main objective of this application is to provide a single platform for the Students

Faheem Ahmad 26 Nov 10, 2022
Flutter Video Conferencing SDK & Sample App

100ms Flutter SDK ?? Here you will find everything you need to build experiences with video using 100ms iOS/Android SDK. Dive into our SDKs, quick sta

100ms 79 Dec 22, 2022
Real-world movie database mobile application with the Flutter SDK and DDD clean architecture.

Moving Pictures Moving Pictures is a mobile application built with the Flutter SDK for Android and iOS. It’s an application that gets the information

Nifemi 57 Jan 7, 2023
Sample app to demonstrate the integration and working of Dyte SDK for mobile, using Flutter.

Flutter Sample App by dyte Sample App to demonstrate Dyte SDK in flutter Explore the docs » View Demo · Report Bug · Request Feature Table of Contents

Dyte 12 Jan 1, 2023
A Flutter plugin for Yandex AppMetrica SDK, a marketing platform for app install attribution, app analytics, and push campaigns.

appmetrica_sdk A Flutter plugin for Yandex AppMetrica SDK. Plugin implementation status Implemented Events Profiles Not implemented yet Revenue. Comin

EM ALL iT Studio 15 Oct 21, 2021
Quickstart guide for the Agora Flutter SDK(Android/iOS)

Agora Flutter Quickstart This tutorial describes how to create an Agora account and build a sample app with Agora using Flutter. Other Languages: 简体中文

Agora.io Community 501 Dec 30, 2022
Official plugin for using Thepeer SDK with flutter https://thepeer.co

Flutter Thepeer This package makes it easy to use the Thepeer in a flutter project. ?? Screen Shots ?? How to Use plugin Adding MaterialSupport Add th

Thepeer 23 Dec 27, 2022
Task List application developed in Dart language with SDK Flutter for Android, iOS and Web

Task List application developed in Dart language with SDK (Software Development Kit) Flutter for Android, iOS and Web.

João Bruno 2 Jun 2, 2022