GrowthBook Flutter SDK。

概述

GrowthBook 是一个开源功能标志和实验平台,无需部署新代码即可轻松调整向用户显示的功能,并运行 A/B 测试。GrowthBook 分为两个部分:GrowthBook 应用程序和实现此功能的 SDK。此 Flutter SDK 允许您在基于 Flutter 的移动应用程序中使用 GrowthBook。

  • 轻巧快速
  • 支持
    • Android
    • iOS
    • Mac
    • Windows
  • 使用您现有的事件跟踪(GA、Segment、Mixpanel、自定义)
  • 无需部署新代码即可调整变体权重和定位

安装

  1. 将 GrowthBook SDK 添加为 pubspec.yaml 文件中的依赖项。
growthbook_sdk_flutter: ^1.0.0

集成

集成非常简单

  1. 从 GrowthBook 应用程序创建 GrowthBook API 密钥。
  2. 如下,在应用程序开头初始化 SDK,传入 API 密钥。

现在,您可以在 Growth Book 应用程序中启动/停止测试、调整覆盖率和变体权重,并将获胜变体应用于 100% 的流量,而无需将代码更改部署到您的站点。

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

初始化时还有其他属性可以设置

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

用法

  • 初始化返回 SDK 实例 – GrowthBookSDK

    使用 sdkInstance 来使用以下功能 –
  • feature 方法接受一个字符串参数,即功能的唯一标识符,并返回一个 FeatureResult 对象。

      GBFeatureResult feature(String id) 
  • run 方法接受一个 Experiment 对象并返回一个 ExperimentResult

    GBExperimentResult run(GBExperiment experiment)   
  • 获取上下文
    GBContext getGBContext()
  • 获取功能
    GBFeatures getFeatures()  

模型

/// 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>{};
}

/// 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;
}

许可证

该项目使用 MIT 许可证。核心 GrowthBook 应用程序将始终保持开放和免费,尽管我们将来可能会添加一些商业企业插件。

GitHub

查看 Github