Skip to main content

bzengage_flutter_sdk 0.7.2 copy "bzengage_flutter_sdk: ^0.7.2" to clipboard
bzengage_flutter_sdk: ^0.7.2 copied to clipboard

Official BzEngage Flutter SDK. A thin Dart layer that forwards calls to the native BzEngage Android and iOS SDKs (in-app messages, zones, push, events, consent, commerce).

example/lib/main.dart

// BzEngage Flutter SDK example.
//
// Run with your own keys:
//   flutter run --dart-define=BZ_BASE_URL=https://your-host/bzengage-api --dart-define=BZ_API_KEY=cpk_...
//   (optional) --dart-define=BZ_USER=member-123
//
// Android push needs android/app/google-services.json (your Firebase project). This example also
// uses firebase_messaging to show how to forward BzEngage pushes when another package owns FCM
// (README §11.6). Apps without firebase_messaging need none of the Firebase code below.
import 'package:bzengage_flutter_sdk/bzengage_flutter_sdk.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

const String kBaseUrl = String.fromEnvironment('BZ_BASE_URL');
const String kApiKey = String.fromEnvironment('BZ_API_KEY');

/// Identifier used by the "Sign in" button (your member ID or email).
const String kUserId = String.fromEnvironment('BZ_USER', defaultValue: 'demo-user');

/// Deep link scheme of this app (registered in AndroidManifest.xml / Info.plist).
const String kAppScheme = 'bzexample';

/// Android only: when firebase_messaging owns FCM, BzEngage pushes are forwarded.
/// Set in main() once Firebase has started (it needs android/app/google-services.json).
bool _usesFirebaseMessaging = false;

/// Runs in a background isolate when a push arrives while the app is closed or in the background.
@pragma('vm:entry-point')
Future<void> firebaseBackgroundHandler(RemoteMessage message) async {
  if (BzEngageSDK.isBzEngagePush(message.data)) {
    await BzEngageSDK.handlePushMessage(message.data);
  }
}

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  if (kBaseUrl.isEmpty || kApiKey.isEmpty) {
    runApp(const MaterialApp(
      home: Scaffold(
        body: Center(
          child: Padding(
            padding: EdgeInsets.all(24),
            child: Text('Run with --dart-define=BZ_BASE_URL=... --dart-define=BZ_API_KEY=cpk_... '
                '(see example/README.md).'),
          ),
        ),
      ),
    ));
    return;
  }

  if (!kIsWeb && defaultTargetPlatform == TargetPlatform.android) {
    try {
      await Firebase.initializeApp();
      FirebaseMessaging.onBackgroundMessage(firebaseBackgroundHandler);
      _usesFirebaseMessaging = true;
    } catch (e) {
      debugPrint('Firebase not configured - push forwarding is off ($e)');
    }
  }

  final sdk = await BzEngageSDK.initialize(
    baseUrl: kBaseUrl,
    apiKey: kApiKey,
    inAppAllowedSchemes: const [kAppScheme],
  );
  // Push — on every launch, right after initialize().
  final openedFromPush = await sdk.initialPushDeeplink();
  await sdk.initPush();

  if (_usesFirebaseMessaging) {
    FirebaseMessaging.onMessage.listen((message) {
      if (BzEngageSDK.isBzEngagePush(message.data)) {
        BzEngageSDK.handlePushMessage(message.data);
      }
    });
    FirebaseMessaging.instance.onTokenRefresh.listen((_) => sdk.reRegisterPushToken());
  }

  runApp(ExampleApp(sdk: sdk, openedFromPush: openedFromPush));
}

/// Reports every screen change to BzEngage and shows the in-app messages for it.
class BzEngageNavigatorObserver extends NavigatorObserver {
  BzEngageNavigatorObserver(this.sdk);
  final BzEngageSDK sdk;

  void _report(Route<dynamic>? route) {
    final name = route?.settings.name;
    if (name == null) return;
    sdk.setScreen(name);
    sdk.showMessages().catchError((Object _) => <int>[]);
  }

  @override
  void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) => _report(route);

  @override
  void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) => _report(previousRoute);

  @override
  void didReplace({Route<dynamic>? newRoute, Route<dynamic>? oldRoute}) => _report(newRoute);
}

class ExampleApp extends StatefulWidget {
  const ExampleApp({super.key, required this.sdk, this.openedFromPush});
  final BzEngageSDK sdk;
  final String? openedFromPush;

  @override
  State<ExampleApp> createState() => _ExampleAppState();
}

class _ExampleAppState extends State<ExampleApp> {
  final _navigatorKey = GlobalKey<NavigatorState>();
  final _log = ValueNotifier<List<String>>(const []);

  void _add(String line) => _log.value = [line, ..._log.value].take(30).toList();

  @override
  void initState() {
    super.initState();
    final sdk = widget.sdk;
    sdk.onInAppDeeplink.listen((url) => _add('in-app deeplink: $url'));
    sdk.onInAppUrlOpen.listen((url) => _add('in-app url: $url'));
    sdk.onInAppCustomAction.listen((value) => _add('in-app custom action: $value'));
    sdk.onPushDeeplink.listen(_openPushLink);
    final initial = widget.openedFromPush;
    if (initial != null) {
      WidgetsBinding.instance.addPostFrameCallback((_) => _openPushLink(initial));
    }
  }

  void _openPushLink(String url) {
    _add('push deeplink: $url');
    if (Uri.tryParse(url)?.host == 'cart') _navigatorKey.currentState?.pushNamed('/cart');
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'BzEngage example',
      navigatorKey: _navigatorKey,
      navigatorObservers: [BzEngageNavigatorObserver(widget.sdk)],
      initialRoute: '/home',
      routes: {
        '/home': (_) => HomeScreen(sdk: widget.sdk, log: _log, onLog: _add),
        '/cart': (_) => CartScreen(sdk: widget.sdk),
      },
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key, required this.sdk, required this.log, required this.onLog});
  final BzEngageSDK sdk;
  final ValueNotifier<List<String>> log;
  final ValueChanged<String> onLog;

  Future<void> _run(String label, Future<Object?> Function() action) async {
    try {
      final result = await action();
      onLog('$label${result == null ? '' : ' → $result'}');
    } on BzEngageError catch (e) {
      onLog('$label failed: ${e.code}');
    }
  }

  @override
  Widget build(BuildContext context) {
    final item = {'item_id': 'A1', 'item_name': 'T-shirt', 'price': 9900, 'quantity': 1};
    return Scaffold(
      appBar: AppBar(title: const Text('BzEngage example')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          // Content of the zone "home_top" from the BzEngage editor.
          const BzenZone(name: 'home_top', padding: EdgeInsets.only(bottom: 16)),
          Wrap(spacing: 8, runSpacing: 8, children: [
            FilledButton(
              onPressed: () => _run('identify', () => sdk.identify(kUserId, properties: {'tier': 'gold'})),
              child: const Text('Sign in'),
            ),
            OutlinedButton(onPressed: () => _run('logout', sdk.logout), child: const Text('Sign out')),
            OutlinedButton(
              onPressed: () => _run('person id', sdk.currentPersonId),
              child: const Text('Person ID'),
            ),
            OutlinedButton(
              onPressed: () => _run('event', () async {
                await sdk.trackEvent('example_button', {'source': 'home'});
                return sdk.showMessages();
              }),
              child: const Text('Send event'),
            ),
            OutlinedButton(
              onPressed: () => _run('add to cart', () => sdk.commerce.addToCart(items: [item], value: 9900)),
              child: const Text('Add to cart'),
            ),
            OutlinedButton(
              onPressed: () => _run('cart', sdk.commerce.cartSnapshot),
              child: const Text('Cart state'),
            ),
            OutlinedButton(
              onPressed: () => _run('push consent', () => sdk.setPushOptin(true)),
              child: const Text('Allow marketing push'),
            ),
            OutlinedButton(
              onPressed: () => _run('notification permission', () async {
                // Ask with the tool your app already uses; firebase_messaging is used here.
                if (_usesFirebaseMessaging) await FirebaseMessaging.instance.requestPermission();
                await sdk.reRegisterPushToken();
                return null;
              }),
              child: const Text('Ask notification permission'),
            ),
            OutlinedButton(
              onPressed: () => Navigator.of(context).pushNamed('/cart'),
              child: const Text('Open cart'),
            ),
          ]),
          const SizedBox(height: 16),
          ValueListenableBuilder<List<String>>(
            valueListenable: log,
            builder: (_, lines, _) => Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [for (final l in lines) Text(l, style: const TextStyle(fontSize: 12))],
            ),
          ),
        ],
      ),
    );
  }
}

class CartScreen extends StatelessWidget {
  const CartScreen({super.key, required this.sdk});
  final BzEngageSDK sdk;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Cart')),
      body: Center(
        child: FilledButton(
          onPressed: () => sdk.commerce.purchase(
            transactionId: 'T-${DateTime.now().millisecondsSinceEpoch}',
            items: [
              {'item_id': 'A1', 'item_name': 'T-shirt', 'price': 9900, 'quantity': 1},
            ],
            value: 9900,
          ),
          child: const Text('Purchase'),
        ),
      ),
    );
  }
}
0
likes
140
points
519
downloads

Documentation

API reference

Publisher

verified publisherbzengage.com

Weekly Downloads

Official BzEngage Flutter SDK. A thin Dart layer that forwards calls to the native BzEngage Android and iOS SDKs (in-app messages, zones, push, events, consent, commerce).

Homepage

License

unknown (license)

Dependencies

flutter

More

Packages that depend on bzengage_flutter_sdk

Packages that implement bzengage_flutter_sdk