smart_links 1.0.0 copy "smart_links: ^1.0.0" to clipboard
smart_links: ^1.0.0 copied to clipboard

PlatformAndroid

Production-grade deep linking for Android and Flutter Web with deferred install referrer support.

example/lib/main.dart

import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:smart_links/smart_links.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Smart Links Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF1565C0)),
        useMaterial3: true,
      ),
      home: const HomePage(),
    );
  }
}

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

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

class _HomePageState extends State<HomePage> {
  final _log = <String>[];
  SmartLinkData? _initial;
  SmartLinkData? _deferred;
  bool _ready = false;
  StreamSubscription<SmartLinkData>? _sub;

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

  Future<void> _bootstrap() async {
    await SmartLink.initialize(enableLogs: kDebugMode);
    _initial = await SmartLink.initialLink;
    _deferred = await SmartLink.deferredLink;

    _sub = SmartLink.stream.listen((link) {
      _add('Stream: ${link.path} (${link.source.name})');
      setState(() {});
    });

    if (_initial != null) {
      _add('Initial: ${_initial!.path}');
    }
    if (_deferred != null) {
      _add('Deferred: ${_deferred!.path}');
    }

    setState(() {});
  }

  void _add(String line) {
    setState(() {
      _log.insert(0, '${DateTime.now().toIso8601String().substring(11, 19)} $line');
    });
  }

  void _markReady() {
    if (_ready) return;
    SmartLink.markReady();
    _ready = true;
    _add('markReady() — queued links replayed');
    setState(() {});
  }

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

  @override
  Widget build(BuildContext context) {
    final sampleReferrer = SmartLinkParser.encodeReferrerPayload(
      path: '/product/42',
      params: {'campaign': 'summer'},
    );

    return Scaffold(
      appBar: AppBar(
        title: const Text('Smart Links'),
        actions: [
          IconButton(
            tooltip: 'Mark app ready',
            onPressed: _ready ? null : _markReady,
            icon: Icon(_ready ? Icons.check_circle : Icons.play_circle_outline),
          ),
        ],
      ),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          _StatusCard(
            ready: _ready,
            initial: _initial,
            deferred: _deferred,
          ),
          const SizedBox(height: 12),
          _InfoCard(
            title: 'Try these links',
            children: [
              if (kIsWeb) ...[
                const Text('Change the browser URL path or hash, e.g.'),
                SelectableText(Uri.base.replace(path: '/product/99').toString()),
              ] else ...[
                const Text('Custom scheme (adb):'),
                SelectableText(
                  'adb shell am start -a android.intent.action.VIEW '
                  '-d "smartlinkdemo://product/42?ref=demo"',
                ),
                const SizedBox(height: 8),
                const Text('HTTPS App Link (after domain verification):'),
                const SelectableText(
                  'https://smartlinkdemo.example.com/product/42?campaign=summer',
                ),
              ],
            ],
          ),
          const SizedBox(height: 12),
          _InfoCard(
            title: 'Deferred referrer payload (Play Store)',
            children: [
              const Text(
                'Pass as Play Store referrer (utm_content or payload param):',
              ),
              const SizedBox(height: 8),
              SelectableText(sampleReferrer),
              const SizedBox(height: 8),
              SelectableText(
                'https://play.google.com/store/apps/details?id=com.example.example'
                '&referrer=${Uri.encodeComponent('utm_content=$sampleReferrer')}',
              ),
            ],
          ),
          const SizedBox(height: 12),
          Text('Event log', style: Theme.of(context).textTheme.titleMedium),
          const SizedBox(height: 8),
          if (_log.isEmpty)
            const Text('No events yet. Open a deep link or tap Mark Ready.')
          else
            ..._log.map((e) => Padding(
                  padding: const EdgeInsets.only(bottom: 6),
                  child: Text(e, style: const TextStyle(fontFamily: 'monospace')),
                )),
        ],
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: _ready ? null : _markReady,
        icon: const Icon(Icons.rocket_launch),
        label: Text(_ready ? 'App ready' : 'Mark ready'),
      ),
    );
  }
}

class _StatusCard extends StatelessWidget {
  const _StatusCard({
    required this.ready,
    required this.initial,
    required this.deferred,
  });

  final bool ready;
  final SmartLinkData? initial;
  final SmartLinkData? deferred;

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Icon(
                  ready ? Icons.check_circle : Icons.hourglass_top,
                  color: ready ? Colors.green : Colors.orange,
                ),
                const SizedBox(width: 8),
                Text(
                  ready ? 'Router ready' : 'Queueing links until markReady()',
                  style: Theme.of(context).textTheme.titleMedium,
                ),
              ],
            ),
            const Divider(),
            _row('Initial link', initial?.path ?? '—'),
            _row('Deferred link', deferred?.path ?? '—'),
            if (initial != null)
              _row('Initial params', initial!.queryParams.toString()),
          ],
        ),
      ),
    );
  }

  Widget _row(String label, String value) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 4),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          SizedBox(width: 110, child: Text(label, style: const TextStyle(fontWeight: FontWeight.w600))),
          Expanded(child: Text(value)),
        ],
      ),
    );
  }
}

class _InfoCard extends StatelessWidget {
  const _InfoCard({required this.title, required this.children});

  final String title;
  final List<Widget> children;

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(title, style: Theme.of(context).textTheme.titleMedium),
            const SizedBox(height: 8),
            ...children,
          ],
        ),
      ),
    );
  }
}
0
likes
160
points
57
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

Production-grade deep linking for Android and Flutter Web with deferred install referrer support.

Repository (GitHub)
View/report issues

Topics

#deeplink #deep-linking #app-links #flutter-web #install-referrer

License

MIT (license)

Dependencies

flutter, flutter_web_plugins

More

Packages that depend on smart_links

Packages that implement smart_links