post_enrollment_app/lib/pages/service/multi_video_player.dart
2025-12-28 11:26:50 +05:30

561 lines
18 KiB
Dart
Executable File

import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
class NhanceMultiVideoWrapper extends StatefulWidget {
final List<String> videoUrls;
const NhanceMultiVideoWrapper({
super.key,
required this.videoUrls,
});
@override
State<NhanceMultiVideoWrapper> createState() =>
_NhanceMultiVideoWrapperState();
}
class _NhanceMultiVideoWrapperState extends State<NhanceMultiVideoWrapper> {
final PageController _pageController = PageController();
final List<VideoPlayerController> _controllers = [];
int _currentIndex = 0;
bool _isMuted = false;
/// Optional static cover image
final String coverImage =
"https://picsum.photos/900/500"; // replace with API thumbnail if available
@override
void initState() {
super.initState();
_initControllers();
}
Future<void> _initControllers() async {
for (final url in widget.videoUrls) {
final controller = VideoPlayerController.networkUrl(Uri.parse(url));
await controller.initialize();
controller.setLooping(true);
controller.setVolume(1);
_controllers.add(controller);
}
if (mounted) setState(() {});
}
@override
void dispose() {
for (final c in _controllers) {
c.dispose();
}
_pageController.dispose();
super.dispose();
}
void _next() {
if (_currentIndex < _controllers.length - 1) {
_pageController.nextPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
}
void _previous() {
if (_currentIndex > 0) {
_pageController.previousPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
}
void _seek(VideoPlayerController c, int seconds) {
final pos = c.value.position + Duration(seconds: seconds);
c.seekTo(
pos < Duration.zero ? Duration.zero : pos,
);
}
void _toggleMute(VideoPlayerController c) {
setState(() {
_isMuted = !_isMuted;
c.setVolume(_isMuted ? 0 : 1);
});
}
void _openFullscreen(VideoPlayerController controller) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
backgroundColor: Colors.black,
body: Center(
child: AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: VideoPlayer(controller),
),
),
),
),
);
}
@override
Widget build(BuildContext context) {
if (_controllers.isEmpty) {
return const SizedBox(
height: 250,
child: Center(child: CircularProgressIndicator()),
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// const Text(
// "Videos",
// style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
// ),
// const SizedBox(height: 10),
SizedBox(
height: kIsWeb ? 340 : 240,
child: Stack(
alignment: Alignment.center,
children: [
PageView.builder(
controller: _pageController,
itemCount: _controllers.length,
onPageChanged: (index) {
setState(() {
_controllers[_currentIndex].pause();
_currentIndex = index;
});
},
itemBuilder: (context, index) {
final controller = _controllers[index];
return Center(
child: SizedBox(
width: kIsWeb ? 520 : double.infinity,
child: AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: Stack(
alignment: Alignment.center,
children: [
/// 🎥 VIDEO
VideoPlayer(controller),
/// 🖼 COVER IMAGE
if (!controller.value.isPlaying)
// Positioned.fill(
// child: Image.network(
// coverImage,
// fit: BoxFit.cover,
// ),
// ),
/// ▶ PLAY / PAUSE
IconButton(
iconSize: 64,
icon: Icon(
controller.value.isPlaying
? Icons.pause_circle
: Icons.play_circle,
color: Colors.white,
),
onPressed: () {
setState(() {
controller.value.isPlaying
? controller.pause()
: controller.play();
});
},
),
/// 🎛 BOTTOM CONTROLS
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 6),
color: Colors.black.withOpacity(0.55),
child: Row(
children: [
IconButton(
icon: const Icon(Icons.replay_10,
color: Colors.white),
onPressed: () =>
_seek(controller, -10),
),
IconButton(
icon: const Icon(Icons.forward_10,
color: Colors.white),
onPressed: () =>
_seek(controller, 10),
),
IconButton(
icon: Icon(
_isMuted
? Icons.volume_off
: Icons.volume_up,
color: Colors.white,
),
onPressed: () =>
_toggleMute(controller),
),
Expanded(
child: VideoProgressIndicator(
controller,
allowScrubbing: true,
colors: VideoProgressColors(
playedColor:
const Color(0xFF593AFF),
),
),
),
IconButton(
icon: const Icon(Icons.fullscreen,
color: Colors.white),
onPressed: () =>
_openFullscreen(controller),
),
],
),
),
),
],
),
),
),
);
},
),
/// ⬅ PREVIOUS (WEB)
if (kIsWeb)
Positioned(
left: 12,
child: _ArrowButton(
icon: Icons.chevron_left,
enabled: _currentIndex > 0,
onTap: _previous,
),
),
/// ➡ NEXT (WEB)
if (kIsWeb)
Positioned(
right: 12,
child: _ArrowButton(
icon: Icons.chevron_right,
enabled: _currentIndex < _controllers.length - 1,
onTap: _next,
),
),
],
),
),
const SizedBox(height: 8),
/// 🔘 DOT INDICATOR
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(
_controllers.length,
(i) => AnimatedContainer(
duration: const Duration(milliseconds: 250),
margin: const EdgeInsets.symmetric(horizontal: 4),
height: 8,
width: _currentIndex == i ? 20 : 8,
decoration: BoxDecoration(
color: _currentIndex == i
? const Color(0xFF593AFF)
: Colors.grey.shade400,
borderRadius: BorderRadius.circular(4),
),
),
),
),
],
);
}
}
/// 🔘 Arrow Button
class _ArrowButton extends StatelessWidget {
final IconData icon;
final bool enabled;
final VoidCallback onTap;
const _ArrowButton({
required this.icon,
required this.enabled,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return IgnorePointer(
ignoring: !enabled,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 200),
opacity: enabled ? 1 : 0.3,
child: Material(
color: Colors.black.withOpacity(0.6),
shape: const CircleBorder(),
elevation: 4,
child: InkWell(
customBorder: const CircleBorder(),
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(10),
child: Icon(icon, size: 36, color: Colors.white),
),
),
),
),
);
}
}
// import 'package:flutter/foundation.dart';
// import 'package:flutter/material.dart';
// import 'package:video_player/video_player.dart';
//
// class NhanceMultiVideoWrapper extends StatefulWidget {
// final List<String> videoUrls;
//
// const NhanceMultiVideoWrapper({
// super.key,
// required this.videoUrls,
// });
//
// @override
// State<NhanceMultiVideoWrapper> createState() =>
// _NhanceMultiVideoWrapperState();
// }
//
// class _NhanceMultiVideoWrapperState extends State<NhanceMultiVideoWrapper> {
// final PageController _pageController = PageController();
// int _currentIndex = 0;
//
// final List<VideoPlayerController> _controllers = [];
//
// @override
// void initState() {
// super.initState();
// _initControllers();
// }
//
// Future<void> _initControllers() async {
// for (final url in widget.videoUrls) {
// final controller = VideoPlayerController.networkUrl(Uri.parse(url));
// await controller.initialize();
// controller.setLooping(true);
// controller.setVolume(1);
// _controllers.add(controller);
// }
//
// if (mounted) setState(() {});
// }
//
// @override
// void dispose() {
// for (final c in _controllers) {
// c.dispose();
// }
// _pageController.dispose();
// super.dispose();
// }
//
// void _next() {
// if (_currentIndex < widget.videoUrls.length - 1) {
// _pageController.nextPage(
// duration: const Duration(milliseconds: 300),
// curve: Curves.easeInOut,
// );
// }
// }
//
// void _previous() {
// if (_currentIndex > 0) {
// _pageController.previousPage(
// duration: const Duration(milliseconds: 300),
// curve: Curves.easeInOut,
// );
// }
// }
//
// @override
// Widget build(BuildContext context) {
// if (_controllers.isEmpty) {
// return const SizedBox(
// height: 250,
// child: Center(child: CircularProgressIndicator()),
// );
// }
//
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// // Text(
// // "Videos",
// // style: Theme.of(context)
// // .textTheme
// // .titleMedium
// // ?.copyWith(fontWeight: FontWeight.w600),
// // ),
// // const SizedBox(height: 10),
//
// /// 🎥 VIDEO WITH WEB CONTROLS
// SizedBox(
// height: kIsWeb ? 320 : 220,
// child: Stack(
// alignment: Alignment.center,
// children: [
// PageView.builder(
// controller: _pageController,
// itemCount: _controllers.length,
// onPageChanged: (index) {
// setState(() {
// _controllers[_currentIndex].pause();
// _currentIndex = index;
// });
// },
// itemBuilder: (context, index) {
// final controller = _controllers[index];
//
// return Center(
// child: SizedBox(
// width: kIsWeb ? 520 : double.infinity,
// child: AspectRatio(
// aspectRatio: controller.value.aspectRatio,
// child: Stack(
// alignment: Alignment.center,
// children: [
// VideoPlayer(controller),
//
// IconButton(
// iconSize: 60,
// icon: Icon(
// controller.value.isPlaying
// ? Icons.pause_circle
// : Icons.play_circle,
// color: Colors.white,
// ),
// onPressed: () {
// setState(() {
// controller.value.isPlaying
// ? controller.pause()
// : controller.play();
// });
// },
// ),
// ],
// ),
// ),
// ),
// );
// },
// ),
//
// /// ⬅ PREVIOUS (WEB ONLY)
// if (kIsWeb)
// Positioned(
// left: 16,
// child: _VideoArrowButton(
// icon: Icons.chevron_left,
// enabled: _currentIndex > 0,
// onTap: _previous,
// ),
// ),
//
//
// /// ➡ NEXT (WEB ONLY)
// if (kIsWeb)
// Positioned(
// right: 16,
// child: _VideoArrowButton(
// icon: Icons.chevron_right,
// enabled: _currentIndex < _controllers.length - 1,
// onTap: _next,
// ),
// ),
//
// ],
// ),
// ),
//
// const SizedBox(height: 8),
//
// /// 🔘 DOT INDICATOR
// Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: List.generate(
// _controllers.length,
// (index) => AnimatedContainer(
// duration: const Duration(milliseconds: 250),
// margin: const EdgeInsets.symmetric(horizontal: 4),
// height: 8,
// width: _currentIndex == index ? 20 : 8,
// decoration: BoxDecoration(
// color: _currentIndex == index
// ? const Color(0xFF593AFF)
// : Colors.grey.shade400,
// borderRadius: BorderRadius.circular(4),
// ),
// ),
// ),
// ),
// ],
// );
// }
//
//
// }
// class _VideoArrowButton extends StatelessWidget {
// final IconData icon;
// final bool enabled;
// final VoidCallback onTap;
//
// const _VideoArrowButton({
// required this.icon,
// required this.enabled,
// required this.onTap,
// });
//
// @override
// Widget build(BuildContext context) {
// return IgnorePointer(
// ignoring: !enabled,
// child: AnimatedOpacity(
// duration: const Duration(milliseconds: 200),
// opacity: enabled ? 1 : 0.3,
// child: Material(
// color: Colors.black.withOpacity(0.55),
// shape: const CircleBorder(),
// elevation: 4,
// child: InkWell(
// customBorder: const CircleBorder(),
// onTap: onTap,
// child: Padding(
// padding: const EdgeInsets.all(10),
// child: Icon(
// icon,
// size: 36,
// color: Colors.white,
// ),
// ),
// ),
// ),
// ),
// );
// }
// }