flutter 技术选型 —— 视频播放器插件

chewie

依赖:video_player + chewie
缺点:ui 简陋

class VideoPlayerScreen extends StatefulWidget {
  const VideoPlayerScreen({Key? key}) : super(key: key);

  @override
  State<VideoPlayerScreen> createState() => _VideoPlayerScreenState();
}

class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
  VideoPlayerController? videoPlayerController;
  ChewieController? chewieController;

  @override
  void initState() {
    super.initState();
    initVideoPlayer();
  }

  Future<void> initVideoPlayer() async {
    videoPlayerController = VideoPlayerController.network('https://sdx2.oss-cn-beijing.aliyuncs.com/qcloud.mp4');

    await videoPlayerController?.initialize();

    setState(() {
      chewieController = ChewieController(
        videoPlayerController: videoPlayerController!,
        autoPlay: true,
        looping: true,
      );
    });
  }

  @override
  void dispose() {
    videoPlayerController?.dispose();
    chewieController?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: chewieController == null
          ? const LinearProgressIndicator()
          : ListView(
              children: [
                AspectRatio(
                  aspectRatio: 16 / 9,
                  child: Chewie(
                    controller: chewieController!,
                  ),
                ),
              ],
            ),
    );
  }
}

fijkplayer

依赖:fijkplayer
优点:支持后台音频播放
缺点:需要定制 ui

class Fijkplayer extends StatefulWidget {
  const Fijkplayer({Key? key}) : super(key: key);

  @override
  State<Fijkplayer> createState() => _FijkplayerState();
}

class _FijkplayerState extends State<Fijkplayer> {
  final FijkPlayer player = FijkPlayer();

  @override
  void initState() {
    super.initState();
    player.setDataSource('https://sdx2.oss-cn-beijing.aliyuncs.com/qcloud.mp4', autoPlay: true);
  }

  @override
  void dispose() {
    super.dispose();
    player.release();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        alignment: Alignment.center,
        child: FijkView(
          color: Colors.black,
          fsFit: FijkFit.fitHeight,
          player: player,
          panelBuilder: (FijkPlayer player, FijkData data, BuildContext context, Size viewSize, Rect texturePos) {
            return CustomFijkPanel(
                player: player,
                buildContext: context,
                viewSize: viewSize,
                texturePos: texturePos);
          },
        ),
      ),
    );
  }
}

class CustomFijkPanel extends StatefulWidget {
  final FijkPlayer player;
  final BuildContext? buildContext;
  final Size? viewSize;
  final Rect? texturePos;

  const CustomFijkPanel({
    required this.player,
    this.buildContext,
    this.viewSize,
    this.texturePos,
  });

  @override
  _CustomFijkPanelState createState() => _CustomFijkPanelState();
}

class _CustomFijkPanelState extends State<CustomFijkPanel> {
  FijkPlayer get player => widget.player;
  bool _playing = false;

  @override
  void initState() {
    super.initState();
    //初始化
    _playing = player.state == FijkState.started;
    widget.player.addListener(_playerValueChanged);
  }

  void _playerValueChanged() {
    FijkValue value = player.value;

    print("playing ${value.state}");
    bool playing = (value.state == FijkState.started);
    setState(() {
      _playing = playing;
    });

  }

  @override
  Widget build(BuildContext context) {
    Rect rect = Rect.fromLTRB(max(0.0, widget.texturePos?.left ?? 0), max(0.0, widget.texturePos?.top ?? 0),
        min(widget.viewSize?.width ?? 0, widget.texturePos?.right ?? 0), min(widget.viewSize?.height ?? 0, widget.texturePos?.bottom ?? 0));

    return Positioned.fromRect(
      rect: rect,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.end,
        children: [
          IconButton(
            icon: Icon(
              _playing ? Icons.pause : Icons.play_arrow,
              color: Colors.white,
            ),
            onPressed: () {
              _playing ? widget.player.pause() : widget.player.start();
            },
          ),
          IconButton(
            icon: Icon(
              widget.player.value.fullScreen ? Icons.fullscreen_exit : Icons.fullscreen,
              color: Colors.white,
            ),
            onPressed: () {
              widget.player.value.fullScreen ? widget.player.exitFullScreen() : widget.player.enterFullScreen();
            },
          ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    player.removeListener(_playerValueChanged);
  }
}

flickplayer

依赖:flick_video_player, video_player
优点:UI 很好看
缺点:不支持后台音频

class FlickPlayerScreen extends StatefulWidget {
  const FlickPlayerScreen({Key? key}) : super(key: key);

  @override
  State<FlickPlayerScreen> createState() => _FlickPlayerScreenState();
}

class _FlickPlayerScreenState extends State<FlickPlayerScreen> {
  late FlickManager flickManager;

  @override
  void initState() {
    super.initState();
    flickManager = FlickManager(
      videoPlayerController: VideoPlayerController.network(
          "https://sdx2.oss-cn-beijing.aliyuncs.com/qcloud.mp4"),
    );
  }

  @override
  void dispose() {
    flickManager.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: AspectRatio(
          aspectRatio: 16/9,
          child: FlickVideoPlayer(
            flickManager: flickManager,
            preferredDeviceOrientationFullscreen: [
              DeviceOrientation.landscapeLeft,
              DeviceOrientation.landscapeRight,
            ],
            flickVideoWithControls: FlickVideoWithControls(
              videoFit: BoxFit.contain,
              controls: CustomOrientationControls(
              ),
            ),
            flickVideoWithControlsFullscreen: FlickVideoWithControls(
              videoFit: BoxFit.contain,
              controls: SafeArea(child: CustomOrientationControls()),
            ),
          ),
        ),
      ),
    );
  }
} 

class CustomOrientationControls extends StatelessWidget {
  const CustomOrientationControls(
      {Key? key, this.iconSize = 25, this.fontSize = 12})
      : super(key: key);
  final double iconSize;
  final double fontSize;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        Positioned.fill(
          child: FlickShowControlsAction(
            child: FlickSeekVideoAction(
              child: Center(
                child:  FlickAutoHideChild(
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                      Padding(
                        padding: const EdgeInsets.all(8.0),
                        child: FlickPlayToggle(size: 50),
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ),
        ),
        Positioned.fill(
          child: FlickAutoHideChild(
            child: Padding(
              padding: const EdgeInsets.all(8.0),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.end,
                children: <Widget>[
                  Row(
                    children: <Widget>[
                      Row(
                        children: <Widget>[
                          FlickCurrentPosition(
                            fontSize: fontSize,
                          ),
                          Text(
                            ' / ',
                            style: TextStyle(
                                color: Colors.white, fontSize: fontSize),
                          ),
                          FlickTotalDuration(
                            fontSize: fontSize,
                          ),
                        ],
                      ),
                      Expanded(
                        child: Container(),
                      ),
                      FlickFullScreenToggle(
                        size: iconSize,
                      ),
                    ],
                  ),
                  FlickVideoProgressBar(
                    flickProgressBarSettings: FlickProgressBarSettings(
                      height: 5,
                      handleRadius: 5,
                      curveRadius: 50,
                      backgroundColor: Colors.white24,
                      bufferedColor: Colors.white38,
                      playedColor: Colors.red,
                      handleColor: Colors.red,
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ],
    );
  }
}

注:iOS 后台音频需要添加 Info.plist 参考链接

<key>UIBackgroundModes</key>
  <array>
    <string>audio</string>
  </array>

media_kit

posted on 2022-08-16 21:17  Lemo_wd  阅读(1331)  评论(0编辑  收藏  举报

导航