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

Flutter 隔空传送分享插件,支持 HarmonyOS/OpenHarmony。通过系统 ShareKit 注册隔空传送监听,用户触发手势时自动分享指定图片与 App Linking。

example/lib/main.dart

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

import 'package:flutter/services.dart';
import 'package:gesturesshare/gesturesshare.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        colorSchemeSeed: Colors.red,
        useMaterial3: true,
      ),
      home: const HomePage(),
    );
  }
}

/// 首页 - 选择测试场景
class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('隔空传送分享测试')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          _ScenarioCard(
            icon: Icons.card_giftcard,
            color: Colors.red,
            title: '分享福卡',
            subtitle: '模拟隔空传送赠送福卡场景(图片 + App Linking)',
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const ShareCardPage()),
            ),
          ),
          const SizedBox(height: 12),
          _ScenarioCard(
            icon: Icons.image,
            color: Colors.blue,
            title: '分享图片',
            subtitle: '仅分享一张本地图片(无链接)',
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const ShareImagePage()),
            ),
          ),
          const SizedBox(height: 12),
          _ScenarioCard(
            icon: Icons.tune,
            color: Colors.teal,
            title: '自定义参数',
            subtitle: '手动填写所有参数进行测试',
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const CustomTestPage()),
            ),
          ),
        ],
      ),
    );
  }
}

class _ScenarioCard extends StatelessWidget {
  final IconData icon;
  final Color color;
  final String title;
  final String subtitle;
  final VoidCallback onTap;

  const _ScenarioCard({
    required this.icon,
    required this.color,
    required this.title,
    required this.subtitle,
    required this.onTap,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      clipBehavior: Clip.antiAlias,
      child: InkWell(
        onTap: onTap,
        child: Padding(
          padding: const EdgeInsets.all(16),
          child: Row(
            children: [
              Container(
                width: 48,
                height: 48,
                decoration: BoxDecoration(
                  color: color.withOpacity(0.1),
                  borderRadius: BorderRadius.circular(12),
                ),
                child: Icon(icon, color: color, size: 28),
              ),
              const SizedBox(width: 16),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(title,
                        style: const TextStyle(
                            fontSize: 16, fontWeight: FontWeight.bold)),
                    const SizedBox(height: 4),
                    Text(subtitle,
                        style:
                            TextStyle(fontSize: 13, color: Colors.grey[600])),
                  ],
                ),
              ),
              Icon(Icons.chevron_right, color: Colors.grey[400]),
            ],
          ),
        ),
      ),
    );
  }
}

// ============================================================
// 场景一:分享福卡(模拟 uni-app 示例)
// ============================================================
class ShareCardPage extends StatefulWidget {
  const ShareCardPage({super.key});

  @override
  State<ShareCardPage> createState() => _ShareCardPageState();
}

class _ShareCardPageState extends State<ShareCardPage> {
  final _plugin = Gesturesshare();
  StreamSubscription<GesturesShareResult>? _sub;
  bool _isSharing = false;
  bool _isLoading = false;
  String _statusText = '点击按钮注册隔空传送分享';

  @override
  void initState() {
    super.initState();
    _sub = _plugin.onShareTriggered.listen((result) {
      if (!mounted) return;
      if (result.success) {
        _showSnackBar('分享成功!对方即将收到福卡', Colors.green);
      } else {
        _showSnackBar('分享失败: ${result.errMsg}', Colors.red);
      }
    });
  }

  @override
  void dispose() {
    _sub?.cancel();
    // 页面销毁时注销(与 uni-app onHide 行为一致)
    if (_isSharing) {
      _plugin.unregisterGesturesShare();
    }
    super.dispose();
  }

  void _showSnackBar(String msg, Color color) {
    if (!mounted) return;
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text(msg),
        backgroundColor: color,
        behavior: SnackBarBehavior.floating,
      ),
    );
  }

  /// 分享福卡 - 对应 uni-app 中的 shareCard()
  Future<void> _shareCard() async {
    if (_isLoading) return;
    setState(() => _isLoading = true);

    try {
      await _plugin.registerGesturesShare(
        RegisterGesturesShareOptions(
          imagePath: '/data/storage/el2/base/haps/entry/files/card.jpg',
          appLink: 'https://babyone.ljcljc.cn/api/festival/receive?uid=fc_xxx',
          title: '赠送福卡',
          description: '快来接收吧!',
        ),
      );
      setState(() {
        _isSharing = true;
        _statusText = '已注册,请使用隔空传送手势分享';
      });
      _showSnackBar('请使用隔空传送手势分享', Colors.blue);
    } on PlatformException catch (e) {
      setState(() => _statusText = '注册失败: ${e.message}');
      _showSnackBar('隔空传送功能不可用', Colors.red);
    } catch (e) {
      setState(() => _statusText = '注册失败: $e');
      _showSnackBar('隔空传送功能不可用', Colors.red);
    } finally {
      setState(() => _isLoading = false);
    }
  }

  /// 取消分享 - 对应 uni-app 中 onHide 的注销
  Future<void> _cancelShare() async {
    if (!_isSharing) return;
    setState(() => _isLoading = true);

    try {
      await _plugin.unregisterGesturesShare();
      setState(() {
        _isSharing = false;
        _statusText = '已取消分享';
      });
    } catch (e) {
      _showSnackBar('取消失败: $e', Colors.red);
    } finally {
      setState(() => _isLoading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('分享福卡')),
      body: Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          children: [
            // 福卡预览
            Card(
              color: Colors.red[50],
              child: Container(
                width: double.infinity,
                padding: const EdgeInsets.all(32),
                child: Column(
                  children: [
                    Container(
                      width: 120,
                      height: 120,
                      decoration: BoxDecoration(
                        color: Colors.red[100],
                        borderRadius: BorderRadius.circular(16),
                        border: Border.all(color: Colors.red[300]!, width: 2),
                      ),
                      child: Icon(Icons.card_giftcard,
                          size: 64, color: Colors.red[400]),
                    ),
                    const SizedBox(height: 16),
                    Text(
                      '赠送福卡',
                      style: TextStyle(
                        fontSize: 20,
                        fontWeight: FontWeight.bold,
                        color: Colors.red[800],
                      ),
                    ),
                    const SizedBox(height: 8),
                    Text(
                      '快来接收吧!',
                      style: TextStyle(fontSize: 14, color: Colors.red[600]),
                    ),
                    const SizedBox(height: 4),
                    Text(
                      'App Linking: babyone.ljcljc.cn/...',
                      style: TextStyle(fontSize: 11, color: Colors.grey[500]),
                    ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 24),
            // 状态
            Container(
              width: double.infinity,
              padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
              decoration: BoxDecoration(
                color: _isSharing ? Colors.green[50] : Colors.grey[100],
                borderRadius: BorderRadius.circular(8),
              ),
              child: Row(
                children: [
                  Icon(
                    _isSharing ? Icons.check_circle : Icons.info_outline,
                    size: 18,
                    color: _isSharing ? Colors.green : Colors.grey[600],
                  ),
                  const SizedBox(width: 8),
                  Expanded(
                    child: Text(
                      _statusText,
                      style: TextStyle(
                        fontSize: 13,
                        color: _isSharing ? Colors.green[800] : Colors.grey[700],
                      ),
                    ),
                  ),
                ],
              ),
            ),
            const SizedBox(height: 24),
            // 按钮
            SizedBox(
              width: double.infinity,
              height: 48,
              child: FilledButton.icon(
                onPressed: _isSharing || _isLoading ? null : _shareCard,
                icon: _isLoading && !_isSharing
                    ? const SizedBox(
                        width: 18,
                        height: 18,
                        child: CircularProgressIndicator(
                            strokeWidth: 2, color: Colors.white),
                      )
                    : const Icon(Icons.card_giftcard),
                label: const Text('隔空传送分享福卡'),
                style: FilledButton.styleFrom(
                  backgroundColor: Colors.red,
                ),
              ),
            ),
            const SizedBox(height: 12),
            SizedBox(
              width: double.infinity,
              height: 48,
              child: OutlinedButton.icon(
                onPressed: !_isSharing || _isLoading ? null : _cancelShare,
                icon: const Icon(Icons.close),
                label: const Text('取消分享'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// ============================================================
// 场景二:仅分享图片
// ============================================================
class ShareImagePage extends StatefulWidget {
  const ShareImagePage({super.key});

  @override
  State<ShareImagePage> createState() => _ShareImagePageState();
}

class _ShareImagePageState extends State<ShareImagePage> {
  final _plugin = Gesturesshare();
  StreamSubscription<GesturesShareResult>? _sub;
  bool _isSharing = false;
  bool _isLoading = false;
  String _statusText = '点击按钮注册隔空传送分享图片';

  @override
  void initState() {
    super.initState();
    _sub = _plugin.onShareTriggered.listen((result) {
      if (!mounted) return;
      if (result.success) {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(
            content: Text('图片分享成功!'),
            backgroundColor: Colors.green,
            behavior: SnackBarBehavior.floating,
          ),
        );
      } else {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('图片分享失败: ${result.errMsg}'),
            backgroundColor: Colors.red,
            behavior: SnackBarBehavior.floating,
          ),
        );
      }
    });
  }

  @override
  void dispose() {
    _sub?.cancel();
    if (_isSharing) {
      _plugin.unregisterGesturesShare();
    }
    super.dispose();
  }

  Future<void> _shareImage() async {
    if (_isLoading) return;
    setState(() => _isLoading = true);

    try {
      await _plugin.registerGesturesShare(
        RegisterGesturesShareOptions(
          imagePath: '/data/storage/el2/base/haps/entry/files/share_image.jpg',
        ),
      );
      setState(() {
        _isSharing = true;
        _statusText = '已注册,请使用隔空传送手势分享图片';
      });
    } on PlatformException catch (e) {
      setState(() => _statusText = '注册失败: ${e.message}');
    } catch (e) {
      setState(() => _statusText = '注册失败: $e');
    } finally {
      setState(() => _isLoading = false);
    }
  }

  Future<void> _cancelShare() async {
    if (!_isSharing) return;
    setState(() => _isLoading = true);

    try {
      await _plugin.unregisterGesturesShare();
      setState(() {
        _isSharing = false;
        _statusText = '已取消分享';
      });
    } catch (e) {
      debugPrint('取消失败: $e');
    } finally {
      setState(() => _isLoading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('分享图片')),
      body: Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          children: [
            // 图片预览
            Card(
              color: Colors.blue[50],
              child: Container(
                width: double.infinity,
                height: 200,
                padding: const EdgeInsets.all(24),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Icon(Icons.image, size: 64, color: Colors.blue[300]),
                    const SizedBox(height: 12),
                    Text(
                      'share_image.jpg',
                      style: TextStyle(fontSize: 14, color: Colors.blue[700]),
                    ),
                    const SizedBox(height: 4),
                    Text(
                      '仅分享图片,不含链接',
                      style: TextStyle(fontSize: 12, color: Colors.grey[500]),
                    ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 24),
            // 状态
            Container(
              width: double.infinity,
              padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
              decoration: BoxDecoration(
                color: _isSharing ? Colors.green[50] : Colors.grey[100],
                borderRadius: BorderRadius.circular(8),
              ),
              child: Row(
                children: [
                  Icon(
                    _isSharing ? Icons.check_circle : Icons.info_outline,
                    size: 18,
                    color: _isSharing ? Colors.green : Colors.grey[600],
                  ),
                  const SizedBox(width: 8),
                  Expanded(
                    child: Text(
                      _statusText,
                      style: TextStyle(
                        fontSize: 13,
                        color: _isSharing ? Colors.green[800] : Colors.grey[700],
                      ),
                    ),
                  ),
                ],
              ),
            ),
            const SizedBox(height: 24),
            // 按钮
            SizedBox(
              width: double.infinity,
              height: 48,
              child: FilledButton.icon(
                onPressed: _isSharing || _isLoading ? null : _shareImage,
                icon: _isLoading && !_isSharing
                    ? const SizedBox(
                        width: 18,
                        height: 18,
                        child: CircularProgressIndicator(
                            strokeWidth: 2, color: Colors.white),
                      )
                    : const Icon(Icons.image),
                label: const Text('隔空传送分享图片'),
              ),
            ),
            const SizedBox(height: 12),
            SizedBox(
              width: double.infinity,
              height: 48,
              child: OutlinedButton.icon(
                onPressed: !_isSharing || _isLoading ? null : _cancelShare,
                icon: const Icon(Icons.close),
                label: const Text('取消分享'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// ============================================================
// 场景三:自定义参数测试
// ============================================================
class CustomTestPage extends StatefulWidget {
  const CustomTestPage({super.key});

  @override
  State<CustomTestPage> createState() => _CustomTestPageState();
}

class _CustomTestPageState extends State<CustomTestPage> {
  String _platformVersion = 'Unknown';
  bool _isRegistered = false;
  bool _isLoading = false;
  final List<String> _logs = [];
  final _plugin = Gesturesshare();
  StreamSubscription<GesturesShareResult>? _sub;

  final _imagePathController = TextEditingController(
    text: '/data/storage/el2/base/haps/entry/files/share_image.jpg',
  );
  final _appLinkController = TextEditingController();
  final _titleController = TextEditingController(text: '隔空传送分享');
  final _descriptionController =
      TextEditingController(text: '来自 Flutter 的分享');

  @override
  void initState() {
    super.initState();
    _initPlatformState();
    _sub = _plugin.onShareTriggered.listen((result) {
      if (result.success) {
        _addLog('[分享触发] 成功: ${result.errMsg}');
      } else {
        _addLog('[分享触发] 失败: ${result.errCode} - ${result.errMsg}');
      }
    });
  }

  @override
  void dispose() {
    _sub?.cancel();
    if (_isRegistered) {
      _plugin.unregisterGesturesShare();
    }
    _imagePathController.dispose();
    _appLinkController.dispose();
    _titleController.dispose();
    _descriptionController.dispose();
    super.dispose();
  }

  Future<void> _initPlatformState() async {
    String version;
    try {
      version = await _plugin.getPlatformVersion() ?? 'Unknown';
    } on PlatformException {
      version = 'Failed to get platform version.';
    }
    if (!mounted) return;
    setState(() => _platformVersion = version);
    _addLog('平台版本: $_platformVersion');
  }

  Future<void> _register() async {
    if (_isLoading) return;

    final imagePath = _imagePathController.text.trim();
    if (imagePath.isEmpty) {
      _addLog('[错误] 图片路径不能为空');
      return;
    }

    setState(() => _isLoading = true);
    _addLog('正在注册隔空传送分享...');

    try {
      await _plugin.registerGesturesShare(
        RegisterGesturesShareOptions(
          imagePath: imagePath,
          appLink: _appLinkController.text.trim().isNotEmpty
              ? _appLinkController.text.trim()
              : null,
          title: _titleController.text.trim().isNotEmpty
              ? _titleController.text.trim()
              : null,
          description: _descriptionController.text.trim().isNotEmpty
              ? _descriptionController.text.trim()
              : null,
        ),
      );
      _addLog('[注册成功] 隔空传送分享已注册');
      setState(() => _isRegistered = true);
    } on PlatformException catch (e) {
      _addLog('[注册失败] ${e.code}: ${e.message}');
    } catch (e) {
      _addLog('[注册失败] $e');
    } finally {
      setState(() => _isLoading = false);
    }
  }

  Future<void> _unregister() async {
    if (_isLoading) return;

    setState(() => _isLoading = true);
    _addLog('正在注销隔空传送分享...');

    try {
      await _plugin.unregisterGesturesShare();
      _addLog('[注销成功] 隔空传送分享已注销');
      setState(() => _isRegistered = false);
    } on PlatformException catch (e) {
      _addLog('[注销失败] ${e.code}: ${e.message}');
    } catch (e) {
      _addLog('[注销失败] $e');
    } finally {
      setState(() => _isLoading = false);
    }
  }

  void _addLog(String message) {
    final time = TimeOfDay.now();
    final timeStr =
        '${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}';
    if (!mounted) return;
    setState(() {
      _logs.insert(0, '[$timeStr] $message');
      if (_logs.length > 50) _logs.removeLast();
    });
  }

  void _clearLogs() => setState(() => _logs.clear());

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('自定义参数'),
        actions: [
          IconButton(
            icon: const Icon(Icons.delete_outline),
            tooltip: '清空日志',
            onPressed: _clearLogs,
          ),
        ],
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // 状态卡片
            _buildStatusCard(),
            const SizedBox(height: 16),
            // 参数设置
            _buildSettingsCard(),
            const SizedBox(height: 16),
            // 操作按钮
            _buildActionButtons(),
            const SizedBox(height: 16),
            // 日志区域
            _buildLogCard(),
          ],
        ),
      ),
    );
  }

  Widget _buildStatusCard() {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Row(
          children: [
            Icon(
              _isRegistered ? Icons.check_circle : Icons.cancel,
              color: _isRegistered ? Colors.green : Colors.grey,
              size: 32,
            ),
            const SizedBox(width: 12),
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    _isRegistered ? '已注册' : '未注册',
                    style: const TextStyle(
                        fontSize: 18, fontWeight: FontWeight.bold),
                  ),
                  Text(
                    '平台: $_platformVersion',
                    style: TextStyle(fontSize: 13, color: Colors.grey[600]),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildSettingsCard() {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const Text('分享参数',
                style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
            const SizedBox(height: 12),
            TextField(
              controller: _imagePathController,
              decoration: const InputDecoration(
                labelText: '图片路径 *',
                hintText: '本地图片文件路径',
                border: OutlineInputBorder(),
                isDense: true,
              ),
              enabled: !_isRegistered,
            ),
            const SizedBox(height: 12),
            TextField(
              controller: _appLinkController,
              decoration: const InputDecoration(
                labelText: 'App Linking(可选)',
                hintText: 'https://example.com/share',
                border: OutlineInputBorder(),
                isDense: true,
              ),
              enabled: !_isRegistered,
            ),
            const SizedBox(height: 12),
            Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _titleController,
                    decoration: const InputDecoration(
                      labelText: '标题(可选)',
                      border: OutlineInputBorder(),
                      isDense: true,
                    ),
                    enabled: !_isRegistered,
                  ),
                ),
                const SizedBox(width: 12),
                Expanded(
                  child: TextField(
                    controller: _descriptionController,
                    decoration: const InputDecoration(
                      labelText: '描述(可选)',
                      border: OutlineInputBorder(),
                      isDense: true,
                    ),
                    enabled: !_isRegistered,
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildActionButtons() {
    return Row(
      children: [
        Expanded(
          child: FilledButton.icon(
            onPressed: _isRegistered || _isLoading ? null : _register,
            icon: _isLoading && !_isRegistered
                ? const SizedBox(
                    width: 18,
                    height: 18,
                    child: CircularProgressIndicator(
                        strokeWidth: 2, color: Colors.white),
                  )
                : const Icon(Icons.share),
            label: const Text('注册分享'),
          ),
        ),
        const SizedBox(width: 12),
        Expanded(
          child: OutlinedButton.icon(
            onPressed: !_isRegistered || _isLoading ? null : _unregister,
            icon: _isLoading && _isRegistered
                ? const SizedBox(
                    width: 18,
                    height: 18,
                    child: CircularProgressIndicator(strokeWidth: 2),
                  )
                : const Icon(Icons.close),
            label: const Text('注销分享'),
          ),
        ),
      ],
    );
  }

  Widget _buildLogCard() {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                const Icon(Icons.article_outlined, size: 20),
                const SizedBox(width: 8),
                const Text('运行日志',
                    style:
                        TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
                const Spacer(),
                Text('${_logs.length} 条',
                    style: TextStyle(fontSize: 12, color: Colors.grey[500])),
              ],
            ),
            const Divider(),
            if (_logs.isEmpty)
              const Padding(
                padding: EdgeInsets.symmetric(vertical: 24),
                child: Center(
                  child: Text('暂无日志',
                      style: TextStyle(color: Colors.grey)),
                ),
              )
            else
              ListView.builder(
                shrinkWrap: true,
                physics: const NeverScrollableScrollPhysics(),
                itemCount: _logs.length,
                itemBuilder: (context, index) {
                  final log = _logs[index];
                  Color textColor = Colors.black87;
                  if (log.contains('[成功]') ||
                      log.contains('[注册成功]') ||
                      log.contains('[注销成功]')) {
                    textColor = Colors.green[700]!;
                  } else if (log.contains('[失败]') ||
                      log.contains('[错误]') ||
                      log.contains('[注册失败]') ||
                      log.contains('[注销失败]')) {
                    textColor = Colors.red[700]!;
                  } else if (log.contains('[分享触发]')) {
                    textColor = Colors.blue[700]!;
                  }
                  return Padding(
                    padding: const EdgeInsets.symmetric(vertical: 2),
                    child: Text(
                      log,
                      style: TextStyle(
                        fontSize: 12,
                        fontFamily: 'monospace',
                        color: textColor,
                      ),
                    ),
                  );
                },
              ),
          ],
        ),
      ),
    );
  }
}
0
likes
120
points
64
downloads

Publisher

unverified uploader

Weekly Downloads

Flutter 隔空传送分享插件,支持 HarmonyOS/OpenHarmony。通过系统 ShareKit 注册隔空传送监听,用户触发手势时自动分享指定图片与 App Linking。

Repository
View/report issues

Documentation

Documentation
API reference

License

Apache-2.0 (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on gesturesshare

Packages that implement gesturesshare