Skip to main content

notifiable_flutter 0.1.0 copy "notifiable_flutter: ^0.1.0" to clipboard
notifiable_flutter: ^0.1.0 copied to clipboard

Flutter SDK for Notifiable. Registers a device, asks for notification permission, and identifies the person using the app.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:notifiable_flutter/notifiable.dart';
import 'package:shared_preferences/shared_preferences.dart';

const _keyName = 'notifiable.example.publicKey';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const ExampleApp());
}

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

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

class _ExampleAppState extends State<ExampleApp> {
  final _navigatorKey = GlobalKey<NavigatorState>();
  String? _publicKey;

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

  Future<void> _load() async {
    final preferences = await SharedPreferences.getInstance();
    final stored = preferences.getString(_keyName);

    if (!mounted) {
      return;
    }

    setState(() => _publicKey = stored);
  }

  Future<void> _connect(String publicKey) async {
    final preferences = await SharedPreferences.getInstance();
    await preferences.setString(_keyName, publicKey);

    if (!mounted) {
      return;
    }

    setState(() => _publicKey = publicKey);
  }

  Future<void> _disconnect() async {
    final preferences = await SharedPreferences.getInstance();
    await preferences.remove(_keyName);

    if (!mounted) {
      return;
    }

    setState(() => _publicKey = null);
  }

  void _open(String url) {
    final path = _routePath(url);
    final page = ExamplePage.matching(path);

    if (page == null) {
      return;
    }

    _navigatorKey.currentState?.push(
      MaterialPageRoute<void>(builder: (_) => ExampleScreen(page: page)),
    );
  }

  @override
  Widget build(BuildContext context) {
    final publicKey = _publicKey;

    return MaterialApp(
      navigatorKey: _navigatorKey,
      home: publicKey == null
          ? ConnectScreen(onConnect: _connect)
          : Notifiable(
              publicKey: publicKey,
              deepLinks: ExamplePage.values
                  .map((page) => DeepLink(path: page.path, label: page.title))
                  .toList(),
              onDeepLink: _open,
              child: Home(onDisconnect: _disconnect),
            ),
    );
  }
}

enum ExamplePage {
  orders('/orders', 'Orders'),
  inbox('/inbox', 'Inbox');

  const ExamplePage(this.path, this.title);

  final String path;
  final String title;

  static ExamplePage? matching(String path) {
    for (final page in values) {
      if (page.path == path || page.path == '/$path') {
        return page;
      }
    }

    return null;
  }
}

class ConnectScreen extends StatefulWidget {
  const ConnectScreen({super.key, required this.onConnect});

  final Future<void> Function(String publicKey) onConnect;

  @override
  State<ConnectScreen> createState() => _ConnectScreenState();
}

class _ConnectScreenState extends State<ConnectScreen> {
  final _controller = TextEditingController();

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: ListView(
          padding: const EdgeInsets.all(24),
          children: [
            const Text('Notifiable', style: TextStyle(fontSize: 28, fontWeight: FontWeight.w700)),
            const SizedBox(height: 8),
            const Text('Paste a public key from the dashboard. A development key shows the mark and preview toasts.'),
            const SizedBox(height: 24),
            TextField(
              controller: _controller,
              decoration: const InputDecoration(
                labelText: 'Public key',
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 16),
            FilledButton(
              onPressed: () {
                final key = _controller.text.trim();

                if (key.isEmpty) {
                  return;
                }

                widget.onConnect(key);
              },
              child: const Text('Connect'),
            ),
          ],
        ),
      ),
    );
  }
}

class Home extends StatelessWidget {
  const Home({super.key, required this.onDisconnect});

  final Future<void> Function() onDisconnect;

  @override
  Widget build(BuildContext context) {
    final notifiable = Notifiable.of(context);

    return Scaffold(
      body: SafeArea(
        child: ListenableBuilder(
          listenable: notifiable,
          builder: (context, _) {
            final current = notifiable.snapshot;
            return ListView(
              padding: const EdgeInsets.all(24),
              children: [
                Text(
                  current.ready ? 'Connected' : 'Connecting…',
                  style: const TextStyle(fontSize: 28, fontWeight: FontWeight.w700),
                ),
                const SizedBox(height: 8),
                Text(_status(current)),
                if (current.error != null) ...[
                  const SizedBox(height: 8),
                  Text(current.error!.message, style: const TextStyle(color: Colors.red)),
                ],
                const SizedBox(height: 24),
                FilledButton(
                  onPressed: () async {
                    try {
                      await notifiable.requestPermission();
                    } catch (_) {}
                  },
                  child: const Text('Enable notifications'),
                ),
                const SizedBox(height: 8),
                OutlinedButton(
                  onPressed: () async {
                    try {
                      await notifiable.identify(
                        email: 'ada@example.com',
                        externalId: 'user_123',
                      );
                    } catch (_) {}
                  },
                  child: const Text('Identify Ada'),
                ),
                const SizedBox(height: 8),
                OutlinedButton(
                  onPressed: () async {
                    try {
                      await notifiable.track(
                        'order.shipped',
                        properties: const {
                          'order': EventProperty.string('1001'),
                          'express': EventProperty.bool(true),
                        },
                      );
                    } catch (_) {}
                  },
                  child: const Text('Track order.shipped'),
                ),
                const SizedBox(height: 8),
                TextButton(
                  onPressed: () => Navigator.of(context).push(
                    MaterialPageRoute<void>(
                      builder: (_) => const ExampleScreen(page: ExamplePage.orders),
                    ),
                  ),
                  child: const Text('Orders'),
                ),
                TextButton(
                  onPressed: () => Navigator.of(context).push(
                    MaterialPageRoute<void>(
                      builder: (_) => const ExampleScreen(page: ExamplePage.inbox),
                    ),
                  ),
                  child: const Text('Inbox'),
                ),
                TextButton(onPressed: onDisconnect, child: const Text('Disconnect')),
              ],
            );
          },
        ),
      ),
    );
  }
}

class ExampleScreen extends StatelessWidget {
  const ExampleScreen({super.key, required this.page});

  final ExamplePage page;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(page.title)),
      body: Padding(
        padding: const EdgeInsets.all(24),
        child: Text('This screen is ${page.path}. A notification whose path is ${page.path} opens it.'),
      ),
    );
  }
}

String _status(NotifiableSnapshot snapshot) {
  final subscriber = snapshot.subscriber?.id ?? 'none';
  return 'Permission ${snapshot.permission.value}. Subscriber $subscriber.';
}

String _routePath(String url) {
  final parsed = Uri.tryParse(url);

  if (parsed == null) {
    return url;
  }

  if (parsed.path.isNotEmpty) {
    return parsed.path;
  }

  return url;
}
0
likes
140
points
0
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Flutter SDK for Notifiable. Registers a device, asks for notification permission, and identifies the person using the app.

Homepage
Repository (GitHub)
View/report issues

Topics

#notifications #push-notifications #flutter

License

MIT (license)

Dependencies

flutter, shared_preferences

More

Packages that depend on notifiable_flutter

Packages that implement notifiable_flutter