Skip to main content

protocol_handler 0.3.0 copy "protocol_handler: ^0.3.0" to clipboard
protocol_handler: ^0.3.0 copied to clipboard

This plugin allows Flutter apps to register and handle custom protocols (i.e. deep linking).

example/lib/main.dart

import 'dart:io' show Platform;

import 'package:material_ui/material_ui.dart';
import 'package:flutter/services.dart';
import 'package:protocol_handler/protocol_handler.dart';

/// The scheme the example declares in its Info.plist files and Android
/// manifest, and registers on Windows.
const kScheme = 'myprotocol';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // Writes the registration on Windows; elsewhere it reports whether the app
  // package declares the scheme.
  await protocolHandler.register(kScheme);
  runApp(const ExampleApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'protocol_handler example',
      theme: ThemeData(colorSchemeSeed: const Color(0xff416ff4)),
      home: const HomePage(),
    );
  }
}

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

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

class _HomePageState extends State<HomePage> with ProtocolListener {
  bool? _isRegistered;
  String? _initialUrl;
  final List<String> _receivedUrls = [];

  @override
  void initState() {
    super.initState();
    protocolHandler.addListener(this);
    _init();
  }

  @override
  void dispose() {
    // Listener cleanup: a removed listener is never called again.
    protocolHandler.removeListener(this);
    super.dispose();
  }

  Future<void> _init() async {
    final isRegistered = await protocolHandler.register(kScheme);
    final initialUrl = await protocolHandler.getInitialUrl();
    if (!mounted) return;
    setState(() {
      _isRegistered = isRegistered;
      _initialUrl = initialUrl;
    });
  }

  @override
  void onProtocolUrlReceived(String url) {
    // A launch URL the system delivered after _init read it arrives here too.
    protocolHandler.getInitialUrl().then((initialUrl) {
      if (mounted) setState(() => _initialUrl = initialUrl);
    });
    setState(() => _receivedUrls.insert(0, url));
    ScaffoldMessenger.of(context)
        .showSnackBar(SnackBar(content: Text('Url received: $url')));
  }

  void _setListening(bool isListening) {
    if (isListening) {
      // URLs that arrived while no listener was registered are delivered now.
      protocolHandler.addListener(this);
    } else {
      protocolHandler.removeListener(this);
    }
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('protocol_handler example')),
      body: ListView(
        children: [
          ListTile(
            title: const Text('register'),
            subtitle: Text('$kScheme → ${_isRegistered ?? '…'}'),
          ),
          ListTile(
            title: const Text('getInitialUrl'),
            subtitle: Text(_initialUrl ?? '(none)'),
            onTap: _init,
          ),
          SwitchListTile(
            title: const Text('Listening'),
            subtitle: Text(
              protocolHandler.hasListeners
                  ? '${protocolHandler.listeners.length} listener(s)'
                  : 'URLs are queued until a listener is added',
            ),
            value: protocolHandler.listeners.contains(this),
            onChanged: _setListening,
          ),
          const _SectionTitle('Test commands (tap to copy)'),
          for (final command in _testCommands())
            ListTile(
              dense: true,
              title: Text(
                command,
                style: const TextStyle(fontFamily: 'monospace'),
              ),
              onTap: () {
                Clipboard.setData(ClipboardData(text: command));
                ScaffoldMessenger.of(context).showSnackBar(
                  const SnackBar(content: Text('Copied to clipboard')),
                );
              },
            ),
          const _SectionTitle('Received urls'),
          for (final url in _receivedUrls) ListTile(title: Text(url)),
        ],
      ),
    );
  }
}

class _SectionTitle extends StatelessWidget {
  const _SectionTitle(this.text);

  final String text;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.fromLTRB(16, 24, 16, 8),
      child: Text(text, style: Theme.of(context).textTheme.titleSmall),
    );
  }
}

/// Commands that open a URL of [kScheme] on the current platform.
List<String> _testCommands() {
  var prefix = '';
  var suffix = '';
  if (Platform.isIOS) {
    prefix = 'xcrun simctl openurl booted';
  } else if (Platform.isAndroid) {
    prefix =
        "adb shell 'am start -a android.intent.action.VIEW"
        ' -c android.intent.category.BROWSABLE -d';
    suffix = "'";
  } else if (Platform.isMacOS) {
    prefix = 'open';
  } else if (Platform.isWindows) {
    prefix = 'start ""';
  } else {
    return const [];
  }
  const urls = [
    '$kScheme://host/path/subpath',
    '$kScheme://example.com/path/portion/?uid=123&token=abc',
    '$kScheme://example.com/?addr=1%20Nowhere%20Rd&emoji=%F0%9F%98%82',
  ];
  return [for (final url in urls) '$prefix "$url"$suffix'];
}
69
likes
160
points
3.84k
downloads

Documentation

API reference

Publisher

verified publisherleanflutter.dev

Weekly Downloads

This plugin allows Flutter apps to register and handle custom protocols (i.e. deep linking).

Repository (GitHub)
View/report issues

Topics

#deep-linking #url-scheme #protocol-handler

License

MIT (license)

Dependencies

flutter, nativeapi

More

Packages that depend on protocol_handler

Packages that implement protocol_handler