Pipeline YouTube con relay, overlay e publisher sync lato backend; fix GL pauseGlDuringRotation su Android per evitare crash in rotazione durante lo streaming Flutter. Co-authored-by: Cursor <cursoragent@cursor.com>
104 lines
2.8 KiB
Dart
104 lines
2.8 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
|
|
/// Preview camera nativa (OpenGL) integrata nel layout Flutter.
|
|
class NativeStreamingPreview extends StatelessWidget {
|
|
const NativeStreamingPreview({
|
|
super.key,
|
|
this.showGrid = true,
|
|
this.platformLabel = 'LIVE',
|
|
});
|
|
|
|
final bool showGrid;
|
|
final String platformLabel;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
if (defaultTargetPlatform == TargetPlatform.android) {
|
|
return Stack(
|
|
fit: StackFit.expand,
|
|
children: [
|
|
AndroidView(
|
|
key: key,
|
|
viewType: 'match_live_tv/streaming_preview',
|
|
layoutDirection: TextDirection.ltr,
|
|
creationParamsCodec: const StandardMessageCodec(),
|
|
),
|
|
if (showGrid) CameraPreviewOverlay(platformLabel: platformLabel),
|
|
],
|
|
);
|
|
}
|
|
return CameraPreviewPlaceholder(showGrid: showGrid);
|
|
}
|
|
}
|
|
|
|
class CameraPreviewPlaceholder extends StatelessWidget {
|
|
const CameraPreviewPlaceholder({super.key, this.showGrid = true});
|
|
|
|
final bool showGrid;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
color: Colors.black,
|
|
child: const Center(
|
|
child: Icon(Icons.videocam, size: 64, color: Colors.white24),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Overlay REC / badge sopra la preview nativa.
|
|
class CameraPreviewOverlay extends StatelessWidget {
|
|
const CameraPreviewOverlay({super.key, this.platformLabel = 'LIVE'});
|
|
|
|
final String platformLabel;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final inset = MediaQuery.viewPaddingOf(context);
|
|
const gap = 8.0;
|
|
|
|
return Stack(
|
|
fit: StackFit.expand,
|
|
children: [
|
|
Positioned(
|
|
top: inset.top + gap,
|
|
left: inset.left + gap,
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: Colors.red,
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
child: const Text(
|
|
'REC',
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 11,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
Positioned(
|
|
top: inset.top + gap,
|
|
right: inset.right + gap,
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: Colors.black54,
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
child: Text(
|
|
platformLabel,
|
|
style: const TextStyle(color: Colors.white, fontSize: 11),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|