You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Flutter中创建仿Netflix样式的自定义视频播放器

嘿,刚好我之前做过类似的仿Netflix视频播放器需求,给你分享些实用的方案和代码示例!

推荐的第三方库

首先,最适合的组合是官方的 video_player + chewie

  • video_player 是Flutter官方提供的视频播放核心库,负责底层的视频加载、播放控制等功能,稳定性有保障。
  • chewie 是基于video_player封装的UI层库,提供了常用的播放控件,同时支持高度自定义,能快速搭建出类似Netflix的播放器界面。

你只需要在pubspec.yaml里添加依赖:

dependencies:
  flutter:
    sdk: flutter
  video_player: ^2.8.1
  chewie: ^1.7.0

完整示例代码

下面是一个实现了你需求的完整播放器组件,包含播放/暂停按钮、返回按钮、标题显示、时间指示器,并且强制横屏:

import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
import 'package:chewie/chewie.dart';
import 'package:flutter/services.dart';

class NetflixStyleVideoPlayer extends StatefulWidget {
  final String videoUrl;
  final String videoTitle;

  const NetflixStyleVideoPlayer({
    super.key,
    required this.videoUrl,
    required this.videoTitle,
  });

  @override
  State<NetflixStyleVideoPlayer> createState() => _NetflixStyleVideoPlayerState();
}

class _NetflixStyleVideoPlayerState extends State<NetflixStyleVideoPlayer> {
  late VideoPlayerController _videoPlayerController;
  late ChewieController _chewieController;

  @override
  void initState() {
    super.initState();
    // 初始化时强制设置为横屏模式
    SystemChrome.setPreferredOrientations([
      DeviceOrientation.landscapeLeft,
      DeviceOrientation.landscapeRight,
    ]);
    _setupVideoPlayer();
  }

  Future<void> _setupVideoPlayer() async {
    _videoPlayerController = VideoPlayerController.network(widget.videoUrl);
    await _videoPlayerController.initialize();
    
    // 配置Chewie控制器,自定义控制栏
    _chewieController = ChewieController(
      videoPlayerController: _videoPlayerController,
      aspectRatio: _videoPlayerController.value.aspectRatio,
      autoPlay: true,
      looping: false,
      customControls: const _CustomPlayerControls(), // 替换默认控件
      showControlsOnInitialize: true,
      materialProgressColors: ChewieProgressColors(
        playedColor: Colors.red, // 进度条已播放部分颜色(Netflix红)
        handleColor: Colors.red,
        backgroundColor: Colors.grey[800]!,
        bufferedColor: Colors.grey[600]!,
      ),
    );
    setState(() {});
  }

  @override
  void dispose() {
    // 销毁控制器,恢复竖屏模式
    _videoPlayerController.dispose();
    _chewieController.dispose();
    SystemChrome.setPreferredOrientations([
      DeviceOrientation.portraitUp,
      DeviceOrientation.portraitDown,
    ]);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: Stack(
        children: [
          // 视频播放器主体
          _videoPlayerController.value.isInitialized
              ? Chewie(controller: _chewieController)
              : const Center(child: CircularProgressIndicator(color: Colors.white)),
          // 顶部返回按钮+标题栏
          Positioned(
            top: MediaQuery.of(context).padding.top + 10,
            left: 10,
            right: 10,
            child: Row(
              children: [
                IconButton(
                  icon: const Icon(Icons.arrow_back, color: Colors.white, size: 28),
                  onPressed: () => Navigator.pop(context),
                ),
                const SizedBox(width: 10),
                Expanded(
                  child: Text(
                    widget.videoTitle,
                    style: const TextStyle(
                      color: Colors.white,
                      fontSize: 18,
                      fontWeight: FontWeight.w600,
                    ),
                    overflow: TextOverflow.ellipsis,
                    maxLines: 1,
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

// 自定义播放控制栏:播放/暂停 + 时间指示器 + 进度条
class _CustomPlayerControls extends StatelessWidget {
  const _CustomPlayerControls();

  @override
  Widget build(BuildContext context) {
    final chewieController = ChewieController.of(context)!;
    return Positioned(
      bottom: 20,
      left: 0,
      right: 0,
      child: Column(
        children: [
          // 自定义进度条
          Slider(
            value: chewieController.videoPlayerController.value.position.inSeconds.toDouble(),
            max: chewieController.videoPlayerController.value.duration.inSeconds.toDouble(),
            onChanged: (value) {
              chewieController.seekTo(Duration(seconds: value.toInt()));
            },
            activeColor: Colors.red,
            inactiveColor: Colors.grey[700],
            thumbColor: Colors.red,
          ),
          // 播放按钮+时间显示
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16),
            child: Row(
              children: [
                // 播放/暂停按钮
                IconButton(
                  icon: Icon(
                    chewieController.isPlaying ? Icons.pause : Icons.play_arrow,
                    color: Colors.white,
                    size: 36,
                  ),
                  onPressed: chewieController.togglePlay,
                ),
                const SizedBox(width: 12),
                // 当前播放时间
                Text(
                  _formatDuration(chewieController.videoPlayerController.value.position),
                  style: const TextStyle(color: Colors.white, fontSize: 14),
                ),
                const SizedBox(width: 8),
                const Text('/', style: TextStyle(color: Colors.white)),
                const SizedBox(width: 8),
                // 总时长
                Text(
                  _formatDuration(chewieController.videoPlayerController.value.duration),
                  style: const TextStyle(color: Colors.white, fontSize: 14),
                ),
                const Spacer(),
                // 可以添加全屏按钮(不过这里已经强制横屏,可选)
              ],
            ),
          ),
        ],
      ),
    );
  }

  // 格式化时间为MM:SS或HH:MM:SS
  String _formatDuration(Duration duration) {
    String twoDigits(int n) => n.toString().padLeft(2, '0');
    final hours = twoDigits(duration.inHours);
    final minutes = twoDigits(duration.inMinutes.remainder(60));
    final seconds = twoDigits(duration.inSeconds.remainder(60));
    return duration.inHours > 0 ? '$hours:$minutes:$seconds' : '$minutes:$seconds';
  }
}

// 使用方式示例
// Navigator.push(
//   context,
//   MaterialPageRoute(
//     builder: (context) => NetflixStyleVideoPlayer(
//       videoUrl: 'https://example.com/your-video.mp4',
//       videoTitle: '怪奇物语 第四季 第一集',
//     ),
//   ),
// );

关键细节说明

  1. 横屏强制处理:在页面初始化时用SystemChrome.setPreferredOrientations锁定横屏,销毁时恢复竖屏,确保用户体验。
  2. 自定义控件:通过Chewie的customControls属性替换默认控制栏,完全按照Netflix的样式调整颜色、布局。
  3. 标题与返回按钮:用Stack叠加在视频上方,适配状态栏高度,避免被刘海遮挡。
  4. 时间格式化:自定义_formatDuration方法,把Duration转换为用户友好的时间格式。

进阶优化建议

  • 可以添加自动隐藏控制栏的功能:用Timer监听用户点击,无操作3秒后隐藏控件,点击屏幕时显示。
  • 加入手势控制:比如左右滑动快进快退,上下滑动调节亮度/音量,更贴近Netflix的交互。
  • 支持画中画模式:如果需要后台播放,可以结合相关库实现。

内容的提问来源于stack exchange,提问作者O G

火山引擎 最新活动