fixed drone status menue grid view bug. fields are shown again
This commit is contained in:
@@ -127,11 +127,12 @@ class _DroneStatusWarningsPanelState extends ConsumerState<DroneStatusWarningsPa
|
|||||||
);
|
);
|
||||||
return ListView(
|
return ListView(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
children: [
|
children: _statusGrid([
|
||||||
// Farbcodierte Felder zuerst (Doku: "drone status felder im
|
// Farbcodierte Felder zuerst (Doku: "drone status felder im
|
||||||
// menue auch farblich kodieren", Schwellwerte je Drohnenprofil -
|
// menue auch farblich kodieren", Schwellwerte je Drohnenprofil -
|
||||||
// siehe computeTelemetryFieldStatus), informelle Felder ohne
|
// siehe computeTelemetryFieldStatus), informelle Felder ohne
|
||||||
// Ampel-Bewertung (Koordinaten/Heading/Alt/Speed) danach.
|
// Ampel-Bewertung (Koordinaten/Heading/Alt/Speed) danach. Je 2
|
||||||
|
// Felder pro Zeile (Doku: "platz effizienter nutzen").
|
||||||
_statusRow('GPS satellites', '${frame.numSat}', level: fieldStatus.numSat),
|
_statusRow('GPS satellites', '${frame.numSat}', level: fieldStatus.numSat),
|
||||||
_statusRow(
|
_statusRow(
|
||||||
'GPS precision (HDOP)',
|
'GPS precision (HDOP)',
|
||||||
@@ -159,7 +160,7 @@ class _DroneStatusWarningsPanelState extends ConsumerState<DroneStatusWarningsPa
|
|||||||
_statusRow('Heading', '${frame.headingDeg.round()}°'),
|
_statusRow('Heading', '${frame.headingDeg.round()}°'),
|
||||||
_statusRow('Altitude', '${frame.altitudeM.toStringAsFixed(0)} m'),
|
_statusRow('Altitude', '${frame.altitudeM.toStringAsFixed(0)} m'),
|
||||||
_statusRow('Speed', '${frame.speedMs.toStringAsFixed(1)} m/s'),
|
_statusRow('Speed', '${frame.speedMs.toStringAsFixed(1)} m/s'),
|
||||||
],
|
]),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
loading: () => const Center(
|
loading: () => const Center(
|
||||||
@@ -171,33 +172,81 @@ class _DroneStatusWarningsPanelState extends ConsumerState<DroneStatusWarningsPa
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ordnet die Statuszeilen zu je 2 pro Zeile an (Doku: "2 felder pro zeile
|
||||||
|
/// im drone status menue erlauben um platz effizienter zu nutzen") - bei
|
||||||
|
/// ungerader Feldanzahl bleibt die letzte Zeile einspaltig statt sie
|
||||||
|
/// kuenstlich zu strecken.
|
||||||
|
List<Widget> _statusGrid(List<Widget> tiles) {
|
||||||
|
final rows = <Widget>[];
|
||||||
|
for (var i = 0; i < tiles.length; i += 2) {
|
||||||
|
final hasPair = i + 1 < tiles.length;
|
||||||
|
// Kein CrossAxisAlignment.stretch: diese Row sitzt direkt als
|
||||||
|
// ListView-Kind, das seinen Kindern unbegrenzte Hoehe gibt (0<=h<=
|
||||||
|
// Infinity) - stretch verlangt aber eine endliche Hoehe, um die Kacheln
|
||||||
|
// darauf zu strecken, und wirft sonst "BoxConstraints forces an
|
||||||
|
// infinite height" (fuehrte zum komplett schwarzen Status-Tab). Default
|
||||||
|
// (center) reicht, da beide Kacheln einer Zeile ohnehin fast gleich
|
||||||
|
// hohen Inhalt haben.
|
||||||
|
rows.add(Row(
|
||||||
|
children: [
|
||||||
|
Expanded(child: tiles[i]),
|
||||||
|
if (hasPair) ...[
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(child: tiles[i + 1]),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
static const _signalColors = {
|
static const _signalColors = {
|
||||||
SignalLevel.green: DmcColors.flyModeColor,
|
SignalLevel.green: DmcColors.flyModeColor,
|
||||||
SignalLevel.yellow: DmcColors.pafAmber,
|
SignalLevel.yellow: DmcColors.pafAmber,
|
||||||
SignalLevel.red: DmcColors.warnRed,
|
SignalLevel.red: DmcColors.warnRed,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Label ueber Wert statt nebeneinander (Doku: "2 felder pro zeile") - bei
|
||||||
|
// halber Zeilenbreite kollidiert ein nebeneinander angeordnetes Label wie
|
||||||
|
// "GPS precision (HDOP)" sonst mit dem Wert; gestapelt bleibt auch bei
|
||||||
|
// laengeren Labeln/Werten genug Platz.
|
||||||
Widget _statusRow(String label, String value, {SignalLevel? level}) {
|
Widget _statusRow(String label, String value, {SignalLevel? level}) {
|
||||||
final valueColor = level != null ? _signalColors[level]! : Colors.white;
|
final valueColor = level != null ? _signalColors[level]! : Colors.white;
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.symmetric(vertical: 4),
|
margin: const EdgeInsets.symmetric(vertical: 4),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white.withValues(alpha: 0.04),
|
color: Colors.white.withValues(alpha: 0.04),
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(color: Colors.white70, fontSize: 12),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: Text(label, style: const TextStyle(color: Colors.white70))),
|
|
||||||
if (level != null) ...[
|
if (level != null) ...[
|
||||||
Container(
|
Container(
|
||||||
width: 8,
|
width: 8,
|
||||||
height: 8,
|
height: 8,
|
||||||
margin: const EdgeInsets.only(right: 8),
|
margin: const EdgeInsets.only(right: 6),
|
||||||
decoration: BoxDecoration(color: valueColor, shape: BoxShape.circle),
|
decoration: BoxDecoration(color: valueColor, shape: BoxShape.circle),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
Text(value, style: TextStyle(color: valueColor, fontWeight: FontWeight.w600)),
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
value,
|
||||||
|
style: TextStyle(color: valueColor, fontWeight: FontWeight.w600),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
+1
-1
@@ -878,7 +878,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.2"
|
version: "1.10.2"
|
||||||
sqlite3:
|
sqlite3:
|
||||||
dependency: transitive
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: sqlite3
|
name: sqlite3
|
||||||
sha256: c73fd75df1332d76a6257f4823ae4df9c791f522b97e4a60cbcad214de1becf4
|
sha256: c73fd75df1332d76a6257f4823ae4df9c791f522b97e4a60cbcad214de1becf4
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ dependencies:
|
|||||||
wakelock_plus: ^1.7.0
|
wakelock_plus: ^1.7.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
sqlite3: ^3.5.0
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:drift/native.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:sqlite3/sqlite3.dart' as sqlite3;
|
||||||
|
import 'package:dmc_app/services/database/app_database.dart';
|
||||||
|
import 'package:dmc_app/services/database/drone_profile_repository.dart';
|
||||||
|
|
||||||
|
/// Reproduziert das Upgrade eines echten, bereits laufenden Geraets (Schema
|
||||||
|
/// v7, DroneProfiles ohne die neuen Ampel-Schwellwert-Spalten) auf das
|
||||||
|
/// aktuelle Schema (v8): baut die v7-Tabellenstruktur samt einer
|
||||||
|
/// Bestandszeile roh per sqlite3 auf (wie auf einem Geraet, das die App vor
|
||||||
|
/// diesem Update installiert hatte), oeffnet die Datei dann ganz normal ueber
|
||||||
|
/// AppDatabase und prueft, dass die Migration ohne Exception durchlaeuft und
|
||||||
|
/// die neuen Spalten mit den erwarteten Default-Werten befuellt werden.
|
||||||
|
void main() {
|
||||||
|
test('Upgrade von Schema v7 auf v8 befuellt neue Spalten mit Defaults', () async {
|
||||||
|
final dir = await Directory.systemTemp.createTemp('dmc_migration_test');
|
||||||
|
final file = File('${dir.path}/test.sqlite');
|
||||||
|
|
||||||
|
final raw = sqlite3.sqlite3.open(file.path);
|
||||||
|
raw.execute('''
|
||||||
|
CREATE TABLE missions (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
location TEXT,
|
||||||
|
created_at INTEGER NOT NULL,
|
||||||
|
updated_at INTEGER NOT NULL,
|
||||||
|
waypoints_json TEXT NOT NULL,
|
||||||
|
is_dirty INTEGER NOT NULL DEFAULT 1,
|
||||||
|
server_id TEXT,
|
||||||
|
terrain_profile_json TEXT
|
||||||
|
);
|
||||||
|
CREATE TABLE drone_profiles (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
min_speed REAL NOT NULL,
|
||||||
|
cruise_speed REAL NOT NULL,
|
||||||
|
max_speed REAL NOT NULL,
|
||||||
|
min_turn_radius REAL NOT NULL,
|
||||||
|
loiter_radius REAL NOT NULL,
|
||||||
|
catch_radius REAL NOT NULL,
|
||||||
|
max_climb_rate REAL NOT NULL,
|
||||||
|
max_descent_rate REAL NOT NULL,
|
||||||
|
max_range_m REAL NOT NULL,
|
||||||
|
max_endurance_min REAL NOT NULL,
|
||||||
|
max_altitude_m REAL NOT NULL
|
||||||
|
);
|
||||||
|
CREATE TABLE app_settings_table (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
active_drone_profile_id INTEGER,
|
||||||
|
current_mission_id INTEGER,
|
||||||
|
connection_device_address TEXT,
|
||||||
|
connection_auto_connect INTEGER NOT NULL DEFAULT 1,
|
||||||
|
connection_type TEXT NOT NULL DEFAULT 'wifi',
|
||||||
|
connection_wifi_host TEXT,
|
||||||
|
connection_wifi_port INTEGER NOT NULL DEFAULT 14550,
|
||||||
|
connection_wifi_ssid_prefix TEXT NOT NULL DEFAULT 'mLRS-',
|
||||||
|
connection_wifi_last_ssid TEXT
|
||||||
|
);
|
||||||
|
INSERT INTO drone_profiles (
|
||||||
|
id, name, min_speed, cruise_speed, max_speed, min_turn_radius,
|
||||||
|
loiter_radius, catch_radius, max_climb_rate, max_descent_rate,
|
||||||
|
max_range_m, max_endurance_min, max_altitude_m
|
||||||
|
) VALUES (
|
||||||
|
1, 'T1 Ranger', 13, 15, 25, 35, 100, 60, 5, 6, 15000, 45, 2000
|
||||||
|
);
|
||||||
|
PRAGMA user_version = 7;
|
||||||
|
''');
|
||||||
|
raw.close();
|
||||||
|
|
||||||
|
final db = AppDatabase.forTesting(NativeDatabase(file));
|
||||||
|
final repo = DroneProfileRepository(db);
|
||||||
|
|
||||||
|
final all = await repo.loadAll();
|
||||||
|
expect(all, hasLength(1));
|
||||||
|
expect(all.first.name, 'T1 Ranger');
|
||||||
|
expect(all.first.gpsSatGreenMin, 8.0);
|
||||||
|
expect(all.first.batteryVoltageRedMinV, 19.2);
|
||||||
|
|
||||||
|
await db.close();
|
||||||
|
await dir.delete(recursive: true);
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user