Files
pyramid/mockup/flutter_export
Bernd Steckmeister 25ed765a03 wip: Sicherungs-Commit aller Änderungen seit April + Arbeitsstruktur (CLAUDE.md, ROADMAP.md, PROGRESS.md, Autopilot)
6 Wochen uncommittete Arbeit (Voice-Channels, LiveKit-Manager, Settings-Modal u.v.m.)
als ein WIP-Commit gesichert, damit nichts verloren geht und der Pi den aktuellen
Stand klonen kann. Thematische Aufarbeitung: siehe ROADMAP M0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPrAGBxBT6GfPXzeWQ4AXb
2026-07-03 05:47:18 +02:00
..

Pyramid · Media Players (Flutter)

Player-Skelette im Pyramid-Stil — Audio (4 Varianten) und Video (3 Varianten) als wiederverwendbare Flutter-Widgets. Dies ist ein UI-Gerüst ohne echte Medienwiedergabeposition, duration, buffered, playing sind reine Props. Zum Anschluss an echte Medien siehe „Mit echtem Playback verbinden" weiter unten.


📁 Dateien

flutter_export/
├── pubspec.yaml
└── lib/
    ├── pyramid_theme.dart         ← Farb-, Typo-, Radius-Tokens (Single Source of Truth)
    ├── pyramid_audio_player.dart  ← PyramidAudioPlayer + AudioPlayerVariant
    ├── pyramid_video_player.dart  ← PyramidVideoPlayer + VideoPlayerVariant
    └── example_app.dart           ← Showcase mit allen Varianten (zum Reinkopieren)

Kopiere die drei pyramid_*.dart Dateien in dein Projekt (z. B. lib/widgets/players/) und importiere sie wie üblich.


🎨 Theme

Alle Player ziehen Farben/Typo aus PyramidTheme. Default ist Dark + Amber.

PyramidTheme.dark = true;              // false → Light
PyramidTheme.setAccent(42, 95, 58);    // HSL — Default Pyramid Amber
// Weitere Akzente: 280/70/60 (Violet), 160/65/50 (Mint), 0/75/60 (Coral)

Fonts: Theme erwartet Geist und GeistMono. Ohne Asset-Einbindung in pubspec.yaml fällt Flutter auf System-Sans/Mono zurück (immer noch ok). Die Font-Asset-Konfig ist in pubspec.yaml auskommentiert vorbereitet.


🎧 Audio Player

PyramidAudioPlayer(
  variant: AudioPlayerVariant.gracile,
  title: 'test track',
  artist: 'pyramid · demo',
  duration: const Duration(minutes: 3, seconds: 24),
  position: _position,                       // ValueNotifier oder State
  playing: _playing,
  onTogglePlay: () => setState(() => _playing = !_playing),
  onSeek: (Duration to) => setState(() => _position = to),
  onSkipBack: () { /* 15s */ },
  onSkipForward: () { /* +15s */ },
  onSpeedChange: (double s) { /* 1.0 / 1.5 / 2.0 */ },
)

Wann welche Variante?

Variante Wann benutzen Größe
AudioPlayerVariant.defaultBars Standard-Audio-Anhang in einer Nachricht. Robuste Bar-Waveform — wirkt „solide", für Musik-Snippets. ≥ 380px breit
AudioPlayerVariant.gracile Empfehlung für Voice-Notes und längere Audio-Dateien. Filigrane Hairline-Waveform — passt am besten zum Pyramid-Stil. ≥ 380px breit
AudioPlayerVariant.compact In-Chat Inline-Embed, wenn Platz knapp ist (Reply-Quote, Thread-Vorschau, Side-Panel). Eine Zeile. ≥ 320px breit
AudioPlayerVariant.mono Spezielle Räume mit Terminal-/Editorial-Identität (z. B. #announce, „archive", Devs-Spaces). Mono-Font, harte Kanten. Sparsam einsetzen. ≥ 380px breit

🎬 Video Player

PyramidVideoPlayer(
  variant: VideoPlayerVariant.defaultHover,
  title: 'test clip',
  subtitle: 'pyramid · 1080p',
  duration: const Duration(minutes: 4, seconds: 12),
  position: _videoPos,
  buffered: 0.65,                            // 0..1
  playing: _playing,
  onTogglePlay: () => setState(() => _playing = !_playing),
  onSeek: (Duration to) => setState(() => _videoPos = to),
  onSpeedChange: (double s) { /* 0.5 / 1 / 1.25 / 1.5 / 2 */ },
  onQualityChange: (String q) { /* Auto / 1080p / 720p / 480p / 360p */ },
  onFullscreen: () { /* Navigator push fullscreen route */ },
)

Wann welche Variante?

Variante Wann benutzen Aspect Chrome
VideoPlayerVariant.defaultHover Standard-Video-Embed im Chat. Player nimmt volle Breite ein, Controls erscheinen bei Hover/Tap. 16:10 hover/auto-hide
VideoPlayerVariant.minimal Inline-Vorschau in dichten Layouts (Reply, Thread-Liste). Controls immer sichtbar, kompakter. 16:9 always-on
VideoPlayerVariant.framed Galerie-/Karten-Layouts oder Posts mit Caption darunter. Stage in Card, Controls auf hellem Card-BG außerhalb des schwarzen Stages. 16:9 + Card always-on (Card)

🔌 Mit echtem Playback verbinden

Diese Widgets sind reine UI-Mockups. Für echte Wiedergabe binde z. B. das offizielle video_player (Video) oder just_audio (Audio) Package an:

// Video-Beispiel mit video_player
final controller = VideoPlayerController.networkUrl(Uri.parse(url))..initialize();
controller.addListener(() => setState(() {}));

// im build():
PyramidVideoPlayer(
  variant: VideoPlayerVariant.defaultHover,
  title: 'remote clip',
  duration: controller.value.duration,
  position: controller.value.position,
  buffered: controller.value.buffered.isEmpty ? 0
      : controller.value.buffered.last.end.inMilliseconds /
        controller.value.duration.inMilliseconds,
  playing: controller.value.isPlaying,
  onTogglePlay: () => controller.value.isPlaying ? controller.pause() : controller.play(),
  onSeek: (to) => controller.seekTo(to),
  onSpeedChange: (s) => controller.setPlaybackSpeed(s),
)

Stage rendern: Aktuell zeigt der Player einen Pyramid-Triangle als Platzhalter. Ersetze in pyramid_video_player.dart die _stage()-Methode durch dein VideoPlayer(controller) Widget.


🧪 Showcase

example_app.dart zeigt alle 7 Player-Varianten in einem scrollbaren Dark-Layout. Direkt ausführbar via flutter run -t lib/example_app.dart.


Checklist beim Einbau

  • pyramid_theme.dart + Audio + Video kopiert
  • PyramidTheme.dark und setAccent(...) in App-State gesetzt
  • (optional) Geist + Geist Mono in pubspec.yaml eingebunden
  • Player mit echtem Audio-/Video-Controller verdrahtet
  • Skip-Intervall an Produkt-Konvention angepasst (Material hat replay_10 / forward_10 — wenn du wirklich ±15s willst, die Icons mit Stack(text) ersetzen)