Files
pyramid/integration_test/call_bubbles_demo_test.dart
Bernd SteckmeisterandClaude Opus 5.5 ab84701e73 feat(call): aufgeblasene Formen statt runder Bubbles (Bernds Referenzbild)
- Neues Layout lib/features/call_ui/inflated_layout.dart: gewichtetes
  Voronoi-/Potenzdiagramm im Anrufbereich, Zellen fuellen das Fenster bis an
  die Raender, an Nachbarn plattgedrueckt, weich gerundete Ecken;
  Lloyd-Relaxation + sanfter Zug ins Raster (aufgeraeumt), Flaechen per
  Gewicht nachgefuehrt (Sprecher 1,7-fach), Neue pumpen sich auf, Gehende
  schrumpfen weg, je mehr Personen desto kleiner
- Darstellung nur als Kontur mit weichem Innenleuchten, Profilbild mit
  Kontur + Name in der Mitte, Sprecher gruen pulsierend, Kamera-Bild
  fuellt die Form
- Tests: test/inflated_layout_test.dart (7), Vorschau-/Animationsbilder
  integration_test/call_bubbles_demo_test.dart; alte Kugel-Physik entfernt
- Version 0.1.43

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 14:54:45 +02:00

112 lines
3.9 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:io';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:pyramid/core/theme.dart';
import 'package:pyramid/features/call_ui/voice_channel.dart' show CallBubblesDemo;
/// Vorschaubilder der Anrufansicht mit erfundenen Teilnehmern (ohne Login):
/// Standbilder für 1–7 Personen (Handy + PC) und eine Bildfolge der
/// Animation (Sprecherwechsel, jemand kommt dazu, jemand geht) für ein GIF.
/// Bilder: build/call_demo/.
///
/// flutter test integration_test/call_bubbles_demo_test.dart -d windows
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('Anruf-Formen: Vorschaubilder', (tester) async {
final count = ValueNotifier(5);
final speaking = ValueNotifier(2);
void phone() => tester.view
..physicalSize = const Size(1080, 2340)
..devicePixelRatio = 2.625;
void desktop() => tester.view
..physicalSize = const Size(1600, 1000)
..devicePixelRatio = 1.0;
addTearDown(tester.view.reset);
Future<void> smooth(Duration d) async {
final end = DateTime.now().add(d);
while (DateTime.now().isBefore(end)) {
await tester.pump(const Duration(milliseconds: 16));
}
}
phone();
await tester.pumpWidget(PyramidTheme(
child: MaterialApp(
debugShowCheckedModeBanner: false,
theme: buildPyramidThemeData(ThemeVariant.soft),
home: ValueListenableBuilder<int>(
valueListenable: count,
builder: (_, n, _) => ValueListenableBuilder<int>(
valueListenable: speaking,
builder: (_, s, _) => CallBubblesDemo(count: n, speaking: s),
),
),
),
));
// ── Standbilder ──
for (final n in [1, 2, 3, 5, 7]) {
count.value = n;
speaking.value = n >= 3 ? 2 : -1;
phone();
await smooth(const Duration(milliseconds: 2500));
await _shot(tester, 'p_${n}_personen');
desktop();
await smooth(const Duration(milliseconds: 2500));
await _shot(tester, 'd_${n}_personen');
}
// ── Animation (Handy) ──
phone();
count.value = 4;
speaking.value = -1;
await smooth(const Duration(seconds: 3));
var frame = 0;
Future<void> record(Duration d) async {
final end = DateTime.now().add(d);
while (DateTime.now().isBefore(end)) {
await tester.pump(const Duration(milliseconds: 16));
await _shot(tester, 'anim/f${(frame++).toString().padLeft(4, '0')}',
scale: 0.4, log: false);
}
}
await record(const Duration(milliseconds: 600));
speaking.value = 1; // Anna spricht
await record(const Duration(milliseconds: 1600));
speaking.value = 3; // Clara spricht
await record(const Duration(milliseconds: 1600));
speaking.value = -1;
count.value = 5; // David kommt dazu
await record(const Duration(milliseconds: 1800));
count.value = 4; // David geht wieder
await record(const Duration(milliseconds: 1800));
// ignore: avoid_print
print('Animation: $frame Bilder');
}, timeout: const Timeout(Duration(minutes: 6)));
}
Future<void> _shot(WidgetTester tester, String name,
{double scale = 1, bool log = true}) async {
final renderView = tester.binding.renderViews.first;
final layer = renderView.debugLayer! as OffsetLayer;
final physical = tester.view.physicalSize;
final image = await tester.binding.runAsync(
() => layer.toImage(Offset.zero & physical, pixelRatio: scale),
);
final bytes = await tester.binding.runAsync(
() => image!.toByteData(format: ui.ImageByteFormat.png),
);
final file = File('build/call_demo/$name.png');
file.parent.createSync(recursive: true);
file.writeAsBytesSync(bytes!.buffer.asUint8List());
// ignore: avoid_print
if (log) print('Bild $name');
}