swift_notifications 0.0.1 copy "swift_notifications: ^0.0.1" to clipboard
swift_notifications: ^0.0.1 copied to clipboard

A unified Flutter plugin for rich push and local notifications on Android, iOS, and macOS with no native code required.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:swift_notifications/swift_notifications.dart';
import 'dart:async';
import 'dart:convert';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // Enable verbose logging for debugging
  SwiftNotifications.enableVerboseLogging();
  
  // Initialize notifications
  final notifications = SwiftNotifications();
  await notifications.initialize(verbose: true);
  
  runApp(MyApp(notifications: notifications));
}

class MyApp extends StatelessWidget {
  final SwiftNotifications notifications;
  
  const MyApp({super.key, required this.notifications});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Swift Notifications Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      routes: {
        '/': (context) => HomeScreen(notifications: notifications),
        '/message': (context) => MessageScreen(),
        '/order': (context) => OrderScreen(),
        '/settings': (context) => SettingsScreen(notifications: notifications),
        '/test-cold-start': (context) => ColdStartTestScreen(notifications: notifications),
      },
      initialRoute: '/',
    );
  }
}

class HomeScreen extends StatefulWidget {
  final SwiftNotifications notifications;
  
  const HomeScreen({super.key, required this.notifications});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  String _status = 'Initializing...';
  NotificationPermissionStatus _permissionStatus = NotificationPermissionStatus.notDetermined;
  StreamSubscription<NotificationResponse>? _notificationSubscription;
  List<NotificationResponse> _notificationHistory = [];

  @override
  void initState() {
    super.initState();
    _checkPermission();
    _listenToNotifications();
  }

  Future<void> _checkPermission() async {
    try {
      final status = await widget.notifications.checkPermission();
      setState(() {
        _permissionStatus = status;
        _status = 'Permission: ${status.name}';
      });
    } catch (e) {
      setState(() {
        _status = 'Error: $e';
      });
    }
  }

  Future<void> _requestPermission() async {
    try {
      final status = await widget.notifications.requestPermission();
      setState(() {
        _permissionStatus = status;
        _status = 'Permission: ${status.name}';
      });
      _showSnackBar('Permission status: ${status.name}');
    } catch (e) {
      _showSnackBar('Error requesting permission: $e');
    }
  }

  void _listenToNotifications() {
    _notificationSubscription = widget.notifications.onNotificationResponse.listen((response) {
      setState(() {
        _notificationHistory.insert(0, response);
        if (_notificationHistory.length > 10) {
          _notificationHistory.removeLast();
        }
      });
      
      _showNotificationDialog(response);
    });
  }

  void _showNotificationDialog(NotificationResponse response) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Notification Response'),
        content: SingleChildScrollView(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.min,
            children: [
              Text('Notification ID: ${response.notificationId}'),
              const SizedBox(height: 8),
              Text('Action ID: ${response.actionId ?? "Tapped (body)"}'),
              if (response.payload != null) ...[
                const SizedBox(height: 8),
                const Text('Payload:', style: TextStyle(fontWeight: FontWeight.bold)),
                Text(JsonEncoder.withIndent('  ').convert(response.payload!)),
              ],
              if (response.actionPayload != null) ...[
                const SizedBox(height: 8),
                const Text('Action Payload:', style: TextStyle(fontWeight: FontWeight.bold)),
                Text(JsonEncoder.withIndent('  ').convert(response.actionPayload!)),
              ],
            ],
          ),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.of(context).pop(),
            child: const Text('OK'),
          ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    _notificationSubscription?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('Swift Notifications'),
        actions: [
          IconButton(
            icon: const Icon(Icons.settings),
            onPressed: () => Navigator.pushNamed(context, '/settings'),
          ),
        ],
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // Status Card
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  children: [
                    const Text(
                      'Status',
                      style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                    ),
                    const SizedBox(height: 8),
                    Text(_status),
                    const SizedBox(height: 8),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                      children: [
                        ElevatedButton(
                          onPressed: _checkPermission,
                          child: const Text('Check Permission'),
                        ),
                        ElevatedButton(
                          onPressed: _requestPermission,
                          child: const Text('Request Permission'),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 16),
            
            // Basic Notifications Section
            _buildSection(
              title: 'Basic Notifications',
              children: [
                _buildButton(
                  'Simple Notification',
                  () => _showSimpleNotification(),
                ),
                _buildButton(
                  'Notification with Payload',
                  () => _showNotificationWithPayload(),
                ),
                _buildButton(
                  'Notification for Cold Start Test',
                  () => _showColdStartNotification(),
                ),
              ],
            ),
            
            // Rich Notifications Section
            _buildSection(
              title: 'Rich Notifications',
              children: [
                _buildButton(
                  'Notification with Image',
                  () => _showImageNotification(),
                ),
                _buildButton(
                  'Notification with Buttons',
                  () => _showNotificationWithButtons(),
                ),
                _buildButton(
                  'Rich Notification (Image + Buttons)',
                  () => _showRichNotification(),
                ),
              ],
            ),
            
            // Advanced Features Section
            _buildSection(
              title: 'Advanced Features',
              children: [
                _buildButton(
                  'Test Cold Start Handling',
                  () => Navigator.pushNamed(context, '/test-cold-start'),
                ),
                _buildButton(
                  'Notification with Custom Category',
                  () => _showNotificationWithCategory(),
                ),
                _buildButton(
                  'Notification with Priority',
                  () => _showNotificationWithPriority(),
                ),
              ],
            ),
            
            // Actions Section
            _buildSection(
              title: 'Actions',
              children: [
                _buildButton(
                  'Cancel All Notifications',
                  () => _cancelAllNotifications(),
                  color: Colors.red,
                ),
              ],
            ),
            
            // Notification History
            if (_notificationHistory.isNotEmpty) ...[
              const SizedBox(height: 24),
              const Text(
                'Notification History',
                style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 8),
              ..._notificationHistory.map((response) => Card(
                margin: const EdgeInsets.only(bottom: 8),
                child: ListTile(
                  title: Text('ID: ${response.notificationId}'),
                  subtitle: Text('Action: ${response.actionId ?? "Tapped"}'),
                  trailing: const Icon(Icons.arrow_forward_ios, size: 16),
                  onTap: () => _showNotificationDialog(response),
                ),
              )),
            ],
          ],
        ),
      ),
    );
  }

  Widget _buildSection({required String title, required List<Widget> children}) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          title,
          style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 8),
        ...children,
        const SizedBox(height: 24),
      ],
    );
  }

  Widget _buildButton(String text, VoidCallback onPressed, {Color? color}) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 8.0),
      child: ElevatedButton(
        onPressed: onPressed,
        style: color != null ? ElevatedButton.styleFrom(backgroundColor: color) : null,
        child: Text(text),
      ),
    );
  }

  Future<void> _showSimpleNotification() async {
    try {
      await widget.notifications.showSimpleNotification(
        id: 'simple_${DateTime.now().millisecondsSinceEpoch}',
        title: 'Simple Notification',
        body: 'This is a simple text notification without any extras.',
      );
      _showSnackBar('Simple notification sent!');
    } catch (e) {
      _showSnackBar('Error: $e');
    }
  }

  Future<void> _showNotificationWithPayload() async {
    try {
      await widget.notifications.showSimpleNotification(
        id: 'payload_${DateTime.now().millisecondsSinceEpoch}',
        title: 'Notification with Payload',
        body: 'Tap this notification to see the payload data.',
        payload: {
          'type': 'user_message',
          'userId': '12345',
          'message': 'Hello from notification!',
          'timestamp': DateTime.now().toIso8601String(),
        },
      );
      _showSnackBar('Notification with payload sent!');
    } catch (e) {
      _showSnackBar('Error: $e');
    }
  }

  Future<void> _showColdStartNotification() async {
    try {
      await widget.notifications.showNotification(
        NotificationRequest(
          id: 'coldstart_${DateTime.now().millisecondsSinceEpoch}',
          title: 'Cold Start Test',
          body: 'Close the app completely, then tap this notification. The event will be stored and delivered when the app starts.',
          payload: {
            'type': 'cold_start_test',
            'message': 'This notification tests cold start event handling',
            'timestamp': DateTime.now().toIso8601String(),
          },
          buttonsEnabled: true,
          actions: [
            NotificationAction(
              id: 'view_message',
              title: 'View Message',
              payload: {'action': 'view', 'screen': '/message'},
            ),
            NotificationAction(
              id: 'view_order',
              title: 'View Order',
              payload: {'action': 'view', 'screen': '/order'},
            ),
          ],
        ),
      );
      _showSnackBar('Cold start test notification sent! Close the app and tap it.');
    } catch (e) {
      _showSnackBar('Error: $e');
    }
  }

  Future<void> _showImageNotification() async {
    try {
      await widget.notifications.showImageNotification(
        id: 'image_${DateTime.now().millisecondsSinceEpoch}',
        title: 'Image Notification',
        body: 'This notification includes an image!',
        imageUrl: 'https://picsum.photos/400/300?random=${DateTime.now().millisecondsSinceEpoch}',
      );
      _showSnackBar('Image notification sent!');
    } catch (e) {
      _showSnackBar('Error: $e');
    }
  }

  Future<void> _showNotificationWithButtons() async {
    try {
      await widget.notifications.showNotification(
        NotificationRequest(
          id: 'buttons_${DateTime.now().millisecondsSinceEpoch}',
          title: 'Notification with Buttons',
          body: 'This notification has action buttons. Try tapping them!',
          buttonsEnabled: true,
          actions: [
            NotificationAction(
              id: 'action_view',
              title: 'View',
              payload: {'action': 'view'},
            ),
            NotificationAction(
              id: 'action_reply',
              title: 'Reply',
              payload: {'action': 'reply'},
            ),
            NotificationAction(
              id: 'action_delete',
              title: 'Delete',
              isDestructive: true,
              payload: {'action': 'delete'},
            ),
          ],
        ),
      );
      _showSnackBar('Notification with buttons sent!');
    } catch (e) {
      _showSnackBar('Error: $e');
    }
  }

  Future<void> _showRichNotification() async {
    try {
      await widget.notifications.showNotification(
        NotificationRequest(
          id: 'rich_${DateTime.now().millisecondsSinceEpoch}',
          title: 'Rich Notification',
          body: 'This is a rich notification with both image and buttons!',
          imageEnabled: true,
          image: NotificationImage(
            url: 'https://picsum.photos/400/300?random=${DateTime.now().millisecondsSinceEpoch}',
          ),
          buttonsEnabled: true,
          actions: [
            NotificationAction(
              id: 'action_like',
              title: '👍 Like',
              payload: {'action': 'like'},
            ),
            NotificationAction(
              id: 'action_share',
              title: '📤 Share',
              payload: {'action': 'share'},
            ),
          ],
          payload: {
            'type': 'rich_notification',
            'timestamp': DateTime.now().toIso8601String(),
          },
        ),
      );
      _showSnackBar('Rich notification sent!');
    } catch (e) {
      _showSnackBar('Error: $e');
    }
  }

  Future<void> _showNotificationWithCategory() async {
    try {
      await widget.notifications.showNotification(
        NotificationRequest(
          id: 'category_${DateTime.now().millisecondsSinceEpoch}',
          title: 'Categorized Notification',
          body: 'This notification uses a custom category.',
          categoryId: 'custom_category',
          payload: {'category': 'custom_category'},
        ),
      );
      _showSnackBar('Categorized notification sent!');
    } catch (e) {
      _showSnackBar('Error: $e');
    }
  }

  Future<void> _showNotificationWithPriority() async {
    try {
      await widget.notifications.showNotification(
        NotificationRequest(
          id: 'priority_${DateTime.now().millisecondsSinceEpoch}',
          title: 'High Priority Notification',
          body: 'This notification has high priority (Android).',
          priority: NotificationPriority.high,
          payload: {'priority': 'high'},
        ),
      );
      _showSnackBar('High priority notification sent!');
    } catch (e) {
      _showSnackBar('Error: $e');
    }
  }

  Future<void> _cancelAllNotifications() async {
    try {
      await widget.notifications.cancelAllNotifications();
      _showSnackBar('All notifications cancelled!');
    } catch (e) {
      _showSnackBar('Error: $e');
    }
  }

  void _showSnackBar(String message) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(message)),
    );
  }
}

// Example screens for routing
class MessageScreen extends StatelessWidget {
  const MessageScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Message Screen'),
      ),
      body: const Center(
        child: Text('This is the message screen'),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Order Screen'),
      ),
      body: const Center(
        child: Text('This is the order screen'),
      ),
    );
  }
}

class SettingsScreen extends StatefulWidget {
  final SwiftNotifications notifications;
  
  const SettingsScreen({super.key, required this.notifications});

  @override
  State<SettingsScreen> createState() => _SettingsScreenState();
}

class _SettingsScreenState extends State<SettingsScreen> {
  bool _verboseLogging = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Settings'),
      ),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          SwitchListTile(
            title: const Text('Verbose Logging'),
            subtitle: const Text('Enable detailed logging for debugging'),
            value: _verboseLogging,
            onChanged: (value) {
              setState(() {
                _verboseLogging = value;
                if (value) {
                  SwiftNotifications.enableVerboseLogging();
                } else {
                  SwiftNotifications.disableVerboseLogging();
                }
              });
            },
          ),
          const Divider(),
          ListTile(
            title: const Text('About'),
            subtitle: const Text('Swift Notifications Example App'),
          ),
        ],
      ),
    );
  }
}

class ColdStartTestScreen extends StatelessWidget {
  final SwiftNotifications notifications;
  
  const ColdStartTestScreen({super.key, required this.notifications});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Cold Start Test'),
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const Card(
              color: Colors.blue,
              child: Padding(
                padding: EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      'How to Test Cold Start',
                      style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white),
                    ),
                    SizedBox(height: 8),
                    Text(
                      '1. Send a notification using the button below\n'
                      '2. Close the app completely (swipe away from recent apps)\n'
                      '3. Tap the notification\n'
                      '4. The app will launch and the event will be delivered',
                      style: TextStyle(color: Colors.white),
                    ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: () async {
                try {
                  await notifications.showNotification(
                    NotificationRequest(
                      id: 'coldstart_test_${DateTime.now().millisecondsSinceEpoch}',
                      title: 'Cold Start Test',
                      body: 'Close the app completely, then tap this notification to test cold start handling.',
                      payload: {
                        'type': 'cold_start_test',
                        'testId': DateTime.now().millisecondsSinceEpoch.toString(),
                        'message': 'This is a cold start test notification',
                      },
                      buttonsEnabled: true,
                      actions: [
                        NotificationAction(
                          id: 'test_action_1',
                          title: 'Test Action 1',
                          payload: {'action': 'test1', 'screen': '/message'},
                        ),
                        NotificationAction(
                          id: 'test_action_2',
                          title: 'Test Action 2',
                          payload: {'action': 'test2', 'screen': '/order'},
                        ),
                      ],
                    ),
                  );
                  ScaffoldMessenger.of(context).showSnackBar(
                    const SnackBar(
                      content: Text('Notification sent! Close the app and tap it to test cold start.'),
                      duration: Duration(seconds: 5),
                    ),
                  );
                } catch (e) {
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(content: Text('Error: $e')),
                  );
                }
              },
              child: const Text('Send Cold Start Test Notification'),
            ),
            const SizedBox(height: 16),
            const Card(
              child: Padding(
                padding: EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      'What Happens:',
                      style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
                    ),
                    SizedBox(height: 8),
                    Text('• Notification event is stored in persistent storage'),
                    Text('• App launches from cold start'),
                    Text('• Plugin checks for pending events on initialization'),
                    Text('• Event is delivered to onNotificationResponse stream'),
                    Text('• You can handle routing based on the response'),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
2
likes
150
points
162
downloads

Publisher

verified publisherswiftflutter.com

Weekly Downloads

A unified Flutter plugin for rich push and local notifications on Android, iOS, and macOS with no native code required.

Repository (GitHub)

Documentation

API reference

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on swift_notifications

Packages that implement swift_notifications