firebase_notification_helper 0.0.4 copy "firebase_notification_helper: ^0.0.4" to clipboard
firebase_notification_helper: ^0.0.4 copied to clipboard

A lightweight helper for Firebase Messaging, local notifications, and sending FCM push notifications via Legacy API. Best for internal tools and testing environments.

example/lib/main.dart

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

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

  await FirebaseNotificationHelper.initialize(
    config: const NotificationHelperConfig(
      debug: true,

      // Android notification channel
      channelId: 'high_importance_channel',
      channelName: 'High Importance Notifications',
      channelDescription: 'Used for important notifications.',

      // Firebase Cloud Function config
      cloudFunctionName: 'sendNotification',
      cloudFunctionRegion: 'us-central1',

      requestPermissionOnInit: true,
      enableForegroundLocalNotification: true,
      handleInitialMessage: true,
      emitInitialToken: true,
    ),
    onNotificationTap: (payload) {
      debugPrint('Notification tapped: $payload');
    },
    onTokenChanged: (token) {
      debugPrint('FCM Token Changed: $token');
    },
  );

  runApp(const FcmTokenSenderApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FCM Sender Example',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        useMaterial3: true,
        colorSchemeSeed: const Color(0xFFFF7A00),
      ),
      home: const FcmSenderHome(),
    );
  }
}

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

  @override
  State<FcmSenderHome> createState() => _FcmSenderHomeState();
}

class _FcmSenderHomeState extends State<FcmSenderHome> {
  final TextEditingController _tokenCtrl = TextEditingController();

  final TextEditingController _topicCtrl = TextEditingController(
    text: 'all_users',
  );

  final TextEditingController _titleCtrl = TextEditingController(
    text: 'Test Notification',
  );

  final TextEditingController _bodyCtrl = TextEditingController(
    text: 'This is a remote notification from firebase_notification_helper.',
  );

  final TextEditingController _screenCtrl = TextEditingController(
    text: 'home',
  );

  bool _isSending = false;
  bool _sendToTopic = false;

  String? _myToken;
  String? _result;

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

  Future<void> _loadToken() async {
    try {
      final token = await FirebaseNotificationHelper.getToken();

      if (!mounted) return;

      setState(() {
        _myToken = token;
        _tokenCtrl.text = token ?? '';
      });

      debugPrint('My FCM Token: $token');
    } catch (e) {
      if (!mounted) return;

      setState(() {
        _result = 'Token fetch error: $e';
      });
    }
  }

  @override
  void dispose() {
    _tokenCtrl.dispose();
    _topicCtrl.dispose();
    _titleCtrl.dispose();
    _bodyCtrl.dispose();
    _screenCtrl.dispose();
    super.dispose();
  }

  Future<void> _copyMyTokenToTarget() async {
    final token = _myToken;

    if (token == null || token.trim().isEmpty) {
      setState(() {
        _result = 'Token not found. Please refresh token.';
      });
      return;
    }

    setState(() {
      _tokenCtrl.text = token;
      _result = 'Own device token copied to target token field.';
    });
  }

  Future<void> _subscribeToTopic() async {
    final topic = _topicCtrl.text.trim();

    if (topic.isEmpty) {
      setState(() {
        _result = 'Topic name is required.';
      });
      return;
    }

    try {
      await FirebaseNotificationHelper.subscribeToTopic(topic);

      if (!mounted) return;

      setState(() {
        _result = 'Subscribed to topic: $topic';
      });
    } catch (e) {
      if (!mounted) return;

      setState(() {
        _result = 'Topic subscribe error: $e';
      });
    }
  }

  Future<void> _unsubscribeFromTopic() async {
    final topic = _topicCtrl.text.trim();

    if (topic.isEmpty) {
      setState(() {
        _result = 'Topic name is required.';
      });
      return;
    }

    try {
      await FirebaseNotificationHelper.unsubscribeFromTopic(topic);

      if (!mounted) return;

      setState(() {
        _result = 'Unsubscribed from topic: $topic';
      });
    } catch (e) {
      if (!mounted) return;

      setState(() {
        _result = 'Topic unsubscribe error: $e';
      });
    }
  }

  Future<void> _showLocalNotification() async {
    final title = _titleCtrl.text.trim().isEmpty
        ? 'Local Notification'
        : _titleCtrl.text.trim();

    final body = _bodyCtrl.text.trim().isEmpty
        ? 'This is a local test notification.'
        : _bodyCtrl.text.trim();

    await FirebaseNotificationHelper.showLocalNotification(
      title: title,
      body: body,
      payload: {
        'type': 'local',
        'screen': _screenCtrl.text.trim(),
      },
    );
  }

  Future<void> _sendRemoteNotification() async {
    final token = _tokenCtrl.text.trim();
    final topic = _topicCtrl.text.trim();

    final title = _titleCtrl.text.trim().isEmpty
        ? 'Notification'
        : _titleCtrl.text.trim();

    final body = _bodyCtrl.text.trim().isEmpty
        ? 'This is a remote notification.'
        : _bodyCtrl.text.trim();

    if (_sendToTopic) {
      if (topic.isEmpty) {
        setState(() {
          _result = 'Topic name is required.';
        });
        return;
      }
    } else {
      if (token.isEmpty) {
        setState(() {
          _result = 'Target FCM token is required.';
        });
        return;
      }
    }

    setState(() {
      _isSending = true;
      _result = null;
    });

    try {
      final result = await FirebaseNotificationHelper.sendRemoteNotification(
        target: _sendToTopic ? FcmTarget.topic(topic) : FcmTarget.token(token),
        title: title,
        body: body,
        data: {
          'sender': 'firebase_notification_helper_example',
          'screen': _screenCtrl.text.trim().isEmpty
              ? 'home'
              : _screenCtrl.text.trim(),
          'time': DateTime.now().toIso8601String(),
        },
      );

      if (!mounted) return;

      setState(() {
        _result = result.rawBody;
      });
    } catch (e) {
      if (!mounted) return;

      setState(() {
        _result = 'Error: $e';
      });
    } finally {
      if (!mounted) return;

      setState(() {
        _isSending = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    final pad = MediaQuery.of(context).padding;

    return Scaffold(
      appBar: AppBar(
        title: const Text('FCM Sender Example'),
        actions: [
          IconButton(
            onPressed: _loadToken,
            icon: const Icon(Icons.refresh),
            tooltip: 'Refresh Token',
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: _showLocalNotification,
        icon: const Icon(Icons.notifications),
        label: const Text('Local'),
      ),
      body: Padding(
        padding: EdgeInsets.fromLTRB(
          16,
          16 + pad.top,
          16,
          16 + pad.bottom,
        ),
        child: SingleChildScrollView(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              _SectionCard(
                title: 'My FCM Token',
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    SelectableText(
                      _myToken?.isNotEmpty == true
                          ? _myToken!
                          : 'Token loading...',
                      style: const TextStyle(fontSize: 12),
                    ),
                    const SizedBox(height: 12),
                    SizedBox(
                      width: double.infinity,
                      child: OutlinedButton.icon(
                        onPressed: _copyMyTokenToTarget,
                        icon: const Icon(Icons.copy),
                        label: const Text('Use My Token As Target'),
                      ),
                    ),
                  ],
                ),
              ),
              const SizedBox(height: 16),
              _SectionCard(
                title: 'Send Target',
                child: Column(
                  children: [
                    SwitchListTile(
                      contentPadding: EdgeInsets.zero,
                      value: _sendToTopic,
                      title: const Text('Send to Topic'),
                      subtitle: Text(
                        _sendToTopic
                            ? 'Notification will be sent to topic subscribers.'
                            : 'Notification will be sent to a single device token.',
                      ),
                      onChanged: (value) {
                        setState(() {
                          _sendToTopic = value;
                        });
                      },
                    ),
                    const SizedBox(height: 8),
                    if (_sendToTopic) ...[
                      TextField(
                        controller: _topicCtrl,
                        decoration: const InputDecoration(
                          border: OutlineInputBorder(),
                          labelText: 'Topic Name',
                          hintText: 'all_users',
                        ),
                      ),
                      const SizedBox(height: 12),
                      Row(
                        children: [
                          Expanded(
                            child: OutlinedButton.icon(
                              onPressed: _subscribeToTopic,
                              icon: const Icon(Icons.add_alert),
                              label: const Text('Subscribe'),
                            ),
                          ),
                          const SizedBox(width: 10),
                          Expanded(
                            child: OutlinedButton.icon(
                              onPressed: _unsubscribeFromTopic,
                              icon: const Icon(Icons.notifications_off),
                              label: const Text('Unsubscribe'),
                            ),
                          ),
                        ],
                      ),
                    ] else ...[
                      TextField(
                        controller: _tokenCtrl,
                        decoration: const InputDecoration(
                          border: OutlineInputBorder(),
                          labelText: 'Target Device FCM Token',
                          hintText: 'Paste target device FCM token here',
                        ),
                        maxLines: 4,
                      ),
                    ],
                  ],
                ),
              ),
              const SizedBox(height: 16),
              _SectionCard(
                title: 'Notification Content',
                child: Column(
                  children: [
                    TextField(
                      controller: _titleCtrl,
                      decoration: const InputDecoration(
                        border: OutlineInputBorder(),
                        labelText: 'Notification Title',
                      ),
                    ),
                    const SizedBox(height: 12),
                    TextField(
                      controller: _bodyCtrl,
                      decoration: const InputDecoration(
                        border: OutlineInputBorder(),
                        labelText: 'Notification Body',
                      ),
                      maxLines: 3,
                    ),
                    const SizedBox(height: 12),
                    TextField(
                      controller: _screenCtrl,
                      decoration: const InputDecoration(
                        border: OutlineInputBorder(),
                        labelText: 'Data: screen',
                        hintText: 'home / chat / order_details',
                      ),
                    ),
                  ],
                ),
              ),
              const SizedBox(height: 20),
              SizedBox(
                width: double.infinity,
                child: FilledButton.icon(
                  onPressed: _isSending ? null : _sendRemoteNotification,
                  icon: _isSending
                      ? const SizedBox(
                    width: 18,
                    height: 18,
                    child: CircularProgressIndicator(strokeWidth: 2),
                  )
                      : const Icon(Icons.send),
                  label: Text(
                    _isSending
                        ? 'Sending...'
                        : _sendToTopic
                        ? 'Send Remote Notification To Topic'
                        : 'Send Remote Notification To Token',
                  ),
                ),
              ),
              const SizedBox(height: 20),
              _SectionCard(
                title: 'Response',
                child: Container(
                  constraints: const BoxConstraints(
                    minHeight: 80,
                    maxHeight: 260,
                  ),
                  width: double.infinity,
                  padding: const EdgeInsets.all(12),
                  decoration: BoxDecoration(
                    color: Colors.grey.shade50,
                    borderRadius: BorderRadius.circular(8),
                    border: Border.all(color: Colors.grey.shade300),
                  ),
                  child: SingleChildScrollView(
                    child: SelectableText(
                      _result ?? 'No response yet.',
                      style: const TextStyle(fontSize: 12),
                    ),
                  ),
                ),
              ),
              const SizedBox(height: 80),
            ],
          ),
        ),
      ),
    );
  }
}

class _SectionCard extends StatelessWidget {
  const _SectionCard({
    required this.title,
    required this.child,
  });

  final String title;
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return Card(
      elevation: 0,
      color: Theme.of(context).cardColor,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(14),
        side: BorderSide(
          color: Theme.of(context).dividerColor.withOpacity(0.3),
        ),
      ),
      child: Padding(
        padding: const EdgeInsets.all(14),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              title,
              style: Theme.of(context).textTheme.titleMedium?.copyWith(
                fontWeight: FontWeight.w700,
              ),
            ),
            const SizedBox(height: 12),
            child,
          ],
        ),
      ),
    );
  }
}
1
likes
140
points
180
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A lightweight helper for Firebase Messaging, local notifications, and sending FCM push notifications via Legacy API. Best for internal tools and testing environments.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

cloud_functions, firebase_core, firebase_messaging, flutter, flutter_local_notifications, http

More

Packages that depend on firebase_notification_helper