notify_mvp 1.0.3 copy "notify_mvp: ^1.0.3" to clipboard
notify_mvp: ^1.0.3 copied to clipboard

Official Flutter SDK for NotifyMVP — a OneSignal-like push notification platform. Handles FCM token registration, auto-refresh, and device identification with zero boilerplate.

example/lib/main.dart

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:notify_mvp/notify_mvp.dart';

// ── Background message handler (must be top-level function) ──────────────────
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp();
  debugPrint('[NotifyMVP Example] Background message: ${message.messageId}');
}

// ── Entry point ───────────────────────────────────────────────────────────────
void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // 1. Initialize Firebase first
  await Firebase.initializeApp(
    // Replace with your generated firebase_options.dart
    // options: DefaultFirebaseOptions.currentPlatform,
  );

  // 2. Register background message handler
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);

  // 3. Initialize NotifyMVP SDK
  //    Replace these values with your actual NotifyMVP credentials
  final result = await NotifyMVP.initialize(
    appId: 'app_your_app_id',         // From NotifyMVP dashboard → Projects
    apiKey: 'your_api_key_here',      // From NotifyMVP dashboard → Projects
    baseUrl: 'https://your-worker.workers.dev', // your Cloudflare Worker URL
    debugLogging: true,               // Set false in production
    autoRegister: true,               // Auto-registers on init
  );

  if (result.isSuccess) {
    debugPrint('NotifyMVP: Device registered successfully!');
  } else {
    debugPrint('NotifyMVP: Registration failed — ${result.error}');
  }

  runApp(const MyApp());
}

// ── App ───────────────────────────────────────────────────────────────────────
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'NotifyMVP Example',
      theme: ThemeData.dark(useMaterial3: true).copyWith(
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF6366F1),
          brightness: Brightness.dark,
        ),
      ),
      home: const HomePage(),
    );
  }
}

// ── Home Page ─────────────────────────────────────────────────────────────────
class HomePage extends StatefulWidget {
  const HomePage({super.key});

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

class _HomePageState extends State<HomePage> {
  String _status = 'Initializing…';
  String _fcmToken = 'Not available';
  final List<String> _receivedMessages = [];

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

  Future<void> _setup() async {
    // Update UI with SDK status
    setState(() {
      _status = NotifyMVP.isInitialized ? 'Registered ✓' : 'Not registered';
      _fcmToken = NotifyMVP.fcmToken ?? 'Unavailable';
    });

    // Listen for foreground messages
    FirebaseMessaging.onMessage.listen((RemoteMessage message) {
      setState(() {
        final title = message.notification?.title ?? '(no title)';
        final body = message.notification?.body ?? '(no body)';
        _receivedMessages.insert(0, '$title: $body');
      });

      // Show a snackbar when a foreground notification arrives
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text(
              '${message.notification?.title ?? 'Notification'}: '
              '${message.notification?.body ?? ''}',
            ),
            behavior: SnackBarBehavior.floating,
          ),
        );
      }
    });

    // Handle notification tap when app is in background (not terminated)
    NotifyMVP.onNotificationOpened().listen((payload) {
      debugPrint('[NotifyMVP] Opened from background: ${payload.url}');
      setState(() {
        _receivedMessages.insert(
          0,
          payload.url != null
              ? '${payload.title}: ${payload.body} → ${payload.url}'
              : '${payload.title}: ${payload.body}',
        );
      });
      // Navigate with GoRouter / Navigator using payload.url
    });

    // Handle notification tap when app was terminated
    final initial = await NotifyMVP.getInitialNotification();
    if (initial != null) {
      debugPrint('[NotifyMVP] Opened from terminated: ${initial.url}');
      setState(() {
        _receivedMessages.insert(
          0,
          initial.url != null
              ? '${initial.title}: ${initial.body} → ${initial.url}'
              : '${initial.title}: ${initial.body}',
        );
      });
    }
  }

  Future<void> _manualRegister() async {
    setState(() => _status = 'Registering…');
    final result = await NotifyMVP.register();
    setState(() {
      _status = result.isSuccess ? 'Registered ✓' : 'Failed: ${result.error}';
      _fcmToken = NotifyMVP.fcmToken ?? 'Unavailable';
    });
  }

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;

    return Scaffold(
      appBar: AppBar(
        title: const Text('NotifyMVP SDK Demo'),
        backgroundColor: colorScheme.surface,
      ),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // Status card
            _InfoCard(
              title: 'SDK Status',
              value: _status,
              color: _status.contains('✓')
                  ? Colors.green.shade400
                  : _status.contains('Failed')
                      ? Colors.red.shade400
                      : Colors.amber.shade400,
            ),

            const SizedBox(height: 12),

            // FCM token card
            _InfoCard(
              title: 'FCM Token',
              value: _fcmToken.length > 40
                  ? '${_fcmToken.substring(0, 20)}…${_fcmToken.substring(_fcmToken.length - 20)}'
                  : _fcmToken,
              color: colorScheme.primary,
            ),

            const SizedBox(height: 16),

            // Manual register button
            SizedBox(
              width: double.infinity,
              child: FilledButton.icon(
                onPressed: _manualRegister,
                icon: const Icon(Icons.refresh),
                label: const Text('Re-register Device'),
              ),
            ),

            const SizedBox(height: 24),

            // Received messages
            Text(
              'Received Notifications',
              style: Theme.of(context).textTheme.titleSmall?.copyWith(
                    color: colorScheme.onSurfaceVariant,
                  ),
            ),
            const SizedBox(height: 8),

            Expanded(
              child: _receivedMessages.isEmpty
                  ? Center(
                      child: Text(
                        'No notifications received yet.\nSend one from the NotifyMVP dashboard.',
                        textAlign: TextAlign.center,
                        style: TextStyle(color: colorScheme.onSurfaceVariant),
                      ),
                    )
                  : ListView.separated(
                      itemCount: _receivedMessages.length,
                      separatorBuilder: (_, __) => const Divider(height: 1),
                      itemBuilder: (context, index) => ListTile(
                        dense: true,
                        leading: const Icon(Icons.notifications_outlined, size: 18),
                        title: Text(
                          _receivedMessages[index],
                          style: const TextStyle(fontSize: 13),
                        ),
                      ),
                    ),
            ),
          ],
        ),
      ),
    );
  }
}

// ── Reusable info card widget ─────────────────────────────────────────────────
class _InfoCard extends StatelessWidget {
  final String title;
  final String value;
  final Color color;

  const _InfoCard({
    required this.title,
    required this.value,
    required this.color,
  });

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.all(14),
      decoration: BoxDecoration(
        color: Theme.of(context).colorScheme.surfaceContainerHighest,
        borderRadius: BorderRadius.circular(10),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            title,
            style: TextStyle(
              fontSize: 11,
              fontWeight: FontWeight.w600,
              color: Theme.of(context).colorScheme.onSurfaceVariant,
              letterSpacing: 0.8,
            ),
          ),
          const SizedBox(height: 4),
          Text(
            value,
            style: TextStyle(
              fontSize: 14,
              fontWeight: FontWeight.w500,
              color: color,
              fontFamily: 'monospace',
            ),
          ),
        ],
      ),
    );
  }
}
1
likes
70
points
63
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Official Flutter SDK for NotifyMVP — a OneSignal-like push notification platform. Handles FCM token registration, auto-refresh, and device identification with zero boilerplate.

Homepage

License

unknown (license)

Dependencies

device_info_plus, firebase_core, firebase_messaging, flutter, flutter_local_notifications, flutter_secure_storage, http, package_info_plus, uuid

More

Packages that depend on notify_mvp