diff --git a/assets/marketing/screenshots/Mobile/Dashboard.png b/assets/marketing/screenshots/Mobile/Dashboard.png index a7d46bd..17282e5 100644 Binary files a/assets/marketing/screenshots/Mobile/Dashboard.png and b/assets/marketing/screenshots/Mobile/Dashboard.png differ diff --git a/assets/marketing/screenshots/Mobile/Details.png b/assets/marketing/screenshots/Mobile/Details.png index f41046b..0b102fb 100644 Binary files a/assets/marketing/screenshots/Mobile/Details.png and b/assets/marketing/screenshots/Mobile/Details.png differ diff --git a/assets/marketing/screenshots/Mobile/Details_2.png b/assets/marketing/screenshots/Mobile/Details_2.png index 26f2828..1a2420f 100644 Binary files a/assets/marketing/screenshots/Mobile/Details_2.png and b/assets/marketing/screenshots/Mobile/Details_2.png differ diff --git a/assets/marketing/screenshots/Mobile/Favourites.png b/assets/marketing/screenshots/Mobile/Favourites.png index 20763f6..6c874c1 100644 Binary files a/assets/marketing/screenshots/Mobile/Favourites.png and b/assets/marketing/screenshots/Mobile/Favourites.png differ diff --git a/assets/marketing/screenshots/Mobile/Library.png b/assets/marketing/screenshots/Mobile/Library.png index 0a27852..f8d761b 100644 Binary files a/assets/marketing/screenshots/Mobile/Library.png and b/assets/marketing/screenshots/Mobile/Library.png differ diff --git a/assets/marketing/screenshots/Mobile/Library_Search.png b/assets/marketing/screenshots/Mobile/Library_Search.png index 4a09053..758a05b 100644 Binary files a/assets/marketing/screenshots/Mobile/Library_Search.png and b/assets/marketing/screenshots/Mobile/Library_Search.png differ diff --git a/assets/marketing/screenshots/Mobile/Player.png b/assets/marketing/screenshots/Mobile/Player.png index 20687e6..20ba978 100644 Binary files a/assets/marketing/screenshots/Mobile/Player.png and b/assets/marketing/screenshots/Mobile/Player.png differ diff --git a/assets/marketing/screenshots/Mobile/Resume_Tab.png b/assets/marketing/screenshots/Mobile/Resume_Tab.png index 03a797b..530bf7c 100644 Binary files a/assets/marketing/screenshots/Mobile/Resume_Tab.png and b/assets/marketing/screenshots/Mobile/Resume_Tab.png differ diff --git a/assets/marketing/screenshots/Mobile/Settings.png b/assets/marketing/screenshots/Mobile/Settings.png index efd44c1..a1feff9 100644 Binary files a/assets/marketing/screenshots/Mobile/Settings.png and b/assets/marketing/screenshots/Mobile/Settings.png differ diff --git a/assets/marketing/screenshots/Mobile/Sync.png b/assets/marketing/screenshots/Mobile/Sync.png index b17e1a7..c17469f 100644 Binary files a/assets/marketing/screenshots/Mobile/Sync.png and b/assets/marketing/screenshots/Mobile/Sync.png differ diff --git a/assets/marketing/screenshots/Tablet/Dashboard.png b/assets/marketing/screenshots/Tablet/Dashboard.png index 107c853..58f2bea 100644 Binary files a/assets/marketing/screenshots/Tablet/Dashboard.png and b/assets/marketing/screenshots/Tablet/Dashboard.png differ diff --git a/assets/marketing/screenshots/Tablet/Details.png b/assets/marketing/screenshots/Tablet/Details.png index 9d5e01f..22c0bb0 100644 Binary files a/assets/marketing/screenshots/Tablet/Details.png and b/assets/marketing/screenshots/Tablet/Details.png differ diff --git a/assets/marketing/screenshots/Tablet/Details_2.png b/assets/marketing/screenshots/Tablet/Details_2.png index 197d44f..64e1bab 100644 Binary files a/assets/marketing/screenshots/Tablet/Details_2.png and b/assets/marketing/screenshots/Tablet/Details_2.png differ diff --git a/assets/marketing/screenshots/Tablet/Favourites.png b/assets/marketing/screenshots/Tablet/Favourites.png index 6c4ff94..517a7ae 100644 Binary files a/assets/marketing/screenshots/Tablet/Favourites.png and b/assets/marketing/screenshots/Tablet/Favourites.png differ diff --git a/assets/marketing/screenshots/Tablet/Library.png b/assets/marketing/screenshots/Tablet/Library.png index 569373c..25a2d68 100644 Binary files a/assets/marketing/screenshots/Tablet/Library.png and b/assets/marketing/screenshots/Tablet/Library.png differ diff --git a/assets/marketing/screenshots/Tablet/Library_Search.png b/assets/marketing/screenshots/Tablet/Library_Search.png index 2119aac..87dec7b 100644 Binary files a/assets/marketing/screenshots/Tablet/Library_Search.png and b/assets/marketing/screenshots/Tablet/Library_Search.png differ diff --git a/assets/marketing/screenshots/Tablet/Player.png b/assets/marketing/screenshots/Tablet/Player.png index 72d96a2..048b8b8 100644 Binary files a/assets/marketing/screenshots/Tablet/Player.png and b/assets/marketing/screenshots/Tablet/Player.png differ diff --git a/assets/marketing/screenshots/Tablet/Resume_Tab.png b/assets/marketing/screenshots/Tablet/Resume_Tab.png index 48750a7..a15298d 100644 Binary files a/assets/marketing/screenshots/Tablet/Resume_Tab.png and b/assets/marketing/screenshots/Tablet/Resume_Tab.png differ diff --git a/assets/marketing/screenshots/Tablet/Settings.png b/assets/marketing/screenshots/Tablet/Settings.png index b9e2a64..e6d96cf 100644 Binary files a/assets/marketing/screenshots/Tablet/Settings.png and b/assets/marketing/screenshots/Tablet/Settings.png differ diff --git a/assets/marketing/screenshots/Tablet/Sync.png b/assets/marketing/screenshots/Tablet/Sync.png index e5ae951..44601e7 100644 Binary files a/assets/marketing/screenshots/Tablet/Sync.png and b/assets/marketing/screenshots/Tablet/Sync.png differ diff --git a/assets/marketing/screenshots/Television/Dashboard.png b/assets/marketing/screenshots/Television/Dashboard.png new file mode 100644 index 0000000..c630716 Binary files /dev/null and b/assets/marketing/screenshots/Television/Dashboard.png differ diff --git a/assets/marketing/screenshots/Television/Details.png b/assets/marketing/screenshots/Television/Details.png new file mode 100644 index 0000000..2d52b5b Binary files /dev/null and b/assets/marketing/screenshots/Television/Details.png differ diff --git a/assets/marketing/screenshots/Television/Favourites.png b/assets/marketing/screenshots/Television/Favourites.png new file mode 100644 index 0000000..450e764 Binary files /dev/null and b/assets/marketing/screenshots/Television/Favourites.png differ diff --git a/assets/marketing/screenshots/Television/Library.png b/assets/marketing/screenshots/Television/Library.png new file mode 100644 index 0000000..ffd03a1 Binary files /dev/null and b/assets/marketing/screenshots/Television/Library.png differ diff --git a/assets/marketing/screenshots/Television/Library_Search.png b/assets/marketing/screenshots/Television/Library_Search.png new file mode 100644 index 0000000..0d467c9 Binary files /dev/null and b/assets/marketing/screenshots/Television/Library_Search.png differ diff --git a/assets/marketing/screenshots/Television/Player.png b/assets/marketing/screenshots/Television/Player.png new file mode 100644 index 0000000..2dd76d7 Binary files /dev/null and b/assets/marketing/screenshots/Television/Player.png differ diff --git a/assets/marketing/screenshots/Television/Settings.png b/assets/marketing/screenshots/Television/Settings.png new file mode 100644 index 0000000..aef3b02 Binary files /dev/null and b/assets/marketing/screenshots/Television/Settings.png differ diff --git a/assets/marketing/screenshots/Television/Sync.png b/assets/marketing/screenshots/Television/Sync.png new file mode 100644 index 0000000..6a74cb7 Binary files /dev/null and b/assets/marketing/screenshots/Television/Sync.png differ diff --git a/lib/util/screenshot_runner.dart b/lib/util/screenshot_runner.dart new file mode 100644 index 0000000..f6f94ca --- /dev/null +++ b/lib/util/screenshot_runner.dart @@ -0,0 +1,251 @@ +import 'dart:io'; +import 'dart:ui' as ui; + +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +import 'package:fladder/util/adaptive_layout/adaptive_layout.dart'; +import 'package:fladder/util/adaptive_layout/adaptive_layout_model.dart'; +import 'package:fladder/util/poster_defaults.dart'; + +class _DeviceProfile { + final String name; + final Size size; + final double scale; + final bool isDesktop; + final InputDevice inputDevice; + final ViewSize viewSize; + final LayoutMode layoutMode; + final TargetPlatform platform; + + const _DeviceProfile({ + required this.name, + required this.size, + this.scale = 1.0, + this.isDesktop = false, + this.inputDevice = InputDevice.dPad, + required this.viewSize, + required this.layoutMode, + required this.platform, + }); +} + +class ScreenshotRunner extends StatefulWidget { + final Widget child; + final String outputPath; + + const ScreenshotRunner({ + super.key, + required this.child, + required this.outputPath, + }); + + @override + State createState() => _ScreenshotRunnerState(); +} + +class _ScreenshotRunnerState extends State { + final GlobalKey _repaintKey = GlobalKey(); + int _currentIndex = 0; + bool _isCapturing = false; + + bool _showNameInput = false; + + final TextEditingController _nameController = TextEditingController(); + + Future _capture(_DeviceProfile device, String screenshotName) async { + final boundary = _repaintKey.currentContext!.findRenderObject() as RenderRepaintBoundary?; + if (boundary == null) { + debugPrint('No boundary found for ${device.name}'); + return; + } + + final rawImage = await boundary.toImage(pixelRatio: 1.0); + + final resized = await resizeUiImage( + rawImage, + (device.size.width * device.scale).toInt(), + (device.size.height * device.scale).toInt(), + ); + + final byteData = await resized.toByteData(format: ui.ImageByteFormat.png); + final bytes = byteData!.buffer.asUint8List(); + + final dir = Directory('${widget.outputPath}/${device.name}'); + if (!dir.existsSync()) dir.createSync(recursive: true); + + final file = File('${dir.path}/$screenshotName.png'); + await file.writeAsBytes(bytes); + debugPrint('Saved: ${file.path}'); + } + + Future _runScreenshots(String value) async { + if (_isCapturing) return; + setState(() => _isCapturing = true); + + for (int i = 0; i < _defaultDeviceProfiles.length; i++) { + setState(() => _currentIndex = i); + await Future.delayed(const Duration(milliseconds: 300)); + await _capture(_defaultDeviceProfiles[i], value); + } + + setState(() => _isCapturing = false); + } + + Future resizeUiImage(ui.Image image, int targetWidth, int targetHeight) async { + final recorder = ui.PictureRecorder(); + final canvas = Canvas(recorder); + final paint = Paint(); + + final src = Rect.fromLTWH(0, 0, image.width.toDouble(), image.height.toDouble()); + final dst = Rect.fromLTWH(0, 0, targetWidth.toDouble(), targetHeight.toDouble()); + + canvas.drawImageRect(image, src, dst, paint); + + final picture = recorder.endRecording(); + return await picture.toImage(targetWidth, targetHeight); + } + + @override + Widget build(BuildContext context) { + final device = _defaultDeviceProfiles[_currentIndex]; + + final scale = device.scale; + final mediaQuery = MediaQuery.of(context); + final screenSize = device.size; + + final scaledMedia = _isCapturing + ? mediaQuery.copyWith( + size: screenSize, + padding: EdgeInsets.zero, + viewInsets: EdgeInsets.zero, + viewPadding: EdgeInsets.zero, + devicePixelRatio: mediaQuery.devicePixelRatio * scale, + ) + : MediaQuery.of(context); + + return Scaffold( + body: Stack( + children: [ + Center( + child: RepaintBoundary( + key: _repaintKey, + child: FittedBox( + alignment: Alignment.center, + child: SizedBox( + width: scaledMedia.size.width, + height: scaledMedia.size.height, + child: MediaQuery( + data: scaledMedia, + child: Builder( + builder: (context) { + return AdaptiveLayout( + data: _isCapturing + ? AdaptiveLayoutModel( + viewSize: device.viewSize, + layoutMode: device.layoutMode, + inputDevice: device.inputDevice, + platform: device.platform, + isDesktop: device.isDesktop, + posterDefaults: const PosterDefaults(size: 350, ratio: 0.55), + controller: {}, + sideBarWidth: 0, + ) + : AdaptiveLayout.of(context), + child: widget.child, + ); + }, + ), + ), + ), + ), + ), + ), + if (_showNameInput) + Center( + child: Overlay( + initialEntries: [ + OverlayEntry( + canSizeOverlay: true, + builder: (context) => Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.black, + borderRadius: BorderRadius.circular(12), + ), + width: 300, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text("Enter screenshot name:"), + const SizedBox(height: 8), + TextField( + controller: _nameController, + autofocus: true, + onSubmitted: (value) { + setState(() => _showNameInput = false); + _runScreenshots(value.trim()); + _nameController.clear(); + }, + ), + const SizedBox(height: 12), + ElevatedButton( + onPressed: () { + setState(() => _showNameInput = false); + _runScreenshots(_nameController.text.trim()); + _nameController.clear(); + }, + child: const Text("Capture"), + ), + ], + ), + ), + ) + ], + ), + ), + ], + ), + floatingActionButton: FloatingActionButton.extended( + onPressed: () => setState(() { + _showNameInput = true; + }), + label: Text(_isCapturing ? 'Capturing...' : 'Take Screenshots'), + icon: const Icon(Icons.camera), + ), + ); + } +} + +const _defaultDeviceProfiles = [ + _DeviceProfile( + name: 'Mobile', + size: Size(412, 915), + scale: 3.0, + inputDevice: InputDevice.touch, + viewSize: ViewSize.phone, + layoutMode: LayoutMode.single, + platform: TargetPlatform.android, + isDesktop: false, + ), + _DeviceProfile( + name: 'Tablet', + size: Size(1194, 834), + scale: 2.0, + inputDevice: InputDevice.touch, + viewSize: ViewSize.tablet, + layoutMode: LayoutMode.single, + platform: TargetPlatform.android, + isDesktop: false, + ), + _DeviceProfile( + name: 'Television', + size: Size(1920, 1080), + scale: 1.0, + inputDevice: InputDevice.dPad, + viewSize: ViewSize.television, + layoutMode: LayoutMode.dual, + platform: TargetPlatform.android, + isDesktop: false, + ), +]; diff --git a/lib/widgets/navigation_scaffold/components/side_navigation_bar.dart b/lib/widgets/navigation_scaffold/components/side_navigation_bar.dart index d082542..b769333 100644 --- a/lib/widgets/navigation_scaffold/components/side_navigation_bar.dart +++ b/lib/widgets/navigation_scaffold/components/side_navigation_bar.dart @@ -72,12 +72,12 @@ class _SideNavigationBarState extends ConsumerState { return Stack( children: [ - AdaptiveLayoutBuilder( - adaptiveLayout: AdaptiveLayout.of(context).copyWith( + AdaptiveLayout( + data: AdaptiveLayout.of(context).copyWith( // -0.1 offset to fix single visible pixel line sideBarWidth: (fullyExpanded ? expandedWidth : collapsedWidth) - 0.1, ), - child: (context) => widget.child, + child: widget.child, ), FocusTraversalGroup( policy: _RailTraversalPolicy(),