Skip to main content

zuraffa_intents 7.3.0 copy "zuraffa_intents: ^7.3.0" to clipboard
zuraffa_intents: ^7.3.0 copied to clipboard

The incoming-share seam for the Zuraffa ecosystem: receive shared text/media (boot-time plus live stream) and record sent-message share suggestions.

example/lib/main.dart

// The zuraffa_intents example: a fully working share-intents app for
// android, ios and macos.
//
// It exercises every operation the plugin exposes over the REAL platform
// driver (ShareIntentService.platform):
//
//   - getInitialSharedMedia  — the boot-time share, read repeatably until
//     reset (the Reset card action demonstrates resetInitialSharedMedia).
//   - sharedMediaStream      — the live broadcast stream, every event
//     appended to the feed below.
//   - recordSentMessage      — the form at the bottom carries ALL four
//     arguments (conversationIdentifier, conversationName,
//     conversationImageFilePath, serviceName) and maps them onto the
//     carrier exactly like the plugin does.
//   - PlatformException      — surfaced verbatim in the error banner
//     (e.g. `channel-error` when a native half is missing).
//
// The "in-memory demo" switch swaps the platform driver for the pure-Dart
// InMemoryShareIntentAdapter, whose producer side (storeInitial / emit)
// powers the Simulate actions — so the full receive flow can be tried
// without an OS share sheet.
library;

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get_it/get_it.dart';
import 'package:zuraffa_intents/zuraffa_intents.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // The plugin's composition root: binds ShareIntentPort to the
  // method-channel driver and ShareIntentService to that port (lazy
  // singletons). Swap-in for tests: GetIt.reset() + register again.
  registerShareIntentDependencies(GetIt.instance);
  final service = GetIt.instance<ShareIntentService>();
  runApp(ShareIntentsApp(initialService: service));
}

class ShareIntentsApp extends StatelessWidget {
  const ShareIntentsApp({super.key, this.initialService});

  /// Injectable for tests: omit it to use the GetIt-registered platform
  /// service (see [main]).
  final ShareIntentService? initialService;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'zuraffa_intents example',
      theme: ThemeData(colorSchemeSeed: const Color(0xFF7B61FF), useMaterial3: true),
      home: ShareIntentsPage(service: initialService),
    );
  }
}

class ShareIntentsPage extends StatefulWidget {
  const ShareIntentsPage({super.key, this.service});

  final ShareIntentService? service;

  @override
  State<ShareIntentsPage> createState() => _ShareIntentsPageState();
}

class _ShareIntentsPageState extends State<ShareIntentsPage> {
  late ShareIntentService _service;
  StreamSubscription<SharedMedia>? _subscription;
  final _events = <SharedMedia>[];
  SharedMedia? _initialShare;
  String? _error;
  bool _demoMode = false;

  // recordSentMessage form — all four arguments the plugin carries.
  final _conversationId = TextEditingController(text: 'conv-9');
  final _conversationName = TextEditingController(text: 'Mom');
  final _conversationImage = TextEditingController();
  final _serviceName = TextEditingController();

  @override
  void initState() {
    super.initState();
    _bindService(widget.service ?? GetIt.instance<ShareIntentService>());
    _readInitialShare();
  }

  void _bindService(ShareIntentService service) {
    _subscription?.cancel();
    _service = service;
    _demoMode = service.port is InMemoryShareIntentAdapter;
    _subscription = service.sharedMediaStream.listen(
      (media) => setState(() => _events.insert(0, media)),
      onError: (Object error) => setState(() => _error = error.toString()),
    );
  }

  Future<void> _readInitialShare() async {
    try {
      final media = await _service.getInitialSharedMedia();
      setState(() {
        _initialShare = media;
        _error = null;
      });
    } on PlatformException catch (error) {
      setState(() => _error = 'code=${error.code} message=${error.message}');
    }
  }

  Future<void> _resetInitialShare() async {
    try {
      await _service.resetInitialSharedMedia();
      await _readInitialShare();
    } on PlatformException catch (error) {
      setState(() => _error = 'code=${error.code} message=${error.message}');
    }
  }

  Future<void> _recordSentMessage() async {
    try {
      await _service.recordSentMessage(
        conversationIdentifier: _conversationId.text.trim(),
        conversationName: _conversationName.text.trim(),
        conversationImageFilePath:
            _conversationImage.text.trim().isEmpty
                ? null
                : _conversationImage.text.trim(),
        serviceName: _serviceName.text.trim().isEmpty
            ? null
            : _serviceName.text.trim(),
      );
      setState(() => _error = null);
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('suggestion recorded')),
      );
    } on PlatformException catch (error) {
      setState(() => _error = 'code=${error.code} message=${error.message}');
    }
  }

  // ---- demo-mode producers (the in-memory adapter's test side) ----

  void _simulateInitialShare() {
    final port = _service.port as InMemoryShareIntentAdapter;
    port.storeInitial(
      SharedMedia(
        content: 'Check this out',
        attachments: [
          SharedAttachment(path: '/tmp/pic.jpg', type: SharedAttachmentType.image),
        ],
      ),
    );
    _readInitialShare();
  }

  void _simulateLiveShare() {
    (_service.port as InMemoryShareIntentAdapter).emit(
      SharedMedia(content: 'hello from the share sheet'),
    );
  }

  @override
  void dispose() {
    _subscription?.cancel();
    _conversationId.dispose();
    _conversationName.dispose();
    _conversationImage.dispose();
    _serviceName.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('zuraffa_intents'),
        actions: [
          Switch(
            value: _demoMode,
            onChanged: (demo) => setState(() {
              _bindService(
                demo ? ShareIntentService() : GetIt.instance<ShareIntentService>(),
              );
              _events.clear();
              _readInitialShare();
            }),
          ),
          const Padding(
            padding: EdgeInsets.only(right: 12),
            child: Center(child: Text('in-memory demo')),
          ),
        ],
      ),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          if (_error != null)
            Card(
              color: Theme.of(context).colorScheme.errorContainer,
              child: Padding(
                padding: const EdgeInsets.all(12),
                child: Text(_error!, key: const Key('error-banner')),
              ),
            ),
          Card(
            child: ListTile(
              key: const Key('initial-share'),
              leading: const Icon(Icons.launch),
              title: const Text('Boot-time initial share'),
              subtitle: Text(_initialShare == null
                  ? 'nothing stored'
                  : '${_initialShare!.content ?? '(no content)'}'
                    ' — ${_describe(_initialShare!)}'),
              trailing: Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  IconButton(
                    key: const Key('refresh-initial'),
                    tooltip: 'getInitialSharedMedia',
                    onPressed: _readInitialShare,
                    icon: const Icon(Icons.refresh),
                  ),
                  IconButton(
                    key: const Key('reset-initial'),
                    tooltip: 'resetInitialSharedMedia',
                    onPressed: _resetInitialShare,
                    icon: const Icon(Icons.delete_sweep),
                  ),
                ],
              ),
            ),
          ),
          if (_demoMode)
            Wrap(
              spacing: 8,
              children: [
                OutlinedButton(
                  key: const Key('simulate-initial'),
                  onPressed: _simulateInitialShare,
                  child: const Text('Simulate boot share'),
                ),
                OutlinedButton(
                  key: const Key('simulate-live'),
                  onPressed: _simulateLiveShare,
                  child: const Text('Simulate live share'),
                ),
              ],
            ),
          Card(
            child: Padding(
              padding: const EdgeInsets.all(12),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text('Live share stream (${_events.length})',
                      style: Theme.of(context).textTheme.titleMedium),
                  const SizedBox(height: 8),
                  if (_events.isEmpty)
                    const Text('no live shares yet — share something into the app'),
                  for (final media in _events)
                    ListTile(
                      key: Key('event-${_events.indexOf(media)}'),
                      dense: true,
                      leading: const Icon(Icons.share),
                      title: Text(media.content ?? '(no content)'),
                      subtitle: Text(_describe(media)),
                    ),
                ],
              ),
            ),
          ),
          Card(
            child: Padding(
              padding: const EdgeInsets.all(12),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text('Record sent message',
                      style: Theme.of(context).textTheme.titleMedium),
                  const SizedBox(height: 8),
                  TextField(
                    key: const Key('field-conversation-id'),
                    controller: _conversationId,
                    decoration: const InputDecoration(
                        labelText: 'conversationIdentifier (required)'),
                  ),
                  TextField(
                    key: const Key('field-conversation-name'),
                    controller: _conversationName,
                    decoration: const InputDecoration(
                        labelText: 'conversationName (required) → speakableGroupName'),
                  ),
                  TextField(
                    key: const Key('field-conversation-image'),
                    controller: _conversationImage,
                    decoration: const InputDecoration(
                        labelText:
                            'conversationImageFilePath (optional) → imageFilePath'),
                  ),
                  TextField(
                    key: const Key('field-service-name'),
                    controller: _serviceName,
                    decoration: const InputDecoration(
                        labelText: 'serviceName (optional)'),
                  ),
                  const SizedBox(height: 8),
                  FilledButton(
                    key: const Key('record-button'),
                    onPressed: _recordSentMessage,
                    child: const Text('recordSentMessage'),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }

  String _describe(SharedMedia media) {
    final parts = <String>[
      if (media.conversationIdentifier != null)
        'conversation=${media.conversationIdentifier}',
      if (media.speakableGroupName != null) 'group=${media.speakableGroupName}',
      if (media.serviceName != null) 'service=${media.serviceName}',
      if (media.attachments != null)
        '${media.attachments!.length} attachment(s)',
    ];
    return parts.isEmpty ? 'no extra fields' : parts.join(' · ');
  }
}
0
likes
160
points
281
downloads

Documentation

API reference

Publisher

verified publisherzuraffa.com

Weekly Downloads

The incoming-share seam for the Zuraffa ecosystem: receive shared text/media (boot-time plus live stream) and record sent-message share suggestions.

Homepage
Repository (GitHub)
View/report issues

Topics

#share #share-intents #deep-links #plugins

License

MIT (license)

Dependencies

flutter, get_it, json_annotation, zorphy_annotation, zuraffa

More

Packages that depend on zuraffa_intents

Packages that implement zuraffa_intents