add a preview if longpressing preview
This commit is contained in:
		| @@ -4,6 +4,7 @@ | |||||||
|    <application |    <application | ||||||
|         android:label="openmediacentermobile" |         android:label="openmediacentermobile" | ||||||
|         android:name="${applicationName}" |         android:name="${applicationName}" | ||||||
|  |         android:usesCleartextTraffic="true" | ||||||
|         android:icon="@mipmap/ic_launcher"> |         android:icon="@mipmap/ic_launcher"> | ||||||
|         <activity |         <activity | ||||||
|             android:name=".MainActivity" |             android:name=".MainActivity" | ||||||
|   | |||||||
| @@ -3,12 +3,10 @@ import 'dart:convert'; | |||||||
|  |  | ||||||
| import 'package:http/http.dart' as http; | import 'package:http/http.dart' as http; | ||||||
| import 'package:openmediacentermobile/api/token.dart'; | import 'package:openmediacentermobile/api/token.dart'; | ||||||
| import 'package:openmediacentermobile/log/log.dart'; |  | ||||||
|  |  | ||||||
| class API { | class API { | ||||||
|   static Future<String> query( |   static Future<String> query( | ||||||
|       String apinode, String action, Object payload) async { |       String apinode, String action, Object payload) async { | ||||||
|     final Completer<String> cmpl = Completer(); |  | ||||||
|     final t = await Token.getInstance().getToken(); |     final t = await Token.getInstance().getToken(); | ||||||
|     if (t != null) { |     if (t != null) { | ||||||
|       final resp = await http.post( |       final resp = await http.post( | ||||||
| @@ -20,11 +18,9 @@ class API { | |||||||
|         body: jsonEncode(payload), |         body: jsonEncode(payload), | ||||||
|       ); |       ); | ||||||
|  |  | ||||||
|       cmpl.complete(resp.body); |       return resp.body; | ||||||
|     } else { |     } else { | ||||||
|       cmpl.complete(""); |       return ""; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return cmpl.future; |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ class _LoginScreenState extends State<LoginScreen> { | |||||||
|  |  | ||||||
|   Future<String> login(String password, String domain) async { |   Future<String> login(String password, String domain) async { | ||||||
|     Log.i("logging in..."); |     Log.i("logging in..."); | ||||||
|     final compl = Completer<String>(); |     // final compl = Completer<String>(); | ||||||
|     final resp = await http.post( |     final resp = await http.post( | ||||||
|       Uri.parse(domain + '/api/login/login'), |       Uri.parse(domain + '/api/login/login'), | ||||||
|       headers: <String, String>{ |       headers: <String, String>{ | ||||||
| @@ -33,7 +33,9 @@ class _LoginScreenState extends State<LoginScreen> { | |||||||
|     ); |     ); | ||||||
|  |  | ||||||
|     if (resp.statusCode != 200) { |     if (resp.statusCode != 200) { | ||||||
|       compl.complete(resp.body); |       return resp.body; | ||||||
|  |  | ||||||
|  |       // compl.complete(resp.body); | ||||||
|     } else { |     } else { | ||||||
|       final json = jsonDecode(resp.body); |       final json = jsonDecode(resp.body); | ||||||
|       final token = json["Token"]; |       final token = json["Token"]; | ||||||
| @@ -41,12 +43,14 @@ class _LoginScreenState extends State<LoginScreen> { | |||||||
|       Token.getInstance().setToken(token, domain); |       Token.getInstance().setToken(token, domain); | ||||||
|       LoginContext.of(context).onLoggin(true); |       LoginContext.of(context).onLoggin(true); | ||||||
|  |  | ||||||
|       compl.complete(""); |       return ""; | ||||||
|  |  | ||||||
|  |       // compl.complete(""); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // LoginContext.of(context).onLoggin(true); |     // LoginContext.of(context).onLoggin(true); | ||||||
|  |  | ||||||
|     return compl.future; |     // return compl.future; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   | |||||||
							
								
								
									
										99
									
								
								lib/preview_grid.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								lib/preview_grid.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | |||||||
|  | import 'dart:ui'; | ||||||
|  |  | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; | ||||||
|  | import 'package:openmediacentermobile/platform.dart'; | ||||||
|  | import 'package:openmediacentermobile/preview_tile.dart'; | ||||||
|  |  | ||||||
|  | class PreviewGrid extends StatefulWidget { | ||||||
|  |   const PreviewGrid({Key? key, required this.videoLoader}) : super(key: key); | ||||||
|  |  | ||||||
|  |   final Future<List<VideoT>> Function() videoLoader; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   State<PreviewGrid> createState() => _PreviewGridState(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class _PreviewGridState extends State<PreviewGrid> { | ||||||
|  |   late Future<List<VideoT>> _data; | ||||||
|  |  | ||||||
|  |   Image? _previewImage; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   void initState() { | ||||||
|  |     super.initState(); | ||||||
|  |     _data = widget.videoLoader(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     double width = MediaQuery.of(context).size.width; | ||||||
|  |  | ||||||
|  |     return FutureBuilder<List<VideoT>>( | ||||||
|  |       future: _data, // a previously-obtained Future<String> or null | ||||||
|  |       builder: (BuildContext context, AsyncSnapshot<List<VideoT>> snapshot) { | ||||||
|  |         if (snapshot.hasError) { | ||||||
|  |           return Text("Error"); | ||||||
|  |         } else if (snapshot.hasData) { | ||||||
|  |           return Stack( | ||||||
|  |             children: [ | ||||||
|  |               MasonryGridView.count( | ||||||
|  |                 // every tile should be at max 330 pixels long... | ||||||
|  |                 crossAxisCount: isTV() ? width ~/ 200 : width ~/ 275, | ||||||
|  |                 // crossAxisCount: isTV() ? width ~/ 200 : width ~/ 330, | ||||||
|  |  | ||||||
|  |                 mainAxisSpacing: 4, | ||||||
|  |                 crossAxisSpacing: 4, | ||||||
|  |                 padding: EdgeInsets.all(5), | ||||||
|  |                 itemBuilder: (context, index) { | ||||||
|  |                   return PreviewTile( | ||||||
|  |                     dta: snapshot.data![index], | ||||||
|  |                     onLongPress: (img) { | ||||||
|  |                       setState(() { | ||||||
|  |                         _previewImage = img; | ||||||
|  |                       }); | ||||||
|  |                     }, | ||||||
|  |                     onLongPressEnd: () { | ||||||
|  |                       setState(() { | ||||||
|  |                         _previewImage = null; | ||||||
|  |                       }); | ||||||
|  |                     }, | ||||||
|  |                   ); | ||||||
|  |                 }, | ||||||
|  |               ), | ||||||
|  |               if (_previewImage != null) ...[ | ||||||
|  |                 BackdropFilter( | ||||||
|  |                   filter: ImageFilter.blur( | ||||||
|  |                     sigmaX: 5.0, | ||||||
|  |                     sigmaY: 5.0, | ||||||
|  |                   ), | ||||||
|  |                   child: Container( | ||||||
|  |                     color: Colors.white.withOpacity(0.6), | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |                 Container( | ||||||
|  |                   child: Center( | ||||||
|  |                     child: Padding(padding: EdgeInsets.symmetric(horizontal: 50),child: ClipRRect(borderRadius: BorderRadius.circular(10.0), child: _previewImage!)), | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |               ], | ||||||
|  |             ], | ||||||
|  |           ); | ||||||
|  |         } else { | ||||||
|  |           return Column(children: const <Widget>[ | ||||||
|  |             SizedBox( | ||||||
|  |               width: 60, | ||||||
|  |               height: 60, | ||||||
|  |               child: CircularProgressIndicator(), | ||||||
|  |             ), | ||||||
|  |             Padding( | ||||||
|  |               padding: EdgeInsets.only(top: 16), | ||||||
|  |               child: Text('Awaiting result...'), | ||||||
|  |             ) | ||||||
|  |           ]); | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -1,6 +1,5 @@ | |||||||
| import 'dart:convert'; | import 'dart:convert'; | ||||||
|  |  | ||||||
| import 'package:flutter/foundation.dart'; |  | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:openmediacentermobile/videoscreen_desktop.dart' | import 'package:openmediacentermobile/videoscreen_desktop.dart' | ||||||
|     if (dart.library.html) 'package:openmediacentermobile/videoscreen_web.dart' |     if (dart.library.html) 'package:openmediacentermobile/videoscreen_web.dart' | ||||||
| @@ -23,8 +22,10 @@ class VideoT { | |||||||
| } | } | ||||||
|  |  | ||||||
| class PreviewTile extends StatefulWidget { | class PreviewTile extends StatefulWidget { | ||||||
|   const PreviewTile({Key? key, required this.dta}) : super(key: key); |   const PreviewTile({Key? key, required this.dta, this.onLongPress, this.onLongPressEnd}) : super(key: key); | ||||||
|   final VideoT dta; |   final VideoT dta; | ||||||
|  |   final Function(Image img)? onLongPress; | ||||||
|  |   final Function? onLongPressEnd; | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   _PreviewTileState createState() => _PreviewTileState(); |   _PreviewTileState createState() => _PreviewTileState(); | ||||||
| @@ -80,22 +81,39 @@ class _PreviewTileState extends State<PreviewTile> { | |||||||
|               children: [ |               children: [ | ||||||
|                 Container( |                 Container( | ||||||
|                   child: Column( |                   child: Column( | ||||||
|                     children: [Text(widget.dta.title, style: TextStyle(fontSize: isTV() ? 8 : 12)), snapshot.data!], |                     children: [ | ||||||
|  |                       Text( | ||||||
|  |                         widget.dta.title, | ||||||
|  |                         style: TextStyle(fontSize: isTV() ? 8 : 10.5), | ||||||
|  |                         overflow: TextOverflow.clip, | ||||||
|  |                         maxLines: 1, | ||||||
|  |                       ), | ||||||
|  |                       snapshot.data! | ||||||
|  |                     ], | ||||||
|                   ), |                   ), | ||||||
|                   color: Colors.green, |                   color: Color(0xFF6CE56F), | ||||||
|                 ), |                 ), | ||||||
|                 Positioned.fill( |                 Positioned.fill( | ||||||
|                   child: Material( |                   child: Material( | ||||||
|                     color: Colors.transparent, |                     color: Colors.transparent, | ||||||
|                     child: InkWell( |                     child: GestureDetector( | ||||||
|                       onTap: () { |                       behavior: HitTestBehavior.translucent, | ||||||
|                         Navigator.push( |                       onLongPress: () { | ||||||
|                           context, |                         if (widget.onLongPress != null) widget.onLongPress!(snapshot.data!); | ||||||
|                           MaterialPageRoute( |  | ||||||
|                             builder: (context) => VideoScreen(MetaData: widget.dta), |  | ||||||
|                           ), |  | ||||||
|                         ); |  | ||||||
|                       }, |                       }, | ||||||
|  |                       onLongPressEnd: (details) { | ||||||
|  |                         if (widget.onLongPressEnd != null) widget.onLongPressEnd!(); | ||||||
|  |                       }, | ||||||
|  |                       child: InkWell( | ||||||
|  |                         onTap: () { | ||||||
|  |                           Navigator.push( | ||||||
|  |                             context, | ||||||
|  |                             MaterialPageRoute( | ||||||
|  |                               builder: (context) => VideoScreen(MetaData: widget.dta), | ||||||
|  |                             ), | ||||||
|  |                           ); | ||||||
|  |                         }, | ||||||
|  |                       ), | ||||||
|                     ), |                     ), | ||||||
|                   ), |                   ), | ||||||
|                 ), |                 ), | ||||||
|   | |||||||
| @@ -51,18 +51,23 @@ class _ShuffleScreenState extends State<ShuffleScreen> { | |||||||
|               crossAxisCount: isTV() ? width ~/ 200 : width ~/ 330, |               crossAxisCount: isTV() ? width ~/ 200 : width ~/ 330, | ||||||
|               mainAxisSpacing: 4, |               mainAxisSpacing: 4, | ||||||
|               crossAxisSpacing: 4, |               crossAxisSpacing: 4, | ||||||
|  |               padding: EdgeInsets.all(5), | ||||||
|               itemCount: 4, |               itemCount: 4, | ||||||
|               itemBuilder: (context, index) { |               itemBuilder: (context, index) { | ||||||
|                 return PreviewTile(dta: snapshot.data![index]); |                 return PreviewTile(dta: snapshot.data![index]); | ||||||
|               }, |               }, | ||||||
|             ), |             ), | ||||||
|             const SizedBox(height: 25,), |             const SizedBox( | ||||||
|             TextButton.icon(onPressed: () { |               height: 25, | ||||||
|               setState(() { |             ), | ||||||
|                 _data = loadData(); |             TextButton.icon( | ||||||
|               }); |                 onPressed: () { | ||||||
|  |                   setState(() { | ||||||
|             }, icon: const Icon(Icons.update), label: const Text("Shuffle")) |                     _data = loadData(); | ||||||
|  |                   }); | ||||||
|  |                 }, | ||||||
|  |                 icon: const Icon(Icons.update), | ||||||
|  |                 label: const Text("Shuffle")) | ||||||
|           ]); |           ]); | ||||||
|         } else { |         } else { | ||||||
|           return Column(children: const <Widget>[ |           return Column(children: const <Widget>[ | ||||||
|   | |||||||
| @@ -2,11 +2,10 @@ import 'dart:convert'; | |||||||
|  |  | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:openmediacentermobile/api/api.dart'; | import 'package:openmediacentermobile/api/api.dart'; | ||||||
|  | import 'package:openmediacentermobile/preview_grid.dart'; | ||||||
|  |  | ||||||
| import 'log/log.dart'; | import 'log/log.dart'; | ||||||
| import 'platform.dart'; |  | ||||||
| import 'preview_tile.dart'; | import 'preview_tile.dart'; | ||||||
| import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; |  | ||||||
|  |  | ||||||
| class VideoFeed extends StatefulWidget { | class VideoFeed extends StatefulWidget { | ||||||
|   const VideoFeed({Key? key}) : super(key: key); |   const VideoFeed({Key? key}) : super(key: key); | ||||||
| @@ -18,7 +17,6 @@ class VideoFeed extends StatefulWidget { | |||||||
| } | } | ||||||
|  |  | ||||||
| class VideoFeedState extends State<VideoFeed> { | class VideoFeedState extends State<VideoFeed> { | ||||||
|   late Future<List<VideoT>> _data; |  | ||||||
|  |  | ||||||
|   Future<List<VideoT>> loadData() async { |   Future<List<VideoT>> loadData() async { | ||||||
|     final data = await API.query("video", "getMovies", {'Tag': 1, 'Sort': 0}); |     final data = await API.query("video", "getMovies", {'Tag': 1, 'Sort': 0}); | ||||||
| @@ -30,47 +28,11 @@ class VideoFeedState extends State<VideoFeed> { | |||||||
|     return dta; |     return dta; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @override |  | ||||||
|   void initState() { |  | ||||||
|     super.initState(); |  | ||||||
|  |  | ||||||
|     _data = loadData(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     double width = MediaQuery.of(context).size.width; |     double width = MediaQuery.of(context).size.width; | ||||||
|     Log.d(width); |     Log.d(width); | ||||||
|  |  | ||||||
|     return FutureBuilder<List<VideoT>>( |     return PreviewGrid(videoLoader: () => loadData(),); | ||||||
|       future: _data, // a previously-obtained Future<String> or null |  | ||||||
|       builder: (BuildContext context, AsyncSnapshot<List<VideoT>> snapshot) { |  | ||||||
|         if (snapshot.hasError) { |  | ||||||
|           return Text("Error"); |  | ||||||
|         } else if (snapshot.hasData) { |  | ||||||
|           return MasonryGridView.count( |  | ||||||
|             // every tile should be at max 330 pixels long... |  | ||||||
|             crossAxisCount: isTV() ? width ~/ 200 : width ~/ 330, |  | ||||||
|             mainAxisSpacing: 4, |  | ||||||
|             crossAxisSpacing: 4, |  | ||||||
|             itemBuilder: (context, index) { |  | ||||||
|               return PreviewTile(dta: snapshot.data![index]); |  | ||||||
|             }, |  | ||||||
|           ); |  | ||||||
|         } else { |  | ||||||
|           return Column(children: const <Widget>[ |  | ||||||
|             SizedBox( |  | ||||||
|               width: 60, |  | ||||||
|               height: 60, |  | ||||||
|               child: CircularProgressIndicator(), |  | ||||||
|             ), |  | ||||||
|             Padding( |  | ||||||
|               padding: EdgeInsets.only(top: 16), |  | ||||||
|               child: Text('Awaiting result...'), |  | ||||||
|             ) |  | ||||||
|           ]); |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|     ); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -53,6 +53,10 @@ class _VideoScreenState extends State<VideoScreen> { | |||||||
|         videoPlayerController: _controller, |         videoPlayerController: _controller, | ||||||
|         autoPlay: true, |         autoPlay: true, | ||||||
|         looping: true, |         looping: true, | ||||||
|  |         allowFullScreen: true, | ||||||
|  |         allowMuting: true, | ||||||
|  |         allowPlaybackSpeedChanging: true, | ||||||
|  |         zoomAndPan: true | ||||||
|       ); |       ); | ||||||
|  |  | ||||||
|       setState(() {}); |       setState(() {}); | ||||||
|   | |||||||
| @@ -56,7 +56,7 @@ packages: | |||||||
|       name: collection |       name: collection | ||||||
|       url: "https://pub.dartlang.org" |       url: "https://pub.dartlang.org" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "1.15.0" |     version: "1.16.0" | ||||||
|   csslib: |   csslib: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
| @@ -133,7 +133,7 @@ packages: | |||||||
|       name: fake_async |       name: fake_async | ||||||
|       url: "https://pub.dartlang.org" |       url: "https://pub.dartlang.org" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "1.2.0" |     version: "1.3.0" | ||||||
|   ffi: |   ffi: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
| @@ -526,5 +526,5 @@ packages: | |||||||
|     source: hosted |     source: hosted | ||||||
|     version: "0.2.0+1" |     version: "0.2.0+1" | ||||||
| sdks: | sdks: | ||||||
|   dart: ">=2.16.0-100.0.dev <3.0.0" |   dart: ">=2.17.0-0 <3.0.0" | ||||||
|   flutter: ">=2.8.0" |   flutter: ">=2.8.0" | ||||||
|   | |||||||
| @@ -12,19 +12,6 @@ import 'package:openmediacentermobile/main.dart'; | |||||||
|  |  | ||||||
| void main() { | void main() { | ||||||
|   testWidgets('Counter increments smoke test', (WidgetTester tester) async { |   testWidgets('Counter increments smoke test', (WidgetTester tester) async { | ||||||
|     // Build our app and trigger a frame. |  | ||||||
|     await tester.pumpWidget(const MyApp()); |  | ||||||
|  |  | ||||||
|     // Verify that our counter starts at 0. |  | ||||||
|     expect(find.text('0'), findsOneWidget); |  | ||||||
|     expect(find.text('1'), findsNothing); |  | ||||||
|  |  | ||||||
|     // Tap the '+' icon and trigger a frame. |  | ||||||
|     await tester.tap(find.byIcon(Icons.add)); |  | ||||||
|     await tester.pump(); |  | ||||||
|  |  | ||||||
|     // Verify that our counter has incremented. |  | ||||||
|     expect(find.text('0'), findsNothing); |  | ||||||
|     expect(find.text('1'), findsOneWidget); |  | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user