import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:video_player/video_player.dart'; class CustomVideoProgressBar extends StatefulWidget { final VideoPlayerController controller; final double? width; final bool canSlide; const CustomVideoProgressBar({ super.key, required this.controller, this.width, this.canSlide = true, }); @override State createState() => _CustomVideoProgressBarState(); } class _CustomVideoProgressBarState extends State { bool _isDragging = false; double? _dragValue; @override void initState() { super.initState(); widget.controller.addListener(_updateState); } @override void dispose() { widget.controller.removeListener(_updateState); super.dispose(); } void _updateState() { if (!_isDragging) { if (mounted) setState(() {}); } } String _formatDuration(Duration duration) { final minutes = duration.inMinutes.remainder(60).toString().padLeft(2, '0'); final seconds = duration.inSeconds.remainder(60).toString().padLeft(2, '0'); return '$minutes:$seconds'; } @override Widget build(BuildContext context) { final Duration duration = widget.controller.value.duration; final Duration position = _isDragging ? Duration(milliseconds: _dragValue?.toInt() ?? 0) : widget.controller.value.position; return Column( children: [ // 时间显示 Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Text( '${_formatDuration(position)}/', style: TextStyle(color: Color(0xFFDFDFDF), fontSize: 10.sp), ), Text( _formatDuration(duration), style: TextStyle(color: Color(0xFFDFDFDF), fontSize: 10.sp), ), ], ), // 进度条 SizedBox( width: widget.width ?? ScreenUtil().screenWidth - 30.w, child: SliderTheme( data: SliderTheme.of(context).copyWith( trackHeight: 2.0.w, trackShape: UniformTrackShape(), thumbShape: RoundSliderThumbShape(enabledThumbRadius: 6.0), ), child: Slider( padding: EdgeInsets.zero, min: 0, max: duration.inMilliseconds.toDouble(), value: position.inMilliseconds.toDouble().clamp( 0, duration.inMilliseconds.toDouble(), ), onChangeStart: (_) { if (!widget.canSlide) return; _isDragging = true; }, onChangeEnd: (value) { if (!widget.canSlide) return; _isDragging = false; widget.controller.seekTo(Duration(milliseconds: value.toInt())); widget.controller.play(); _dragValue = null; }, onChanged: (value) { if (!widget.canSlide) return; setState(() => _dragValue = value); }, activeColor: Colors.white, inactiveColor: Colors.white.withValues(alpha: .3), ), ), ), ], ); } } // 自定义轨道形状,确保选中和未选中部分高度一致 class UniformTrackShape extends RoundedRectSliderTrackShape { const UniformTrackShape(); @override Rect getPreferredRect({ required RenderBox parentBox, Offset offset = Offset.zero, required SliderThemeData sliderTheme, bool isEnabled = false, bool isDiscrete = false, }) { final double? trackHeight = sliderTheme.trackHeight; final double trackLeft = offset.dx; final double trackTop = offset.dy + (parentBox.size.height - trackHeight!) / 2; final double trackWidth = parentBox.size.width; return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight); } }