import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:new_learn/core/services/share_service.dart'; import 'package:new_learn/core/services/picker_service.dart'; /// ============================================================ /// 【ShareDemoPage】分享功能演示页 /// /// 一、演示内容: /// 1. 分享纯文本 / 链接 /// 2. 选图后分享(单张图片) /// 3. Widget 截图分享(模拟“攻克海报”场景) /// 4. Widget 截图预览(不分享,只截图看效果) /// 5. 保存到相册(选图 / 截图 → 写入系统相册) /// /// 二、核心能力对应: /// ShareService.shareText → 场景 1 /// ShareService.shareImage → 场景 2(先 PickerService 选图) /// ShareService.shareFromWidget → 场景 3 /// ShareService.captureWidget → 场景 4 /// ShareService.saveToGallery → 场景 5a /// ShareService.saveToGalleryFromBytes → 场景 5b /// /// 三、使用方式: /// 从任务页 Demo 列表进入,或直接: /// context.push('/share-demo'); (需先在 app_router.dart 注册) /// ============================================================ class ShareDemoPage extends StatefulWidget { const ShareDemoPage({super.key}); @override State createState() => _ShareDemoPageState(); } class _ShareDemoPageState extends State { final TextEditingController _textController = TextEditingController( text: '来看看这个超棒的学习 App!https://example.com', ); bool _sharing = false; String? _lastResult; @override void dispose() { _textController.dispose(); super.dispose(); } /// 统一结果展示 void _showResult(String msg) { setState(() => _lastResult = msg); } // ==================== 场景 1:分享文本 ==================== Future _shareText() async { final text = _textController.text.trim(); if (text.isEmpty) { _showResult('请输入要分享的内容'); return; } setState(() => _sharing = true); try { final result = await ShareService.instance.shareText(text: text); _showResult('文本分享结果:${result.status.name}'); } catch (e) { _showResult('分享失败:$e'); } finally { if (mounted) setState(() => _sharing = false); } } // ==================== 场景 2:选图 → 分享 ==================== Future _pickAndShare() async { setState(() => _sharing = true); try { final picked = await PickerService.instance.showPickSheet( context, maxCount: 1, ); if (picked == null || picked.isEmpty) { _showResult('未选择图片'); return; } final filePath = picked.first.path; if (filePath == null) { _showResult('Web 端暂不支持文件路径分享'); return; } final result = await ShareService.instance.shareImage( filePath: filePath, text: '看看这张图片!', ); _showResult('图片分享结果:${result.status.name}'); } catch (e) { _showResult('分享失败:$e'); } finally { if (mounted) setState(() => _sharing = false); } } // ==================== 场景 3:Widget 截图 → 分享(海报) ==================== Future _sharePoster() async { setState(() => _sharing = true); try { final result = await ShareService.instance.shareFromWidget( context: context, text: '我攻克了一道难题,快来挑战!', builder: (_) => _buildPosterWidget( userName: '张三同学', taskName: '二次函数综合练习', score: 98, ), ); _showResult('海报分享结果:${result.status.name}'); } catch (e) { _showResult('分享失败:$e'); } finally { if (mounted) setState(() => _sharing = false); } } // ==================== 场景 4:Widget 截图预览(不分享) ==================== Future _previewPoster() async { setState(() => _sharing = true); try { final bytes = await ShareService.captureWidget( context: context, builder: (_) => _buildPosterWidget( userName: '张三同学', taskName: '二次函数综合练习', score: 98, ), ); if (bytes == null) { _showResult('截图失败'); return; } // 弹窗预览截图结果 if (!mounted) return; showDialog( context: context, builder: (ctx) => Dialog( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Padding( padding: EdgeInsets.all(12), child: Text('截图预览(海报效果)', style: TextStyle(fontWeight: FontWeight.bold)), ), ClipRRect( borderRadius: BorderRadius.circular(12), child: Image.memory(bytes, width: 280), ), Padding( padding: const EdgeInsets.all(12), child: Text( '图片大小:${(bytes.length / 1024).toStringAsFixed(1)} KB', style: const TextStyle(color: Colors.grey), ), ), TextButton( onPressed: () => Navigator.of(ctx).pop(), child: const Text('关闭'), ), ], ), ), ); _showResult('截图成功,可预览效果'); } catch (e) { _showResult('截图失败:$e'); } finally { if (mounted) setState(() => _sharing = false); } } // ==================== 场景 5:保存到相册 ==================== /// 5a:选图 → 保存到相册 Future _pickAndSaveToGallery() async { setState(() => _sharing = true); try { final picked = await PickerService.instance.showPickSheet( context, maxCount: 1, ); if (picked == null || picked.isEmpty) { _showResult('未选择图片'); return; } final filePath = picked.first.path; if (filePath == null) { _showResult('Web 端暂不支持保存'); return; } final ok = await ShareService.saveToGallery(filePath: filePath); _showResult(ok ? '✅ 已保存到相册' : '❌ 保存失败'); } catch (e) { _showResult('保存失败:$e'); } finally { if (mounted) setState(() => _sharing = false); } } /// 5b:Widget 截图 → 保存到相册 Future _captureAndSaveToGallery() async { setState(() => _sharing = true); try { final bytes = await ShareService.captureWidget( context: context, builder: (_) => _buildPosterWidget( userName: '张三同学', taskName: '二次函数综合练习', score: 98, ), ); if (bytes == null) { _showResult('截图失败'); return; } final ok = await ShareService.saveToGalleryFromBytes(bytes: bytes); _showResult(ok ? '✅ 海报已保存到相册' : '❌ 保存失败'); } catch (e) { _showResult('保存失败:$e'); } finally { if (mounted) setState(() => _sharing = false); } } // ==================== 海报 Widget(截图用) ==================== /// 模拟"攻克海报"布局(实际项目中从业务数据构建) Widget _buildPosterWidget({ required String userName, required String taskName, required int score, }) { return Container( width: 300, padding: const EdgeInsets.all(24), decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xFF667EEA), Color(0xFF764BA2)], ), borderRadius: BorderRadius.all(Radius.circular(16)), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ // Logo const Icon(Icons.school, color: Colors.white, size: 40), const SizedBox(height: 8), const Text( '爱学蝶变', style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 20), // 用户信息 Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( color: Colors.white.withOpacity(0.15), borderRadius: BorderRadius.circular(12), ), child: Column( children: [ Text( userName, style: const TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 4), Text( '攻克了 $taskName', style: TextStyle( color: Colors.white.withOpacity(0.8), fontSize: 13, ), ), ], ), ), const SizedBox(height: 16), // 分数 Text( '$score', style: const TextStyle( color: Colors.white, fontSize: 48, fontWeight: FontWeight.w800, height: 1, ), ), const Text( '分', style: TextStyle( color: Colors.white70, fontSize: 14, ), ), const SizedBox(height: 16), // 底部提示 Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(20), ), child: const Text( '扫码一起来挑战', style: TextStyle(color: Colors.white, fontSize: 12), ), ), ], ), ); } // ==================== UI ==================== @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('分享功能演示')), body: SingleChildScrollView( padding: EdgeInsets.all(16.w), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // ---------- 场景 1:文本分享 ---------- _sectionTitle('① 分享文本 / 链接', Icons.text_fields), SizedBox(height: 8.h), TextField( controller: _textController, decoration: InputDecoration( hintText: '输入要分享的文本或链接', border: OutlineInputBorder( borderRadius: BorderRadius.circular(8.r), ), contentPadding: EdgeInsets.symmetric( horizontal: 12.w, vertical: 12.h, ), ), maxLines: 2, ), SizedBox(height: 8.h), _shareButton( label: '分享文本', icon: Icons.share, onPressed: _shareText, ), SizedBox(height: 24.h), // ---------- 场景 2:选图分享 ---------- _sectionTitle('② 选图 → 分享', Icons.photo), SizedBox(height: 8.h), Text( '先从相册/拍照选一张图片,再通过系统分享面板分享出去', style: TextStyle(fontSize: 12.sp, color: Colors.grey), ), SizedBox(height: 8.h), _shareButton( label: '选图并分享', icon: Icons.image_outlined, onPressed: _pickAndShare, ), SizedBox(height: 24.h), // ---------- 场景 3:Widget 截图分享 ---------- _sectionTitle('③ Widget 截图 → 分享(海报)', Icons.card_giftcard), SizedBox(height: 8.h), Text( '把 Flutter Widget 截图成图片,再通过系统分享面板分享。\n' '这就是"攻克海报分享"的核心能力。', style: TextStyle(fontSize: 12.sp, color: Colors.grey), ), SizedBox(height: 8.h), // 海报预览(静态展示) Center(child: _buildPosterWidget( userName: '张三同学', taskName: '二次函数综合练习', score: 98, )), SizedBox(height: 12.h), Row( children: [ Expanded( child: _shareButton( label: '截图并分享', icon: Icons.share, onPressed: _sharePoster, ), ), SizedBox(width: 8.w), Expanded( child: _shareButton( label: '仅截图预览', icon: Icons.preview, onPressed: _previewPoster, color: const Color(0xFF64748B), ), ), ], ), SizedBox(height: 24.h), // ---------- 场景 5:保存到相册 ---------- _sectionTitle('④ 保存到相册', Icons.save_alt), SizedBox(height: 8.h), Text( '将图片或 Widget 截图写入系统相册(用 gal 插件实现)', style: TextStyle(fontSize: 12.sp, color: Colors.grey), ), SizedBox(height: 8.h), Row( children: [ Expanded( child: _shareButton( label: '选图→保存', icon: Icons.photo_library, onPressed: _pickAndSaveToGallery, color: const Color(0xFF059669), ), ), SizedBox(width: 8.w), Expanded( child: _shareButton( label: '截图→保存', icon: Icons.camera_alt, onPressed: _captureAndSaveToGallery, color: const Color(0xFF059669), ), ), ], ), SizedBox(height: 24.h), // ---------- 结果展示 ---------- if (_lastResult != null) Container( padding: EdgeInsets.all(12.w), decoration: BoxDecoration( color: const Color(0xFFF0F9FF), borderRadius: BorderRadius.circular(8.r), border: Border.all(color: const Color(0xFFBAE6FD)), ), child: Row( children: [ const Icon(Icons.info_outline, size: 18, color: Color(0xFF0284C7)), SizedBox(width: 8.w), Expanded( child: Text( _lastResult!, style: const TextStyle( fontSize: 13, color: Color(0xFF0C4A6E), ), ), ), ], ), ), ], ), ), ); } /// 分区标题 Widget _sectionTitle(String title, IconData icon) { return Row( children: [ Icon(icon, size: 20, color: const Color(0xFF3B82F6)), SizedBox(width: 8.w), Text( title, style: TextStyle( fontSize: 15.sp, fontWeight: FontWeight.w600, color: const Color(0xFF1E293B), ), ), ], ); } /// 分享按钮 Widget _shareButton({ required String label, required IconData icon, required VoidCallback onPressed, Color? color, }) { return SizedBox( height: 44.h, child: FilledButton.icon( onPressed: _sharing ? null : onPressed, icon: Icon(icon, size: 18), label: Text(label, style: TextStyle(fontSize: 13.sp)), style: FilledButton.styleFrom( backgroundColor: color ?? const Color(0xFF3B82F6), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.r), ), ), ), ); } }