import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:sqflite/sqflite.dart'; import '../api/api.dart'; import '../db/database.dart'; import '../log/log.dart'; import '../platform.dart'; import '../types/video.dart'; import '../video_screen/videoscreen.dart'; class PreviewTile extends StatefulWidget { const PreviewTile( {Key? key, required this.dta, this.onLongPress, this.onLongPressEnd}) : super(key: key); final VideoT dta; final Function(Image img)? onLongPress; final Function? onLongPressEnd; @override _PreviewTileState createState() => _PreviewTileState(); } class _PreviewTileState extends State { late Future _preview; @override void initState() { super.initState(); _preview = loadData(); Log.d("initial load of tile"); } @override void didUpdateWidget(PreviewTile oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.dta != widget.dta) { setState(() { _preview = loadData(); }); Log.i("load of tile due to change"); } } Future insert(int id, Uint8List pic) async { await Db().db().insert( 'previews', {'id': id, 'thumbnail': pic}, conflictAlgorithm: ConflictAlgorithm.replace, ); } Future _fetchThumbnail(int id) async { final base64str = await API.query("video", "readThumbnail", {'Movieid': id}); return base64Decode(base64str.substring(23)); } Future loadData() async { Uint8List data; final id = widget.dta.id; if (kIsWeb) { data = await _fetchThumbnail(id); } else { final List> prev = await Db().db().query('previews', where: "id=$id"); if (prev.isEmpty) { data = await _fetchThumbnail(id); insert(id, data); Log.d("Adding $id to db"); } else { data = prev.first["thumbnail"] as Uint8List; Log.d("using cached preview for $id"); } } final img = Image.memory( data, width: double.infinity, fit: BoxFit.fitWidth, ); // precache image to avoid loading time to render image await precacheImage(img.image, context); return img; } Widget _buildLoader() { return Column(children: const [ SizedBox(height: 50), SizedBox( width: 60, height: 60, child: CircularProgressIndicator(), ), Padding( padding: EdgeInsets.only(top: 16), child: Text('Awaiting result...'), ), SizedBox(height: 50), ]); } Widget _buildTile(Image image) { return ClipRRect( borderRadius: BorderRadius.circular(20.0), child: Stack( children: [ Container( child: Column( children: [ Text( widget.dta.title, style: TextStyle(fontSize: isTV() ? 8 : 10.5), overflow: TextOverflow.clip, maxLines: 1, ), image ], ), color: Color(0x6a94a6ff), ), Positioned.fill( child: Material( color: Colors.transparent, child: GestureDetector( behavior: HitTestBehavior.translucent, onLongPress: () { if (widget.onLongPress != null) widget.onLongPress!(image); }, onLongPressEnd: (details) { if (widget.onLongPressEnd != null) widget.onLongPressEnd!(); }, child: InkWell( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => VideoScreen(metaData: widget.dta), ), ); }, ), ), ), ), ], ), ); } @override Widget build(BuildContext context) { return FutureBuilder( future: _preview, // a previously-obtained Future or null builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.connectionState != ConnectionState.done) { return _buildLoader(); } if (snapshot.hasError) { return Text("Error"); } else if (snapshot.hasData) { return _buildTile(snapshot.data!); } else { return _buildLoader(); } }, ); } }