import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:veloe_kemono_party_flutter/video_thumbnail_generator'; import '../../main.dart'; class VideoPreview extends StatefulWidget { final String videoUrl; final VoidCallback onTap; const VideoPreview({ super.key, required this.videoUrl, required this.onTap, }); @override State createState() => _VideoPreviewState(); } class _VideoPreviewState extends State { Future? _thumbnailFuture; bool _isGenerating = false; bool _isCached = false; @override void initState() { super.initState(); _checkCacheStatus(); _loadThumbnail(); } void _loadThumbnail() { setState(() => _isGenerating = true); _thumbnailFuture = VideoThumbnailGenerator.getFirstFrameThumbnail(widget.videoUrl) .whenComplete(() => setState(() => _isGenerating = false)); } Future _checkCacheStatus() async { final cachedFile = await videoCacheManager.getFileFromCache(widget.videoUrl); if (cachedFile != null && mounted) { setState(() => _isCached = true); } } @override Widget build(BuildContext context) { return GestureDetector( onTap: widget.onTap, child: FutureBuilder( future: _thumbnailFuture, builder: (context, snapshot) { return AspectRatio( aspectRatio: 16/9, child: Stack( fit: StackFit.expand, children: [ // Thumbnail display if (snapshot.hasData) Image.memory(snapshot.data!, fit: BoxFit.cover) else if (_isGenerating) const Center(child: CircularProgressIndicator()) else Container(color: Colors.black), // Play button overlay const Center( child: Icon(Icons.play_circle_filled, size: 64, color: Colors.white70, ), ), // Video indicator Positioned( bottom: 8, right: 8, child: Container( padding: const EdgeInsets.all(4), color: Colors.black54, child: const Text('VIDEO', style: TextStyle(color: Colors.white)), ), ), if (_isCached) Positioned( top: 8, right: 8, child: Container( padding: const EdgeInsets.all(4), decoration: const BoxDecoration( color: Colors.green, shape: BoxShape.circle, ), child: const Icon(Icons.download_done, size: 16, color: Colors.white), ), ), ], ), ); }, ), ); } }