From dad326ccff60a4b81456139c8ac7c278d4ba48d0 Mon Sep 17 00:00:00 2001 From: Foti Dim Date: Sun, 21 Dec 2025 09:11:57 +0100 Subject: [PATCH] Add device preview --- .vscode/launch.json | 7 ++ .vscode/settings.json | 2 + example/lib/main.dart | 46 +--------- example/lib/main_device_preview.dart | 88 +++++++++++++++++++ example/lib/universal_ble_app.dart | 55 ++++++++++++ .../Flutter/GeneratedPluginRegistrant.swift | 2 + example/macos/Podfile.lock | 15 +++- example/pubspec.lock | 85 ++++++++++++++++++ example/pubspec.yaml | 3 + 9 files changed, 256 insertions(+), 47 deletions(-) create mode 100644 example/lib/main_device_preview.dart create mode 100644 example/lib/universal_ble_app.dart diff --git a/.vscode/launch.json b/.vscode/launch.json index ccb1785..392fd29 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -40,5 +40,12 @@ "--web-port=8080" ], }, + { + "name": "device_preview", + "cwd": "example", + "request": "launch", + "type": "dart", + "program": "lib/main_device_preview.dart" + }, ], } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index c6bb3ee..0eb4ef6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -21,6 +21,7 @@ "LPOSVERSIONINFO", "lpsz", "LRESULT", + "LTWH", "MAXIMIZEBOX", "microtask", "MINIMIZEBOX", @@ -52,6 +53,7 @@ "Unpair", "Unpairing", "unwatch", + "USERPROFILE", "uuidstr", "winrt", "WNDCLASS", diff --git a/example/lib/main.dart b/example/lib/main.dart index 8aa7356..6d8e219 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,47 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:universal_ble/universal_ble.dart'; -import 'package:universal_ble_example/data/storage_service.dart'; -import 'package:universal_ble_example/home/permission_screen.dart'; -import 'package:universal_ble_example/home/scanner_screen.dart'; +import 'package:universal_ble_example/universal_ble_app.dart'; void main() async { - WidgetsFlutterBinding.ensureInitialized(); - await StorageService.instance.init(); - // await UniversalBle.setLogLevel(BleLogLevel.verbose); - bool hasPermission = await UniversalBle.hasPermissions( - withAndroidFineLocation: false, - ); - - runApp(MyApp( - hasPermission: hasPermission, - )); -} - -class MyApp extends StatelessWidget { - final bool hasPermission; - const MyApp({super.key, required this.hasPermission}); - - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Universal BLE', - debugShowCheckedModeBanner: false, - theme: ThemeData( - useMaterial3: true, - colorScheme: ColorScheme.fromSeed( - seedColor: Colors.blue, - brightness: Brightness.light, - ), - ), - darkTheme: ThemeData( - useMaterial3: true, - colorScheme: ColorScheme.fromSeed( - seedColor: Colors.blue, - brightness: Brightness.dark, - ), - ), - themeMode: ThemeMode.system, - home: hasPermission ? ScannerScreen() : const PermissionScreen(), - ); - } + bool hasPermission = await initializeApp(); + runApp(UniversalBleApp(hasPermission: hasPermission)); } diff --git a/example/lib/main_device_preview.dart b/example/lib/main_device_preview.dart new file mode 100644 index 0000000..5ea2d3b --- /dev/null +++ b/example/lib/main_device_preview.dart @@ -0,0 +1,88 @@ +import 'dart:io'; + +import 'package:device_preview_screenshot/device_preview_screenshot.dart'; +import 'package:flutter/material.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:universal_ble_example/universal_ble_app.dart'; + +void main() async { + bool hasPermission = await initializeApp(); + + // On macOS: ~/Library/Containers/com.navideck.universalble/Data/Documents/screenshots + final screenshotsDir = await getScreenshotsDirectory(); + debugPrint('Screenshots will be saved to: ${screenshotsDir.path}'); + + runApp( + DevicePreview( + enabled: true, + builder: (context) => UniversalBleApp( + hasPermission: hasPermission, + locale: DevicePreview.locale(context), + builder: DevicePreview.appBuilder, + ), + devices: [ + createCustomDevice( + name: 'App Store 1242x2688', + physicalWidth: 1242, + physicalHeight: 2688, + ), + ], + tools: [ + ...DevicePreview.defaultTools, + DevicePreviewScreenshot( + onScreenshot: screenshotAsFiles(screenshotsDir), + ), + ], + ), + ); +} + +/// Gets or creates the screenshots directory in the app's documents directory. +Future getScreenshotsDirectory() async { + final appDocumentsDir = await getApplicationDocumentsDirectory(); + final screenshotsDir = Directory('${appDocumentsDir.path}/screenshots'); + + if (!await screenshotsDir.exists()) { + await screenshotsDir.create(recursive: true); + } + + return screenshotsDir; +} + +/// Creates a custom device with the specified physical pixel dimensions. +DeviceInfo createCustomDevice({ + required String name, + required double physicalWidth, + required double physicalHeight, + double pixelRatio = 3.0, +}) { + final logicalWidth = physicalWidth / pixelRatio; + final logicalHeight = physicalHeight / pixelRatio; + + return DeviceInfo( + identifier: DeviceIdentifier( + TargetPlatform.iOS, + DeviceType.phone, + name, + ), + name: name, + pixelRatio: pixelRatio, + safeAreas: EdgeInsets.zero, + rotatedSafeAreas: EdgeInsets.zero, + screenPath: Path() + ..addRect(Rect.fromLTWH(0.0, 0.0, logicalWidth, logicalHeight)), + frameSize: Size(logicalWidth, logicalHeight), + screenSize: Size(logicalWidth, logicalHeight), + framePainter: const _EmptyFramePainter(), + ); +} + +class _EmptyFramePainter extends CustomPainter { + const _EmptyFramePainter(); + + @override + void paint(Canvas canvas, Size size) {} + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; +} diff --git a/example/lib/universal_ble_app.dart b/example/lib/universal_ble_app.dart new file mode 100644 index 0000000..e2b5976 --- /dev/null +++ b/example/lib/universal_ble_app.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:universal_ble/universal_ble.dart'; +import 'package:universal_ble_example/data/storage_service.dart'; +import 'package:universal_ble_example/home/permission_screen.dart'; +import 'package:universal_ble_example/home/scanner_screen.dart'; + +/// Initializes the app services and checks permissions. +/// Returns whether the app has the required permissions. +Future initializeApp() async { + WidgetsFlutterBinding.ensureInitialized(); + await StorageService.instance.init(); + // await UniversalBle.setLogLevel(BleLogLevel.verbose); + return await UniversalBle.hasPermissions( + withAndroidFineLocation: false, + ); +} + +class UniversalBleApp extends StatelessWidget { + final bool hasPermission; + final Locale? locale; + final Widget Function(BuildContext, Widget?)? builder; + + const UniversalBleApp({ + super.key, + required this.hasPermission, + this.locale, + this.builder, + }); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Universal BLE', + debugShowCheckedModeBanner: false, + locale: locale, + builder: builder, + theme: ThemeData( + useMaterial3: true, + colorScheme: ColorScheme.fromSeed( + seedColor: Colors.blue, + brightness: Brightness.light, + ), + ), + darkTheme: ThemeData( + useMaterial3: true, + colorScheme: ColorScheme.fromSeed( + seedColor: Colors.blue, + brightness: Brightness.dark, + ), + ), + themeMode: ThemeMode.system, + home: hasPermission ? ScannerScreen() : const PermissionScreen(), + ); + } +} diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift index 26eafea..18cc2a5 100644 --- a/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,12 +6,14 @@ import FlutterMacOS import Foundation import package_info_plus +import path_provider_foundation import shared_preferences_foundation import universal_ble import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UniversalBlePlugin.register(with: registry.registrar(forPlugin: "UniversalBlePlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock index 6ef0555..ec214e6 100644 --- a/example/macos/Podfile.lock +++ b/example/macos/Podfile.lock @@ -2,6 +2,9 @@ PODS: - FlutterMacOS (1.0.0) - package_info_plus (0.0.1): - FlutterMacOS + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS @@ -14,6 +17,7 @@ PODS: DEPENDENCIES: - FlutterMacOS (from `Flutter/ephemeral`) - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) + - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - universal_ble (from `Flutter/ephemeral/.symlinks/plugins/universal_ble/darwin`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) @@ -23,6 +27,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral package_info_plus: :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos + path_provider_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin shared_preferences_foundation: :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin universal_ble: @@ -32,10 +38,11 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 - package_info_plus: 12f1c5c2cfe8727ca46cbd0b26677728972d9a5b - shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6 - universal_ble: 65e1257dffc557cc7991a93d253beeddc7c1dc92 - url_launcher_macos: 175a54c831f4375a6cf895875f716ee5af3888ce + package_info_plus: f0052d280d17aa382b932f399edf32507174e870 + path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880 + shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb + universal_ble: 45519b2aeafe62761e2c6309f8927edb5288b914 + url_launcher_macos: f87a979182d112f911de6820aefddaf56ee9fbfd PODFILE CHECKSUM: 9ebaf0ce3d369aaa26a9ea0e159195ed94724cf3 diff --git a/example/pubspec.lock b/example/pubspec.lock index 11177ac..aa85ade 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -121,6 +121,30 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.11" + device_frame: + dependency: transitive + description: + name: device_frame + sha256: "7b2ebb2a09d6cc0f086b51bd1412d7be83e0170056a7290349169be41164c86a" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + device_preview: + dependency: "direct main" + description: + name: device_preview + sha256: "88aa1cc73ee9a8ec771b309dcbc4000cc66b5d8456b825980997640ab1195bf5" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + device_preview_screenshot: + dependency: "direct main" + description: + name: device_preview_screenshot + sha256: c79f185e155c0ed5d97b4e1afff6df181c2045fe28c7414c95f0b46307c549e6 + url: "https://pub.dev" + source: hosted + version: "1.0.0" expandable: dependency: "direct main" description: @@ -180,6 +204,11 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.0" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" flutter_test: dependency: "direct dev" description: flutter @@ -198,6 +227,14 @@ packages: description: flutter source: sdk version: "0.0.0" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8" + url: "https://pub.dev" + source: hosted + version: "3.1.0" fuchsia_remote_debug_protocol: dependency: transitive description: flutter @@ -240,6 +277,14 @@ packages: description: flutter source: sdk version: "0.0.0" + intl: + dependency: transitive + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" json_annotation: dependency: transitive description: @@ -320,6 +365,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" package_info_plus: dependency: "direct main" description: @@ -352,6 +405,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e + url: "https://pub.dev" + source: hosted + version: "2.2.22" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "6d13aece7b3f5c5a9731eaf553ff9dcbc2eff41087fd2df587fd0fed9a3eb0c4" + url: "https://pub.dev" + source: hosted + version: "2.5.1" path_provider_linux: dependency: transitive description: @@ -416,6 +493,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.5" + provider: + dependency: transitive + description: + name: provider + sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" + url: "https://pub.dev" + source: hosted + version: "6.1.5+1" shared_preferences: dependency: "direct main" description: diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 31e737a..2b46f1f 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -15,8 +15,11 @@ dependencies: shared_preferences: ^2.5.4 package_info_plus: ^9.0.0 url_launcher: ^6.3.2 + path_provider: ^2.1.1 universal_ble: path: ../ + device_preview: ^1.3.1 + device_preview_screenshot: ^1.0.0 dev_dependencies: integration_test: