Chepuhagram/lib/presentation/screens/media_preview_screen.dart

236 lines
7.0 KiB
Dart

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
import 'package:flutter/services.dart';
import 'dart:math';
import 'package:open_filex/open_filex.dart';
class MediaPreviewScreen extends StatefulWidget {
final String path;
final bool isVideo;
const MediaPreviewScreen({
super.key,
required this.path,
required this.isVideo,
});
@override
State<MediaPreviewScreen> createState() => _MediaPreviewScreenState();
}
class _MediaPreviewScreenState extends State<MediaPreviewScreen> {
VideoPlayerController? _videoController;
bool _isPlaying = true;
String? _videoInitError;
@override
void initState() {
super.initState();
if (widget.isVideo) {
_videoController = VideoPlayerController.file(File(widget.path))
..initialize()
.then((_) {
_videoInitError = null;
if (!mounted) return;
setState(() {});
_videoController!.setLooping(false);
_videoController!.play();
})
.catchError((e) {
_videoInitError = e.toString();
_videoController?.dispose().catchError((_) {});
_videoController = null;
if (mounted) setState(() {});
});
_videoController!.addListener(() {
if (mounted) setState(() {});
});
}
}
@override
void dispose() {
_videoController?.dispose();
super.dispose();
}
String _formatDuration(Duration d) {
String two(int n) => n.toString().padLeft(2, '0');
final m = two(d.inMinutes.remainder(60));
final s = two(d.inSeconds.remainder(60));
return "$m:$s";
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: null,
body: Stack(
children: [
// MEDIA
Center(
child: widget.isVideo
? (_videoInitError != null)
? _buildVideoInitErrorFallback()
: (_videoController != null &&
_videoController!.value.isInitialized)
? Stack(
alignment: Alignment.center,
children: [
AspectRatio(
aspectRatio: _videoController!.value.aspectRatio,
child: VideoPlayer(_videoController!),
),
// overlay controls
Positioned(
bottom: 40,
left: 16,
right: 16,
child: _buildVideoControls(),
),
],
)
: const CircularProgressIndicator()
: Image.file(File(widget.path)),
),
// BOTTOM ACTIONS (как Telegram)
Positioned(
bottom: 40,
left: 16,
right: 16,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// переснять
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white10,
),
onPressed: () {
Navigator.pop(context, false);
},
icon: const Icon(Icons.refresh),
label: const Text("Переснять"),
),
// отправить
ElevatedButton.icon(
onPressed: () {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pop(context, true);
});
},
icon: const Icon(Icons.send),
label: const Text("Отправить"),
),
],
),
),
],
),
);
}
Widget _buildVideoControls() {
final c = _videoController!;
final duration = c.value.duration;
final position = c.value.position;
final posMs = position.inMilliseconds.toDouble();
final maxMs = duration.inMilliseconds
.toDouble()
.clamp(1, double.infinity)
.toDouble();
return Container(
child: Row(
children: [
// ▶️ / ⏸ слева
IconButton(
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
icon: Icon(
c.value.isPlaying ? Icons.pause : Icons.play_arrow,
color: Colors.white,
size: 26,
),
onPressed: () {
setState(() {
if (c.value.isPlaying) {
c.pause();
_isPlaying = false;
} else {
c.play();
_isPlaying = true;
}
});
},
),
Expanded(
child: SliderTheme(
data: SliderTheme.of(context).copyWith(
trackHeight: 2, // ТОНКИЙ как в Telegram
activeTrackColor: Colors.white,
inactiveTrackColor: Colors.white24,
thumbColor: Colors.white,
overlayColor: Colors.transparent,
thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 5),
),
child: Slider(
value: posMs.clamp(0, maxMs).toDouble(),
min: 0,
max: maxMs,
onChanged: (v) {
c.seekTo(Duration(milliseconds: v.toInt()));
},
),
),
),
const SizedBox(width: 8),
Text(
"${_formatDuration(position)} / ${_formatDuration(duration)}",
style: const TextStyle(color: Colors.white, fontSize: 12),
),
],
),
);
}
Widget _buildVideoInitErrorFallback() {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.play_disabled, color: Colors.white70, size: 48),
const SizedBox(height: 10),
const Text(
'Видео не воспроизводится на этом устройстве',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white70),
),
const SizedBox(height: 10),
OutlinedButton.icon(
onPressed: () async {
try {
await OpenFilex.open(widget.path);
} catch (_) {}
},
icon: const Icon(Icons.open_in_new, color: Colors.white70),
label: const Text(
'Открыть внешним плеером',
style: TextStyle(color: Colors.white70),
),
),
],
);
}
}