featurehub_client_sdk 1.4.0 featurehub_client_sdk: ^1.4.0 copied to clipboard
Dart SDK implementation for FeatureHub - open source Feature Flag management, A/B testing and remote configuration platform. Suitable for Flutter Web, Mobile and Desktop.
Flutter / Dart Client SDK for FeatureHub #
Dart SDK implementation for FeatureHub.io - Open source Feature flags management, A/B testing and remote configuration platform. Suitable for Flutter Web, Mobile and Desktop.
It provides the core functionality of the Feature Repository which holds features and their states and creates events, e.g. sends feature updates. This library depends on our own fork of the EventSourcing library for Dart.
Read detailed documentation on FeatureHub here
Visit Demo FeatureHub Admin Console here
https://user-images.githubusercontent.com/9538697/135741127-d813595c-28a4-4d5b-81f2-f754e02d584e.mov
Getting started #
Add dependency:
dependencies:
featurehub_client_sdk: ^1.3.0 #latest version
Add import package:
import 'package:featurehub_client_sdk/featurehub.dart';
Follow these 3 steps to connecting to FeatureHub:
1. Locate your API Key #
Find and copy your Server evaluated API Key from the FeatureHub Admin Console on the API Keys page -
you will use this in your code to configure feature updates for your environments.
It should look similar to this: default/806d0fe8-2842-4d17-9e1f-1c33eedc5f31/tnZHPUIKV9GPM4u0koKPk1yZ3aqZgKNI7b6CT76q
.
Note: This SDK only accepts Server evaluated API key which is designed for insecure clients, e.g. Browser or Mobile. This also means you evaluate one user per client. More on this here
2. Connect to the FeatureHub server: #
Create FeatureHub Repository that holds feature states by providing your FeatureHub server url and API Key from the previous step:
repository = ClientFeatureRepository();
fhConfig = FeatureHubConfig(
'http://localhost:8903',
[
'default/806d0fe8-2842-4d17-9e1f-1c33eedc5f31/tnZHPUIKV9GPM4u0koKPk1yZ3aqZgKNI7b6CT76q'
],
repository!);
3. Get features state from your code: #
class Sample extends StatelessWidget {
const Sample({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My app'),
),
body: StreamBuilder<FeatureStateHolder>(
stream: repository!
.feature('CONTAINER_COLOUR_FEATURE')
.featureUpdateStream,
builder: (context, snapshot) {
if (!snapshot.hasData) return SizedBox.shrink();
return Container(
color: snapshot.data?.booleanValue == true
? Colors.red
: Colors.green,
child: Text('Hello world!'));
}));
}
}
4. Make the call to get latest feature states from the FeatureHub server: #
fhConfig!.request();
request
is an async method and it will return its content directly to the Repository.
A failed call is caught and a Failure status is sent to the repository, which will have an updated error status
(such as FAILED, in the Readyness listener).
If the request has no data or an api key doesn't exist, that is not considered an error because they may just not yet be available and you don't want your application to fail.
Flutter sample app example #
There is a full example you can follow that demonstrates how a feature with a key "CONTAINER_COLOUR" of type string can be processed in the Flutter app. The container colour will get updated states based on the feature value, which can be "yellow", "green", "purple" and so on. (See the video clip above)
Feature state methods #
- Get a raw feature value through "Get" methods
getFlag('FEATURE_KEY')
returns a boolean feature value or null if the feature does not existgetNumber('FEATURE_KEY')
|getString('FEATURE_KEY')
|getJson('FEATURE_KEY')
returns the value of the feature or null if the feature value is empty or does not existexists('FEATURE_KEY')
returns true if feature key exists, otherwise falsefeature('FEATURE_KEY')
|getFeatureState('FEATURE_KEY')
returnsFeatureStateHolder
if feature key exists or null if the feature value is not set or does not exist
Advanced usage with Rollout Strategies #
FeatureHub supports server side evaluation of complex rollout strategies that are applied to individual feature values in a specific environment. This includes support of preset rules, e.g. per user key, country, device type, platform type as well as percentage splits rules and custom rules that you can create according to your application needs.
For more details on rollout strategies, targeting rules and feature experiments see the core documentation.
Coding for Rollout strategies
There are several preset strategies rules we track specifically: user key
, country
, device
and platform
. However, if those do not satisfy your requirements you also have an ability to attach a custom rule. Custom rules can be created as following types: string
, number
, boolean
, date
, date-time
, semantic-version
, ip-address
FeatureHub SDK will match your users according to those rules, so you need to provide attributes to match on in the SDK:
Sending preset attributes:
Provide the following attribute to support userKey
rule:
repository.clientContext.userKey('ideally-unique-id').build();
to support country
rule:
repository.clientContext.country(StrategyAttributeCountryName.NewZealand).build();
to support device
rule:
repository.clientContext.device(StrategyAttributeDeviceName.Browser).build();
to support platform
rule:
repository.clientContext.platform(StrategyAttributePlatformName.Android).build();
to support semantic-version
rule:
repository.clientContext.version('1.2.0').build();
or if you are using multiple rules, you can combine attributes as follows:
repository.clientContext.userKey('ideally-unique-id')
.country(StrategyAttributeCountryName.NewZealand)
.device(StrategyAttributeDeviceName.Browser)
.platform(StrategyAttributePlatformName.Android)
.version('1.2.0')
.build();
Sending custom attributes:
To add a custom key/value pair, use attr(key, value)
repository.clientContext.attr('first-language', 'russian').build();
Or with array of values (only applicable to custom rules):
repository.clientContext.attrs('languages', ['russian', 'english', 'german']).build();
You can also use repository.clientContext.clear()
to empty your context.
In all cases, you need to call build()
followed by featurehubApi!.request()
to re-trigger passing of the new attributes to the server for recalculation.
Coding for percentage splits:
For percentage rollout you are only required to provide the userKey
or sessionKey
.
repository.clientContext.userKey('ideally-unique-id').build();
or
repository.clientContext.sessionKey('session-id').build();
For more details on percentage splits and feature experiments see Percentage Split Rule.
Updating features via SSE (server-sent events) #
In the examples above the mechanism to retrieve feature states from the FeatureHub server is based on the GET
request. However, there is also an option to update the FeatureHub repository using SSE protocol. The advantage of SSE method is that it provides real time updates for features by keeping a link open to the FeatureHub
Edge Server. However, as you can imagine this is an expensive operation to do on a battery and we do not recommend it
for Mobile except for short periods. Please consider carefully if you decide to use this method.
Because these two update methods are interchangeable, you can include them in the same application. You could swap between GET when your app swaps to the background and EventSource when your app swaps to the foreground if immediate updates are important.
Failure #
If for some reason the connection to the FeatureHub server fails - either initially or for some reason during the process, you will get a readyness event to indicate that it has now failed.
enum Readyness {
NotReady = 'NotReady',
Ready = 'Ready',
Failed = 'Failed'
}
FeatureHub Test API #
The FeatureHub Test API is available in this SDK, but it is not broken out into a separate class. The purpose of the test API is to allow you to update features primarily when writing automated integration tests.
We provide a method to do this
using the FeatureServiceApi.setFeatureState
method. Use of the API is based on the rights of your API Key.
Generally you should only give write access to service accounts in test environments.
When specifying the key, the Edge service will get the latest value of the feature and compare your changes against it, compare them to your permissions and act accordingly.
You need to pass in an instance of a FeatureStateUpdate, which takes three values, all of which are optional:
lock
- boolean type. If true it will attempt to lock, false - attempts to unlock. No value will not make any change.value
- this isdynamic
kind of value and is passed when you wish to set a value. Do not pass it if you wish to unset the value. For a flag this means setting it to false (if null), but for the others it will make it null (not passing it).updateValue
- set this to true if you wish to make the value field null. Otherwise, there is no way to distinguish between not setting a value, and setting it to null.
We don't provide a wrapper class for this because most of the code comes directly from the featurehub_client_api
and
you need to include that and its dependencies in your project to use this capability.
Sample code might look like this:
final _api = FeatureServiceApiDelegate(ApiClient(basePath: hostURL));
_api.setFeatureState(apiKey, featureKey, FeatureStateUpdate()..lock = false ..value = 'TEST');
Client-side evaluation #
We are not planning on implementing Client Side evaluation for Dart without direct request as it is mostly used client side in Flutter apps.