launchdarkly_flutter 0.4.0

  • Readme
  • Changelog
  • Example
  • Installing
  • 77

LaunchDarkly Client-side SDK for Flutter #

codecov License: MIT Pub

This is an unofficial LaunchDarkly SDK for Flutter.

This is a work in progress and still has many features that have not been addressed. You are welcome to contribute.

Supported versions #

This SDK is compatible with Flutter 1.12 and Xcode 11 and is tested in Android 28 and iOS 13. Earlier versions of this SDK are compatible with prior versions of Flutter, Android, and iOS.

Getting started #

Check LaunchDarkly's documentation for in-depth instructions on configuring and using LaunchDarkly.

To use this plugin, add launchdarkly_flutter as a dependency in your pubspec.yaml file.

Import package:launchdarkly_flutter/launchdarkly_flutter.dart, instantiate LaunchdarklyFlutter and initiate the plugin with your mobile key from your Environments page.

Android integration #

Because LaunchDarkly Android's SDK (com.launchdarkly:launchdarkly-android-client-sdk:2.8.5) has the label attribute value set in its <application> element, there is a need to override it with your app's own label, if there is one (you will likely have one! :)). Hence, you will need to add tools:replace="android:label" to the <application> element in your AndroidManifest.xml.

<application
        tools:replace="android:label"
        android:name="io.flutter.app.FlutterApplication"
        android:label="YOUR_LABEL"
        android:icon="@mipmap/ic_launcher">

You will probably need to insert the tools namespace as well, on the top of your AndroidManifest.xml file:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="YOUR.PACKAGE.HERE">

Example #

There is an example app that demonstrates how to use the plugin.

You need to instantiate the class and initiate the plugin with your mobile key and the user information, before checking the flags.

// Platform messages are asynchronous, so we initialize in an async method.
LaunchdarklyFlutter launchdarklyFlutter = LaunchdarklyFlutter();

try {
  await launchdarklyFlutter.init('YOUR_MOBILE_KEY', 'USER_ID');
} on PlatformException {}

Be sure to use a mobile key from your Environments page. Never embed a server-side SDK key into a mobile application. Check LaunchDarkly's documentation for in-depth instructions on configuring and using LaunchDarkly.

Give some time for the initialization process to fetch new flags values (or risk getting the defaults right away), and check them:

// Platform messages are asynchronous, so we fetch flags in an async method.
bool shouldShowButton;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
  shouldShowButton = await launchdarklyFlutter.boolVariation('FLAG_KEY', false);
} on PlatformException {
  shouldShowButton = false;
}

Not supported yet #

Check LaunchDarkly's documentation for more information on the features not yet supported. We are slowly and iteratively adding more features as we use them in our own projects. You are welcome to contribute.

Contributing #

We encourage pull requests and other contributions from the community. Check out our contributing guidelines for instructions on how to contribute to this SDK.

About LaunchDarkly #

  • LaunchDarkly is a continuous delivery platform that provides feature flags as a service and allows developers to iterate quickly and safely. We allow you to easily flag your features and manage them from the LaunchDarkly dashboard. With LaunchDarkly, you can:
    • Roll out a new feature to a subset of your users (like a group of users who opt-in to a beta tester group), gathering feedback and bug reports from real-world use cases.
    • Gradually roll out a feature to an increasing percentage of users, and track the effect that the feature has on key metrics (for instance, how likely is a user to complete a purchase if they have feature A versus feature B?).
    • Turn off a feature that you realize is causing performance problems in production, without needing to re-deploy, or even restart the application with a changed configuration file.
    • Grant access to certain features based on user attributes, like payment plan (eg: users on the ‘gold’ plan get access to more features than users in the ‘silver’ plan). Disable parts of your application to facilitate maintenance, without taking everything offline.
  • LaunchDarkly provides feature flag SDKs for a wide variety of languages and technologies. Check out our documentation for a complete list.
  • Explore LaunchDarkly

0.4.0 #

  • Adding all flags listener support.

0.3.0 #

  • Adding all flags support.

0.2.0 #

  • Adding real-time updates support for feature flags.

0.1.7 #

  • Adding documentation on Android integration.

0.1.6 #

  • Some more work behind the scenes in the repository, like configuring CI/CD.

0.1.1 #

  • Some package hygiene, health issues and maintenance.

0.1.0 #

  • First publishable version - only the initialization (with few options), boolVariation and stringVariation available in this first version.

0.0.2 #

  • Slack integration

0.0.1 #

  • Initial release - CI/CD testing.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:launchdarkly_flutter/launchdarkly_flutter.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  bool _shouldShow = false;
  bool _listenerRegistered = false;
  Map<String, dynamic> _allFlags = {};
  bool _listenerAllFlagsRegistered = false;
  LaunchdarklyFlutter launchdarklyFlutter;

  String mobileKey = 'YOUR_MOBILE_KEY';
  String userId = 'USER_ID';
  String flagKey = 'FLAG_KEY';

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

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlatformState() async {
    // Platform messages may fail, so we use a try/catch PlatformException.

    launchdarklyFlutter = LaunchdarklyFlutter();

    try {
      await launchdarklyFlutter.init(mobileKey, userId);
    } on PlatformException {}
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('LaunchDarkly Plugin'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text('Should show: $_shouldShow\n'),
              RaisedButton(
                onPressed: () async {
                  _verifyFlag(flagKey);
                },
                child: Text('Verify'),
              ),
              RaisedButton(
                onPressed: () async {
                  if (_listenerRegistered) {
                    try {
                      setState(() {
                        _listenerRegistered = false;
                      });
                      await launchdarklyFlutter
                          .unregisterFeatureFlagListener(flagKey);
                    } on PlatformException {
                      setState(() {
                        _listenerRegistered = true;
                      });
                    }
                  } else {
                    try {
                      setState(() {
                        _listenerRegistered = true;
                      });
                      await launchdarklyFlutter.registerFeatureFlagListener(
                          flagKey, _verifyFlag);
                    } on PlatformException {
                      setState(() {
                        _listenerRegistered = false;
                      });
                    }
                  }
                },
                child: Text(_listenerRegistered
                    ? 'Unregister listener'
                    : 'Register listener'),
              ),
              SizedBox(height: 30.0,),
              RaisedButton(
                onPressed: () async {
                  _verifyAllFlags([]);
                },
                child: Text('Verify all flags'),
              ),
              RaisedButton(
                onPressed: () async {
                  if (_listenerAllFlagsRegistered) {
                    try {
                      setState(() {
                        _listenerAllFlagsRegistered = false;
                      });
                      await launchdarklyFlutter
                          .unregisterAllFlagsListener('allFlags');
                    } on PlatformException {
                      setState(() {
                        _listenerAllFlagsRegistered = true;
                      });
                    }
                  } else {
                    try {
                      setState(() {
                        _listenerAllFlagsRegistered = true;
                      });
                      await launchdarklyFlutter.registerAllFlagsListener('allFlags', _verifyAllFlags);
                    } on PlatformException {
                      setState(() {
                        _listenerAllFlagsRegistered = false;
                      });
                    }
                  }
                },
                child: Text(_listenerAllFlagsRegistered
                    ? 'Unregister All Flags listener'
                    : 'Register All Flags listener'),
              ),
              Text('All flags: $_allFlags\n'),
            ],
          ),
        ),
      ),
    );
  }

  void _verifyFlag(String flagKey) async {
    bool shouldShow;
    // Platform messages may fail, so we use a try/catch PlatformException.
    try {
      shouldShow = await launchdarklyFlutter.boolVariation(flagKey, false);
    } on PlatformException {
      shouldShow = false;
    }

    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) return;

    setState(() {
      _shouldShow = shouldShow;
    });
  }

  void _verifyAllFlags(List<String> flagKeys) async {
    Map<String, dynamic> allFlags;
    // Platform messages may fail, so we use a try/catch PlatformException.
    try {
      allFlags = await launchdarklyFlutter.allFlags();
    } on PlatformException {
      allFlags = {};
    }

    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) return;

    setState(() {
      _allFlags = allFlags;
    });
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  launchdarkly_flutter: ^0.4.0

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:launchdarkly_flutter/launchdarkly_flutter.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
54
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
77
Learn more about scoring.

We analyzed this package on Mar 27, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.7.1
  • pana: 0.13.6
  • Flutter: 1.12.13+hotfix.8

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.11 1.14.12
meta 1.1.8
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
flutter_test