braze_plugin 0.8.0

  • Readme
  • Changelog
  • Example
  • Installing
  • 85

Braze Flutter SDK #

Effective marketing automation is an essential part of successfully scaling and managing your business.

This project contains the Braze plug-in package, a specialized package that allows integrators to use certain Braze APIs from Flutter app code written in Dart.

Getting Started #

  1. Follow the directions in the Install Tab to import Braze into your project.

  2. Instantiate an instance of the Braze plugin by calling new BrazePlugin()

Android

  1. In your res/values directory, create a file called appboy.xml and add your API key and set your custom endpoint as specified in our public documentation.

  2. Add required permissions to your AndroidManifest, as specified in our public documentation.

  3. To integrate push, follow the directions in our public documentation. Note that only registration using our "automatic FCM registration" is currently supported from Flutter apps.

iOS

  1. Call Appboy.startWithApiKey() in your AppDelegate's didFinishLaunchingWithOptions delegate method as specified in our public documentation.

  2. Set your custom endpoint as specified in our public documentation. We recommend the Info.plist approach.

In-app messages

Disabling automatic display

Native in-app messages display automatically out of the box on Android and iOS.

To disable automatic in-app message display for Android, implement the IInAppMessageManagerListener delegate as described in our public documentation. Your beforeInAppMessageDisplayed method implementation should return InAppMessageOperation.DISCARD. For an example, see MainActivity.kt in our example app.

To disable automatic in-app message display for iOS, implement the ABKInAppMessageControllerDelegate delegate as described in our public documentation. Your beforeInAppMessageDisplayed delegate implementation should return ABKInAppMessageDisplayChoice.discardInAppMessage. For an example, see AppDelegate.swift in our example app.

In-app message data callback

You may set a callback in Dart to receive Braze in-app message data in the Flutter host app.

To set the callback, call BrazePlugin.setBrazeInAppMessageCallback() from your Flutter app with a function that takes a BrazeInAppMessage instance. The BrazeInAppMessage object supports a subset of fields available in the native model objects, including uri, message, header, buttons, extras, and more.

On Android, you will additionally need to implement the IInAppMessageManagerListener delegate as described in our public documentation. Your beforeInAppMessageDisplayed() method implementation must call BrazePlugin.process(inAppMessage). For an example, see MainActivity.kt in our example app.

On iOS, you will additionally need to implement the ABKInAppMessageControllerDelegate delegate as described in our public documentation. Your beforeInAppMessageDisplayed delegate implementation must call BrazePlugin.process(inAppMessage). For an example, see AppDelegate.swift in our example app.

To log analytics using your BrazeInAppMessage, pass the instance into the logInAppMessageClicked, logInAppMessageImpression, and logInAppMessageButtonClicked methods available on the main plugin interface.

Content Cards

Content Card data callback

You may set a callback in Dart to receive Braze Content Card data in the Flutter host app.

To set the callback, call BrazePlugin.setBrazeContentCardsCallback() from your Flutter app with a function that takes a List<BrazeContentCard> instance. The BrazeContentCard object supports a subset of fields available in the native model objects, including description, title, image, url, extras, and more.

On Android, you will additionally need to implement and register an IEventSubscriber<ContentCardsUpdatedEvent> as described in our public documentation. Your trigger() method implementation must call BrazePlugin.processContentCards(event.allCards). For an example, see MainActivity.kt in our example app.

On iOS, you will additionally need to create an NSNotificationCenter listener for ABKContentCardsProcessedNotification events as described in our public documentation. Your ABKContentCardsProcessedNotification callback implementation must call BrazePlugin.processContentCards(contentCards). For an example, see AppDelegate.swift in our example app.

To log analytics using your BrazeContentCard, pass the instance into the logContentCardClicked, logContentCardImpression, and logContentCardDismissed methods available on the main plugin interface.

0.8.0 #

Breaking

0.7.0 #

Added
  • Added BrazePlugin.launchContentCards() and BrazePlugin.refreshContentCards() to natively display and refresh Content Cards.
  • Adds a Dart callback for receiving Braze Content Card data in the Flutter host app.
    • Similar to in-app messages, you will need to subscribe to Content Card updates in your native app code and pass Content Card objects to the Dart layer. Those objects will then be passed to your callback within a List<BrazeContentCard> instance.
    • To set the callback, call BrazePlugin.setBrazeContentCardsCallback() from your Flutter app with a function that takes a List<BrazeContentCard> instance.
      • The BrazeContentCard object supports a subset of fields available in the native model objects, including description, title, image, url, extras, and more.
    • On Android, you will need to register an IEventSubscriber<ContentCardsUpdatedEvent> instance and pass returned Content Card objects to the Dart layer using BrazePlugin.processContentCards(contentCards).
      • See the MainActivity.kt file of our sample app for a reference example.
    • On iOS, you will need to create an NSNotificationCenter listener for ABKContentCardsProcessedNotification events and pass returned Content Card objects to the Dart layer using BrazePlugin.processContentCards(contentCards).
      • See the AppDelegate.swift file of our sample app for a reference example.
  • Added support for logging Content Card analytics to Braze using BrazeContentCard instances. See logContentCardClicked(), logContentCardImpression(), and logContentCardDismissed() on the BrazePlugin interface.

0.6.1 #

Fixed
  • Fixed an issue where the Braze Kotlin plugin file's directory structure did not match its package structure.

0.6.0 #

Changed

0.5.2 #

Important: This patch updates the Braze iOS SDK Dependency from 3.20.1 to 3.20.2, which contains important bugfixes. Integrators should upgrade to this patch version. Please see the Braze iOS SDK Changelog for more information.

Changed

0.5.1 #

Important This release has known issues displaying HTML in-app messages. Do not upgrade to this version and upgrade to 0.5.2 and above instead. If you are using this version, you are strongly encouraged to upgrade to 0.5.2 or above if you make use of HTML in-app messages.

Changed

0.5.0 #

Important This release has known issues displaying HTML in-app messages. Do not upgrade to this version and upgrade to 0.5.2 and above instead. If you are using this version, you are strongly encouraged to upgrade to 0.5.2 or above if you make use of HTML in-app messages.

Breaking
  • The native iOS bridge uses Braze iOS SDK 3.20.0.
  • Important: Braze iOS SDK 3.20.0 contains updated push token registration methods. We recommend upgrading to these methods as soon as possible to ensure a smooth transition as devices upgrade to iOS 13. In application:didRegisterForRemoteNotificationsWithDeviceToken:, replace
[[Appboy sharedInstance] registerPushToken:
                [NSString stringWithFormat:@"%@", deviceToken]];

with

[[Appboy sharedInstance] registerDeviceToken:deviceToken]];
  • registerPushToken() was renamed to registerAndroidPushToken() and is now a no-op on iOS. On iOS, push tokens must now be registered through native methods.

0.4.0 #

Breaking
Added
  • Added the following new field to BrazeInAppMessage: zippedAssetsUrl.
    • Note that a known issue in the iOS plugin prevents HTML IAMs from working reliably with the Dart in-app message callback. Android is not affected.

0.3.0 #

Breaking
  • The native iOS bridge uses Braze iOS SDK 3.15.0.
  • The native Android bridge uses Braze Android SDK 3.5.0.
  • Support for the Android configuration parameter com_appboy_inapp_show_inapp_messages_automatically has been removed.
    • To control whether an in-app message object should be displayed natively or not, create and register an instance of IInAppMessageManagerListener in your native Android code and implement decisioning in the beforeInAppMessageDisplayed method. See MainActivity in our sample app for an example.
  • On Android, in-app message objects are no longer sent automatically to the Dart in-app message callback after calling BrazePlugin.setBrazeInAppMessageCallback() in your Dart code.
    • Similar to iOS, you will need to implement a delegate interface in your native app code and pass in-app message objects to the Dart layer for passing to the callback.
    • On Android, the delegate interface is IInAppMessageManagerListener and the method for passing objects to Dart is BrazePlugin.processInAppMessage(inAppMessage).
    • See the sample IInAppMessageManagerListener implementation in the MainActivity.kt file of our sample app for an example.
    • This approach gives the integrator more flexibility in deciding when a message should be displayed natively, discarded, or passed into the Dart layer.
Added
  • Added support for logging in-app message analytics to Braze using BrazeInAppMessage instances. See logInAppMessageClicked, logInAppMessageImpression, and logInAppMessageButtonClicked on the BrazePlugin interface.

0.2.1 #

Added
  • Added the following new fields to BrazeInAppMessage: imageUrl, useWebView, duration, clickAction, dismissType, messageType
  • Added the following new fields to BrazeButton: useWebView, clickAction.

0.2.0 #

Breaking
Added
  • Adds addAlias() to the public API interface.
  • Adds requestLocationInitialization() to the public API interface.
  • Adds getInstallTrackingId() to the public API interface.
  • Adds support for disabling native in-app message display on Android.
    • To disable automatic in-app message display, create a boolean element named com_appboy_inapp_show_inapp_messages_automatically in your Android app's appboy.xml and set it to false.
    • Note: Disabling automatic in-app message display was already possible for iOS. For instructions, see README.md.
  • Adds a Dart callback for receiving Braze in-app message data in the Flutter host app.
    • Analytics are not currently supported on messages displayed through the callback.
    • To set the callback, call BrazePlugin.setBrazeInAppMessageCallback() from your Flutter app with a function that takes a BrazeInAppMessage instance.
      • The BrazeInAppMessage object supports a subset of fields available in the native model objects, including uri, message, header, buttons, and extras.
    • The callback should begin to function on Android immediately after being set.
    • On iOS, you will additionally need to implement the ABKInAppMessageControllerDelegate delegate as described in our public documentation. Your beforeInAppMessageDisplayed delegate implementation must call BrazePlugin.process(inAppMessage). For an example, see AppDelegate.swift in our example app.

0.1.1 #

  • Formatted braze_plugin.dart.

0.1.0 #

  • Removes the unused dart:async import in braze_plugin.dart.
  • Makes _callStringMethod private in braze_plugin.dart.
  • Adds basic dartdoc to the public API interface.

0.0.2 #

  • Updates the version of Kotlin used by the Android plugin from 1.2.71 to 1.3.11.

0.0.1 #

example/lib/main.dart

import 'dart:io' show Platform;
import 'package:flutter/material.dart';

import 'package:braze_plugin/braze_plugin.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: BrazeFunctions(),
    );
  }
}

class BrazeFunctions extends StatefulWidget {
  @override
  BrazeFunctionsState createState() => new BrazeFunctionsState();
}

class BrazeFunctionsState extends State<BrazeFunctions> {
  String _userId = "";
  String _enabled = "";
  BrazePlugin _braze = new BrazePlugin();
  final userIdController = TextEditingController();
  final customEventNameController = TextEditingController();
  final customEventPropertyKeyController = TextEditingController();
  final customEventPropertyValueController = TextEditingController();

  @override
  void dispose() {
    userIdController.dispose();
    customEventNameController.dispose();
    customEventPropertyKeyController.dispose();
    customEventPropertyValueController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Braze Sample'),
      ),
      body: _buildListView(),
    );
  }

  Widget _buildListView() {
    if (_enabled == "") {
      // This is a hack to determine the enabled state of the Braze API
      // Not recommended for use in production
      _braze.getInstallTrackingId().then((result) {
        if (result == null || result == "") {
          this.setState(() {
            _enabled = "DISABLED";
          });
        } else {
          this.setState(() {
            _enabled = "ENABLED";
          });
        }
      });
    }

    return Builder(
      builder: (BuildContext context) {
        return ListView(
          shrinkWrap: true,
          padding: const EdgeInsets.all(20.0),
          children: <Widget>[
            Center(child: Text("SDK Status: $_enabled")),
            Center(child: Text("User Id: $_userId")),
            TextField(
              autocorrect: false,
              controller: userIdController,
              decoration: InputDecoration(
                  hintText: 'Please enter a user id', labelText: 'User Id'),
            ),
            FlatButton(
              child: const Text('CHANGE USER'),
              onPressed: () {
                String userId = userIdController.text;
                _braze.changeUser(userId);
                this.setState(() {
                  _userId = userId;
                });
              },
            ),
            TextField(
              controller: customEventNameController,
              decoration: InputDecoration(
                  hintText: 'Please enter a custom event name',
                  labelText: 'Event Name'),
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                Flexible(
                  child: TextField(
                    controller: customEventPropertyKeyController,
                    decoration: InputDecoration(
                        hintText: 'Property Key', labelText: 'Property Key'),
                  ),
                ),
                Flexible(
                  child: TextField(
                    controller: customEventPropertyValueController,
                    decoration: InputDecoration(
                        hintText: 'Property Value',
                        labelText: 'Property Value'),
                  ),
                ),
              ],
            ),
            FlatButton(
              child: const Text('LOG CUSTOM EVENT'),
              onPressed: () {
                String customEvent = customEventNameController.text;
                String customPropertyKey =
                    customEventPropertyKeyController.text;
                String customPropertyValue =
                    customEventPropertyValueController.text;
                if (customEvent.isEmpty) {
                  customEvent = 'MyCustomEvent';
                }
                if (customPropertyKey.isEmpty) {
                  _braze.logCustomEvent(customEvent);
                  Scaffold.of(context).showSnackBar(new SnackBar(
                    content: new Text("Custom event $customEvent."),
                  ));
                } else {
                  _braze.logCustomEventWithProperties(
                      customEvent, {customPropertyKey: customPropertyValue});
                  Scaffold.of(context).showSnackBar(new SnackBar(
                    content: new Text(
                        'Custom event $customEvent with properties {"$customPropertyKey":"$customPropertyValue"}.'),
                  ));
                }
              },
            ),
            FlatButton(
              child: const Text('LOG PRESET EVENTS AND PURCHASES'),
              onPressed: () {
                var props = {"k1": "v1", "k2": 2, "k3": 3.5, "k4": false};
                _braze.logCustomEvent("eventName");
                _braze.logCustomEventWithProperties("eventNameProps", props);
                _braze.logPurchase("productId", "USD", 3.50, 2);
                _braze.logPurchaseWithProperties(
                    "productIdProps", "USD", 2.50, 4, props);
                Scaffold.of(context).showSnackBar(new SnackBar(
                  content: new Text("Logged events and purchases"),
                ));
              },
            ),
            FlatButton(
              child: const Text('SET PRESET ATTRIBUTES'),
              onPressed: () {
                _braze.addToCustomAttributeArray("arrayAttribute", "a");
                _braze.addToCustomAttributeArray("arrayAttribute", "c");
                _braze.setStringCustomUserAttribute(
                    "stringAttribute", "stringValue");
                _braze.setStringCustomUserAttribute(
                    "stringAttribute2", "stringValue");
                _braze.setDoubleCustomUserAttribute("doubleAttribute", 1.5);
                _braze.setIntCustomUserAttribute("intAttribute", 1);
                _braze.setBoolCustomUserAttribute("boolAttribute", false);
                _braze.setDateCustomUserAttribute(
                    "dateAttribute", new DateTime.now());
                _braze.setLocationCustomAttribute("work", 40.7128, 74.0060);
                _braze.setPushNotificationSubscriptionType(
                    SubscriptionType.opted_in);
                _braze.setEmailNotificationSubscriptionType(
                    SubscriptionType.opted_in);
                _braze.setAttributionData(
                    "network1", "campaign1", "adgroup1", "creative1");
                _braze.setFirstName("firstName");
                _braze.setLastName("lastName");
                _braze.setDateOfBirth(1990, 4, 13);
                _braze.setEmail("email@email.com");
                _braze.setGender("f");
                _braze.setLanguage("es");
                _braze.setCountry("JP");
                _braze.setHomeCity("homeCity");
                _braze.setPhoneNumber("123456789");
                _braze.setAvatarImageUrl("https://raw.githubusercontent.com/"
                    "Appboy/appboy-react-sdk/master/braze-logo.png");
                _braze.addAlias("alias-name-1", "alias-label-1");
                Scaffold.of(context).showSnackBar(new SnackBar(
                  content: new Text("Logged attributes"),
                ));
              },
            ),
            FlatButton(
              child: const Text('UNSET/INC PRESET ATTRIBUTES'),
              onPressed: () {
                _braze.removeFromCustomAttributeArray("arrayAttribute", "a");
                _braze.unsetCustomUserAttribute("stringAttribute2");
                _braze.incrementCustomUserAttribute("intAttribute", 2);
                Scaffold.of(context).showSnackBar(new SnackBar(
                  content: new Text("Unset/increment attributes"),
                ));
              },
            ),
            FlatButton(
              child: const Text('REQUEST DATA FLUSH'),
              onPressed: () {
                _braze.requestImmediateDataFlush();
                Scaffold.of(context).showSnackBar(new SnackBar(
                  content: new Text("Requested Data Flush"),
                ));
              },
            ),
            FlatButton(
              child: const Text('REQUEST LOCATION INITIALIZATION'),
              onPressed: () {
                _braze.requestLocationInitialization();
              },
            ),
            FlatButton(
              child: const Text('GET INSTALL TRACKING ID'),
              onPressed: () {
                _braze.getInstallTrackingId().then((result) {
                  if (result == null) {
                    Scaffold.of(context).showSnackBar(new SnackBar(
                      content: new Text("Install Tracking ID was null"),
                    ));
                  } else {
                    Scaffold.of(context).showSnackBar(new SnackBar(
                      content: new Text("Install Tracking ID: " + result),
                    ));
                  }
                });
              },
            ),
            FlatButton(
              child: const Text('SET IN-APP MESSAGE CALLBACK'),
              onPressed: () {
                _braze.setBrazeInAppMessageCallback(
                    (BrazeInAppMessage inAppMessage) {
                  print("Received message: " + inAppMessage.toString());
                  _braze.logInAppMessageImpression(inAppMessage);
                  Scaffold.of(context).showSnackBar(new SnackBar(
                    content: new Text("Received message and logging clicks: " +
                        inAppMessage.toString()),
                  ));
                  _braze.logInAppMessageClicked(inAppMessage);
                  inAppMessage.buttons.forEach((button) {
                    _braze.logInAppMessageButtonClicked(
                        inAppMessage, button.id);
                  });
                });
                Scaffold.of(context).showSnackBar(new SnackBar(
                  content: new Text("In-app message callback set. "
                      "In-app message data will appear in snackbars."),
                ));
              },
            ),
            FlatButton(
              child: const Text('REFRESH CONTENT CARDS'),
              onPressed: () {
                _braze.requestContentCardsRefresh();
              },
            ),
            FlatButton(
              child: const Text('LAUNCH CONTENT CARDS'),
              onPressed: () {
                _braze.launchContentCards();
              },
            ),
            FlatButton(
              child: const Text('SET CONTENT CARDS CALLBACK'),
              onPressed: () {
                _braze.setBrazeContentCardsCallback(
                    (List<BrazeContentCard> contentCards) {
                  if (contentCards.isEmpty) {
                    Scaffold.of(context).showSnackBar(new SnackBar(
                      content:
                      new Text("Empty Content Cards update received."),
                    ));
                  }
                  contentCards.forEach((contentCard) {
                    print("Received card: " + contentCard.toString());
                    _braze.logContentCardClicked(contentCard);
                    _braze.logContentCardImpression(contentCard);
                    // _braze.logContentCardDismissed(contentCard);
                    Scaffold.of(context).showSnackBar(new SnackBar(
                      content:
                          new Text("Received card: " + contentCard.toString()),
                    ));
                  });
                });
                Scaffold.of(context).showSnackBar(new SnackBar(
                  content: new Text("Content Cards callback set. "
                      "Content Card data will appear in snackbars."),
                ));
              },
            ),
            FlatButton(
              child: const Text('WIPE DATA'),
              onPressed: () {
                showDialog(
                  context: context,
                  builder: (BuildContext context) {
                    return AlertDialog(
                      title: new Text("Wipe Data"),
                      content: new Text("Are you sure?"),
                      actions: <Widget>[
                        new FlatButton(
                          child: new Text("Yes"),
                          onPressed: () {
                            _braze.wipeData();
                            if (Platform.isIOS) {
                              this.setState(() {
                                _enabled = "DISABLED";
                              });
                            }
                            Navigator.of(context).pop();
                          },
                        ),
                        new FlatButton(
                          child: new Text("Cancel"),
                          onPressed: () {
                            Navigator.of(context).pop();
                          },
                        ),
                      ],
                    );
                  },
                );
              },
            ),
            FlatButton(
              child: const Text('ENABLE SDK'),
              onPressed: () {
                _braze.enableSDK();
                if (Platform.isAndroid) {
                  this.setState(() {
                    _enabled = "ENABLED";
                  });
                }
              },
            ),
            FlatButton(
              child: const Text('DISABLE SDK'),
              onPressed: () {
                showDialog(
                  context: context,
                  builder: (BuildContext context) {
                    return AlertDialog(
                      title: new Text("Disable SDK"),
                      content: new Text("Are you sure?"),
                      actions: <Widget>[
                        new FlatButton(
                          child: new Text("Yes"),
                          onPressed: () {
                            _braze.disableSDK();
                            this.setState(() {
                              _enabled = "DISABLED";
                            });
                            Navigator.of(context).pop();
                          },
                        ),
                        new FlatButton(
                          child: new Text("Cancel"),
                          onPressed: () {
                            Navigator.of(context).pop();
                          },
                        ),
                      ],
                    );
                  },
                );
              },
            ),
          ],
        );
      },
    );
  }
}

Use this package as a library

1. Depend on it

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


dependencies:
  braze_plugin: ^0.8.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:braze_plugin/braze_plugin.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
70
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]
85
Learn more about scoring.

We analyzed this package on Apr 7, 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

Health suggestions

Format lib/braze_plugin.dart.

Run flutter format to format lib/braze_plugin.dart.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.0.0-dev.68.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