Add Flutter project skeleton (Android-only MVP)

Five-layer architecture per DMC_Architektur_und_Design.md: FlightControllerLink
abstraction (Mock + MSP stub), protocol-neutral mission domain layer, App-Mode
state machine with Ready-to-Fly gate, Drift persistence schema, and placeholder
UI screens wired via go_router. App icon generated from design/Y2_pink_dark.svg.
Verified with flutter analyze and flutter test.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Constantin Leue
2026-07-26 21:28:25 +02:00
co-authored by Claude Sonnet 5
parent 62d655fe8e
commit 346fcc2183
45 changed files with 2884 additions and 0 deletions
+65
View File
@@ -0,0 +1,65 @@
import 'package:flutter_bloc/flutter_bloc.dart';
/// Explizite Zustandsfolge Plan -> Connect -> Upload -> Fly
/// (Architektur-Doku 2.2).
enum AppMode { plan, connect, upload, fly }
class AppModeState {
const AppModeState({
this.mode = AppMode.plan,
this.missionUploaded = false,
this.missionVerified = false,
this.droneArmed = false,
});
final AppMode mode;
final bool missionUploaded;
final bool missionVerified;
final bool droneArmed;
/// Ready-to-Fly-Gate (Doku 2.2 / 4.5): Upload + verifiziert + disarmed.
/// Muss technisch erzwungen werden, nicht nur als UI-Konvention gelten.
bool get readyToFly => missionUploaded && missionVerified && !droneArmed;
AppModeState copyWith({
AppMode? mode,
bool? missionUploaded,
bool? missionVerified,
bool? droneArmed,
}) {
return AppModeState(
mode: mode ?? this.mode,
missionUploaded: missionUploaded ?? this.missionUploaded,
missionVerified: missionVerified ?? this.missionVerified,
droneArmed: droneArmed ?? this.droneArmed,
);
}
}
class AppModeCubit extends Cubit<AppModeState> {
AppModeCubit() : super(const AppModeState());
void toConnect() => emit(state.copyWith(mode: AppMode.connect));
void toUpload() => emit(state.copyWith(mode: AppMode.upload));
void missionUploadConfirmed() =>
emit(state.copyWith(missionUploaded: true));
void missionVerifiedConfirmed() =>
emit(state.copyWith(missionVerified: true));
void droneArmedChanged(bool armed) =>
emit(state.copyWith(droneArmed: armed));
/// Wirft, wenn das Ready-to-Fly-Gate nicht erfuellt ist (Doku 4.5).
void toFly() {
if (!state.readyToFly) {
throw StateError('Ready-to-Fly-Gate nicht erfuellt.');
}
emit(state.copyWith(mode: AppMode.fly));
}
/// Verlassen des Fly-Modus setzt den kompletten Handover-Status zurueck.
void toPlan() => emit(const AppModeState());
}