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 videoUrls; const NhanceMultiVideoWrapper({ super.key, required this.videoUrls, }); @override State createState() => _NhanceMultiVideoWrapperState(); } class _NhanceMultiVideoWrapperState extends State { final PageController _pageController = PageController(); final List _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 _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 videoUrls; // // const NhanceMultiVideoWrapper({ // super.key, // required this.videoUrls, // }); // // @override // State createState() => // _NhanceMultiVideoWrapperState(); // } // // class _NhanceMultiVideoWrapperState extends State { // final PageController _pageController = PageController(); // int _currentIndex = 0; // // final List _controllers = []; // // @override // void initState() { // super.initState(); // _initControllers(); // } // // Future _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, // ), // ), // ), // ), // ), // ); // } // }