From fd7123d885b296e8c915875beea51ba4ed10203b Mon Sep 17 00:00:00 2001 From: TID-Test Date: Thu, 29 Jan 2026 18:52:16 +0100 Subject: [PATCH 1/5] try to add rotation --- lib/src/cli/commands/devices.dart | 2 ++ lib/src/cli/flutterpi_command.dart | 31 +++++++++++++++++++ lib/src/config.dart | 8 ++++- lib/src/devices/flutterpi_ssh/device.dart | 12 ++++++- .../flutterpi_ssh/device_discovery.dart | 1 + 5 files changed, 52 insertions(+), 2 deletions(-) diff --git a/lib/src/cli/commands/devices.dart b/lib/src/cli/commands/devices.dart index fc0363c..320aefd 100644 --- a/lib/src/cli/commands/devices.dart +++ b/lib/src/cli/commands/devices.dart @@ -410,6 +410,7 @@ class DevicesAddCommand extends FlutterpiCommand { usesDummyDisplayArg(); usesSshRemoteNonOptionArg(); usesFilesystemLayoutArg(); + usesRotationArg(); } @override @@ -500,6 +501,7 @@ class DevicesAddCommand extends FlutterpiCommand { useDummyDisplay: useDummyDisplay, dummyDisplaySize: dummyDisplaySize, filesystemLayout: fsLayout, + rotation: rotation?.toInt(), ), ); diff --git a/lib/src/cli/flutterpi_command.dart b/lib/src/cli/flutterpi_command.dart index 51da8c6..cabdc2e 100644 --- a/lib/src/cli/flutterpi_command.dart +++ b/lib/src/cli/flutterpi_command.dart @@ -135,6 +135,15 @@ mixin FlutterpiCommandMixin on fl.FlutterCommand { ); } + void usesRotationArg() { + argParser.addOption( + 'rotation', + help: 'The rotation of the display in degrees. (0, 90, 180, 270)', + valueHelp: 'degrees', + allowed: ['0', '90', '180', '270'], + ); + } + (int, int)? get displaySize { final size = stringArg('display-size'); if (size == null) { @@ -232,6 +241,28 @@ mixin FlutterpiCommandMixin on fl.FlutterCommand { return remote.contains('@') ? remote.split('@').first : null; } + int? get rotation { + final rotationArg = stringArg('rotation'); + if (rotationArg == null) { + return null; + } + + switch (rotationArg) { + case '0': + return 0; + case '90': + return 90; + case '180': + return 180; + case '270': + return 270; + default: + usageException( + 'Invalid --rotation: Expected one of "0", "90", "180", or "270".', + ); + } + } + final _contextOverrides = {}; void addContextOverride(dynamic Function() fn) { diff --git a/lib/src/config.dart b/lib/src/config.dart index 704ddad..edeb598 100644 --- a/lib/src/config.dart +++ b/lib/src/config.dart @@ -13,6 +13,7 @@ class DeviceConfigEntry { this.useDummyDisplay = false, this.dummyDisplaySize, this.filesystemLayout = FilesystemLayout.flutterPi, + this.rotation, }); final String id; @@ -24,6 +25,7 @@ class DeviceConfigEntry { final bool useDummyDisplay; final (int, int)? dummyDisplaySize; final FilesystemLayout filesystemLayout; + final int? rotation; static DeviceConfigEntry fromMap(Map map) { return DeviceConfigEntry( @@ -45,6 +47,7 @@ class DeviceConfigEntry { String string => FilesystemLayout.fromString(string), _ => FilesystemLayout.flutterPi, }, + rotation: (map['rotation'] as num?)?.toInt(), ); } @@ -63,6 +66,7 @@ class DeviceConfigEntry { 'dummyDisplaySize': [width, height], if (filesystemLayout != FilesystemLayout.flutterPi) 'filesystemLayout': filesystemLayout.toString(), + if (rotation case int rotation) 'rotation': rotation, }; } @@ -82,7 +86,8 @@ class DeviceConfigEntry { devicePixelRatio == otherEntry.devicePixelRatio && useDummyDisplay == otherEntry.useDummyDisplay && dummyDisplaySize == otherEntry.dummyDisplaySize && - filesystemLayout == otherEntry.filesystemLayout; + filesystemLayout == otherEntry.filesystemLayout && + rotation == otherEntry.rotation; } @override @@ -96,6 +101,7 @@ class DeviceConfigEntry { useDummyDisplay, dummyDisplaySize, filesystemLayout, + rotation, ); @override diff --git a/lib/src/devices/flutterpi_ssh/device.dart b/lib/src/devices/flutterpi_ssh/device.dart index 14ef034..256020c 100644 --- a/lib/src/devices/flutterpi_ssh/device.dart +++ b/lib/src/devices/flutterpi_ssh/device.dart @@ -117,12 +117,21 @@ class FlutterpiArgs { this.useDummyDisplay = false, this.dummyDisplaySize, this.filesystemLayout = FilesystemLayout.flutterPi, - }); + this.rotation, + }) : assert( + rotation == null || + rotation == 0 || + rotation == 90 || + rotation == 180 || + rotation == 270, + 'Rotation must be one of: 0, 90, 180, 270 degrees if non-null.', + ); final (int, int)? explicitDisplaySizeMillimeters; final bool useDummyDisplay; final (int, int)? dummyDisplaySize; final FilesystemLayout filesystemLayout; + final int? rotation; } class FlutterpiSshDevice extends fl.Device { @@ -392,6 +401,7 @@ class FlutterpiSshDevice extends fl.Device { if (args.useDummyDisplay) '--dummy-display', if (args.dummyDisplaySize case (final width, final height)) '--dummy-display-size=$width,$height', + if (args.rotation != null) '--rotation=${args.rotation}', if (runtimeModeArg != null) runtimeModeArg, bundlePath, ...engineArgs, diff --git a/lib/src/devices/flutterpi_ssh/device_discovery.dart b/lib/src/devices/flutterpi_ssh/device_discovery.dart index 5ddfccf..1fd78f7 100644 --- a/lib/src/devices/flutterpi_ssh/device_discovery.dart +++ b/lib/src/devices/flutterpi_ssh/device_discovery.dart @@ -47,6 +47,7 @@ class FlutterpiSshDeviceDiscovery extends PollingDeviceDiscovery { useDummyDisplay: configEntry.useDummyDisplay, dummyDisplaySize: configEntry.dummyDisplaySize, filesystemLayout: configEntry.filesystemLayout, + rotation: configEntry.rotation, ), ); } From f4d88c941eb9501801bc78f8645dd0ba4db66282 Mon Sep 17 00:00:00 2001 From: TID-Test Date: Thu, 29 Jan 2026 19:00:51 +0100 Subject: [PATCH 2/5] update version --- pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 7317bfd..72e918b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: flutterpi_tool description: A tool to make development & distribution of flutter-pi apps easier. -version: 0.10.1 -repository: https://github.com/ardera/flutterpi_tool +version: 0.10.2 +repository: https://github.com/TID-Test/flutterpi_tool environment: sdk: ^3.0.5 From e964ecb5e15842f023e831b2b974d64c46e1a59c Mon Sep 17 00:00:00 2001 From: TID-Test Date: Thu, 5 Feb 2026 11:15:59 +0100 Subject: [PATCH 3/5] Update devices_test.dart --- test/commands/devices_test.dart | 179 ++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/test/commands/devices_test.dart b/test/commands/devices_test.dart index baec59d..2b2b4eb 100644 --- a/test/commands/devices_test.dart +++ b/test/commands/devices_test.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:args/command_runner.dart'; import 'package:file/memory.dart'; import 'package:file/src/interface/file_system.dart'; @@ -783,6 +784,184 @@ void main() { }); }); + group('rotation', () { + test('default (0 degrees)', () async { + var addDeviceWasCalled = false; + config + ..addDeviceFn = (entry) { + expect( + entry, + src.DeviceConfigEntry( + id: 'test-device', + sshExecutable: null, + sshRemote: 'test-device', + remoteInstallPath: null, + rotation: 0, + ), + ); + addDeviceWasCalled = true; + } + ..containsDeviceFn = (id) { + return false; + }; + + await _runInTestContext(() async { + await runner.run(['devices', 'add', 'test-device', '--rotation=0']); + }); + + expect( + addDeviceWasCalled, + isTrue, + reason: 'addDeviceFn should have been called', + ); + }); + + test('90 degrees', () async { + var addDeviceWasCalled = false; + config + ..addDeviceFn = (entry) { + expect( + entry, + src.DeviceConfigEntry( + id: 'test-device', + sshExecutable: null, + sshRemote: 'test-device', + remoteInstallPath: null, + filesystemLayout: FilesystemLayout.flutterPi, + rotation: 90, + ), + ); + addDeviceWasCalled = true; + } + ..containsDeviceFn = (id) { + return false; + }; + + await _runInTestContext(() async { + await runner.run(['devices', 'add', 'test-device', '--rotation=90']); + }); + expect( + addDeviceWasCalled, + isTrue, + reason: 'addDeviceFn should have been called', + ); + }); + + test('180 degrees', () async { + var addDeviceWasCalled = false; + config + ..addDeviceFn = (entry) { + expect( + entry, + src.DeviceConfigEntry( + id: 'test-device', + sshExecutable: null, + sshRemote: 'test-device', + remoteInstallPath: null, + filesystemLayout: FilesystemLayout.flutterPi, + rotation: 180, + ), + ); + addDeviceWasCalled = true; + } + ..containsDeviceFn = (id) { + return false; + }; + + await _runInTestContext(() async { + await runner.run(['devices', 'add', 'test-device', '--rotation=180']); + }); + + expect( + addDeviceWasCalled, + isTrue, + reason: 'addDeviceFn should have been called', + ); + }); + + test('270 degrees', () async { + var addDeviceWasCalled = false; + config + ..addDeviceFn = (entry) { + expect( + entry, + src.DeviceConfigEntry( + id: 'test-device', + sshExecutable: null, + sshRemote: 'test-device', + remoteInstallPath: null, + filesystemLayout: FilesystemLayout.flutterPi, + rotation: 270, + ), + ); + addDeviceWasCalled = true; + } + ..containsDeviceFn = (id) { + return false; + }; + + await _runInTestContext(() async { + await runner.run(['devices', 'add', 'test-device', '--rotation=270']); + }); + + expect( + addDeviceWasCalled, + isTrue, + reason: 'addDeviceFn should have been called', + ); + }); + + test('without rotation flag (should be null)', () async { + var addDeviceWasCalled = false; + config + ..addDeviceFn = (entry) { + expect( + entry, + src.DeviceConfigEntry( + id: 'test-device', + sshExecutable: null, + sshRemote: 'test-device', + remoteInstallPath: null, + filesystemLayout: FilesystemLayout.flutterPi, + rotation: null, // No rotation specified + ), + ); + addDeviceWasCalled = true; + } + ..containsDeviceFn = (id) { + return false; + }; + + await _runInTestContext(() async { + await runner.run(['devices', 'add', 'test-device']); + }); + + expect( + addDeviceWasCalled, + isTrue, + reason: 'addDeviceFn should have been called', + ); + }); + test('179 degrees (should fail - invalid value)', () async { + config + ..addDeviceFn = (entry) { + fail( + 'addDeviceFn should not have been called with invalid rotation'); + } + ..containsDeviceFn = (id) { + return false; + }; + + await _runInTestContext(() async { + expect( + () async => await runner + .run(['devices', 'add', 'test-device', '--rotation=179']), + throwsA(isA()), + ); + }); + }); + }); + group('diagnostics', () { test('attempts connecting to new device', () async { var tryConnectWasCalled = false; From 32484a2618761245162809e1e39b8daf73ee26c4 Mon Sep 17 00:00:00 2001 From: TID-Test <83397277+TID-Test@users.noreply.github.com> Date: Thu, 5 Feb 2026 16:50:07 +0100 Subject: [PATCH 4/5] Update pubspec.yaml Co-authored-by: Hannes Winkler --- pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 72e918b..7317bfd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: flutterpi_tool description: A tool to make development & distribution of flutter-pi apps easier. -version: 0.10.2 -repository: https://github.com/TID-Test/flutterpi_tool +version: 0.10.1 +repository: https://github.com/ardera/flutterpi_tool environment: sdk: ^3.0.5 From 928a94748f0f6128052b72547bf13c65d6753a15 Mon Sep 17 00:00:00 2001 From: TID-Test Date: Thu, 5 Feb 2026 16:58:59 +0100 Subject: [PATCH 5/5] add missing comma --- test/commands/devices_test.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/commands/devices_test.dart b/test/commands/devices_test.dart index 2b2b4eb..61168ac 100644 --- a/test/commands/devices_test.dart +++ b/test/commands/devices_test.dart @@ -946,7 +946,8 @@ void main() { config ..addDeviceFn = (entry) { fail( - 'addDeviceFn should not have been called with invalid rotation'); + 'addDeviceFn should not have been called with invalid rotation', + ); } ..containsDeviceFn = (id) { return false;