Skip to main content

hailbell 1.0.0 copy "hailbell: ^1.0.0" to clipboard
hailbell: ^1.0.0 copied to clipboard

In-app support chat and push notifications for Flutter apps on iOS and Android, using Hailbell's native SDKs.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:hailbell/hailbell.dart';

/// The platform's App ID from the Hailbell dashboard. Pass it when you run the example:
///
///     flutter run --dart-define=HAILBELL_APP_ID=app_...
const appId = String.fromEnvironment('HAILBELL_APP_ID');

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  if (appId.isNotEmpty) await HailbellSupport.configure();
  runApp(const ExampleApp());
}

/// Every Hailbell call in one place, so an empty App ID turns the chat off and hides its entry points.
class HailbellSupport {
  static bool get isEnabled => appId.isNotEmpty;

  static Future<void> configure() async {
    // Set before configure, so a tap that launched the app reaches it.
    HailbellPush.onOpen = (notification) {
      debugPrint('Hailbell push tapped: ${notification.url}');
      return false; // false: Hailbell opens the link
    };
    await Hailbell.configure(appId: appId);
    await Hailbell.setMetadata({'plan': 'free', 'appVersion': '1.0.0 (1)'});
    await Hailbell.setPaywallPlacements(['support_offer', 'winback']);
    Hailbell.onPaywall = (placement, purchased) {
      // Open your paywall for `placement` here, and call purchased() if they buy.
      debugPrint('Hailbell offer tapped: $placement');
    };
  }

  /// `source` names the button, so the dashboard shows where people find the chat.
  static Future<void> open(String source) async {
    await Hailbell.track('chat_opened', properties: {'source': source});
    await Hailbell.open();
  }
}

class ExampleApp extends StatelessWidget {
  const ExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Hailbell example',
      theme: ThemeData(colorSchemeSeed: const Color(0xFF3B5BDB)),
      darkTheme: ThemeData(colorSchemeSeed: const Color(0xFF3B5BDB), brightness: Brightness.dark),
      home: const HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  bool? _canNotify;
  bool _marketing = false;
  bool? _dark = Hailbell.darkMode;

  @override
  void initState() {
    super.initState();
    if (HailbellSupport.isEnabled) _refreshPermission();
  }

  Future<void> _refreshPermission() async {
    final allowed = await HailbellPush.canNotify();
    if (mounted) setState(() => _canNotify = allowed);
  }

  Future<void> _run(Future<void> Function() action, String done) async {
    final messenger = ScaffoldMessenger.of(context);
    try {
      await action();
      messenger.showSnackBar(SnackBar(content: Text(done)));
    } on HailbellException catch (e) {
      messenger.showSnackBar(SnackBar(content: Text('Hailbell: ${e.message ?? e.code}')));
    }
  }

  @override
  Widget build(BuildContext context) {
    if (!HailbellSupport.isEnabled) {
      return const Scaffold(
        body: Center(
          child: Padding(
            padding: EdgeInsets.all(24),
            child: Text(
              'Run with --dart-define=HAILBELL_APP_ID=app_... to try the chat.',
              textAlign: TextAlign.center,
            ),
          ),
        ),
      );
    }
    return Scaffold(
      appBar: AppBar(
        title: const Text('Profile'),
        actions: [SupportButton(onPressed: () => HailbellSupport.open('profile_header'))],
      ),
      body: ListView(
        children: [
          ListTile(
            leading: const Icon(Icons.forum_outlined),
            title: const Text('Chat with us'),
            onTap: () => HailbellSupport.open('profile_row'),
          ),
          const Divider(),
          ListTile(
            leading: const Icon(Icons.person_outline),
            title: const Text('Sign in as a test user'),
            subtitle: const Text('identify with a name and email, no token'),
            onTap: () => _run(
              () => Hailbell.identify(userId: 'test-user', name: 'Test User', email: 'test@example.com'),
              'Identified',
            ),
          ),
          ListTile(
            leading: const Icon(Icons.logout),
            title: const Text('Sign out'),
            subtitle: const Text('reset'),
            onTap: () => _run(Hailbell.reset, 'Back to the anonymous chat'),
          ),
          ListTile(
            leading: const Icon(Icons.workspace_premium_outlined),
            title: const Text('Become Pro'),
            subtitle: const Text('setMetadata and push tags'),
            onTap: () => _run(() async {
              await Hailbell.setMetadata({'plan': 'pro'});
              await HailbellPush.setTags({'plan': 'pro'});
            }, 'Plan set to pro'),
          ),
          ListTile(
            leading: const Icon(Icons.close),
            title: const Text('Close a paywall'),
            subtitle: const Text('track paywall_decline'),
            onTap: () => _run(
              () => Hailbell.track('paywall_decline', properties: {'placement': 'onboarding'}),
              'Signal sent',
            ),
          ),
          const Divider(),
          ListTile(
            leading: const Icon(Icons.notifications_outlined),
            title: const Text('Notifications'),
            subtitle: Text(switch (_canNotify) {
              null => 'Checking…',
              true => 'Allowed',
              false => 'Not allowed. Tap to ask.',
            }),
            onTap: () async {
              await HailbellPush.requestPermission();
              await _refreshPermission();
            },
          ),
          SwitchListTile(
            secondary: const Icon(Icons.local_offer_outlined),
            title: const Text('Offers and updates'),
            subtitle: const Text('setMarketing'),
            value: _marketing,
            onChanged: (on) {
              setState(() => _marketing = on);
              HailbellPush.setMarketing(on);
            },
          ),
          ListTile(
            leading: const Icon(Icons.dark_mode_outlined),
            title: const Text('Chat look'),
            subtitle: Padding(
              padding: const EdgeInsets.only(top: 8),
              child: SegmentedButton<bool?>(
                segments: const [
                  ButtonSegment(value: null, label: Text('Phone')),
                  ButtonSegment(value: false, label: Text('Light')),
                  ButtonSegment(value: true, label: Text('Dark')),
                ],
                selected: {_dark},
                onSelectionChanged: (choice) {
                  setState(() => _dark = choice.single);
                  Hailbell.darkMode = choice.single;
                },
              ),
            ),
          ),
          const Divider(),
          ListTile(
            leading: Icon(Icons.delete_outline, color: Theme.of(context).colorScheme.error),
            title: const Text('Delete my account'),
            subtitle: const Text('deleteData'),
            onTap: () => _run(Hailbell.deleteData, 'Chat deleted from Hailbell'),
          ),
        ],
      ),
    );
  }
}

/// A headset button with a red dot while there are unread replies.
class SupportButton extends StatelessWidget {
  const SupportButton({super.key, required this.onPressed});

  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<int>(
      valueListenable: Hailbell.unreadCount,
      builder: (context, unread, _) => IconButton(
        tooltip: unread > 0 ? 'Chat with us, $unread unread' : 'Chat with us',
        onPressed: onPressed,
        icon: Badge(isLabelVisible: unread > 0, smallSize: 9, child: const Icon(Icons.headset_mic_outlined)),
      ),
    );
  }
}
0
likes
160
points
367
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

In-app support chat and push notifications for Flutter apps on iOS and Android, using Hailbell's native SDKs.

Homepage
Repository (GitHub)
View/report issues

Topics

#chat #support #push-notifications #customer-support

License

MIT (license)

Dependencies

flutter

More

Packages that depend on hailbell

Packages that implement hailbell