内置UI


fplayer除了默认皮肤外,还提供了高级皮肤,本节是对默认皮肤和高级皮肤的介绍。

默认皮肤

默认UI主要实现了一个播放器最基础的能力。

用法如下:

FView(
  player: player,
  panelBuilder: defaultFPanelBuilder, // 可省略
)

运行截图:

高级皮肤

高级皮肤是对默认UI的扩展,提供了一个更高级的panel builder函数,手势包括,主要分为四个部分(单视频模式、多视频模式、功能介绍以及API)。

单视频模式配置

只需要播放一个视频时的配置

用法如下:


void initState() {
  super.initState();
  // 播放传入的视频
  player.setDataSource(widget.url, autoPlay: true, showCover: true);
}


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

FView(
  player: player,
  width: double.infinity,
  height: videoHeight, // 需自行设置,此处宽度/高度=16/9
  color: Colors.black,
  fsFit: FFit.contain, // 全屏模式下的填充
  fit: FFit.fill, // 正常模式下的填充
  panelBuilder: fPanelBuilder(
    title: '视频标题',
    subTitle: '视频副标题',
  ),
)

运行截图:

小屏模式全屏模式
单视频 小屏模式 demo 运行截图单视频 全屏模式 demo 运行截图

多视频模式配置

需要播放视频列表时的配置(当一个视频播放完成时自动播放下一个视频等需求)

用法如下:

首先需要配置一些变量与方法

// 视频列表
List<VideoItem> videoList = [
  VideoItem(
    title: '第一集',
    subTitle: '视频1副标题',
    url: 'http://player.alicdn.com/video/aliyunmedia.mp4',
  ),
  VideoItem(
    title: '第二集',
    subTitle: '视频2副标题',
    url: 'https://www.runoob.com/try/demo_source/mov_bbb.mp4',
  ),
  VideoItem(
    title: '第三集',
    subTitle: '视频3副标题',
    url: 'http://player.alicdn.com/video/aliyunmedia.mp4',
  ),
];

// 视频索引,单个视频可不传
int videoIndex = 0;

// 播放传入的url
Future<void> setVideoUrl(String url) async {
  try {
    await player.setDataSource(url, autoPlay: true, showCover: true);
  } catch (error) {
    print("播放-异常: $error");
    return;
  }
}


void initState() {
  super.initState();
  // 播放视频列表的第一个视频
  setVideoUrl(videoList[videoIndex].url);
}


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

然后需要把这两个变量传给FView

Column(
  children: [
    FView(
      player: player,
      width: double.infinity,
      height: videoHeight, // 需自行设置,此处宽度/高度=16/9
      color: Colors.black,
      fsFit: FFit.contain, // 全屏模式下的填充
      fit: FFit.fill, // 正常模式下的填充
      panelBuilder: fPanelBuilder(
        // 视频列表开关
        isVideos: true,
        // 视频列表列表
        videoList: videoList,
        // 当前视频索引
        videoIndex: videoIndex,
        // 全屏模式下点击播放下一集视频回调
        playNextVideoFun: () {
          setState(() {
            videoIndex += 1;
          });
        },
        // 视频播放完成回调
        onVideoEnd: () async {
          var index = videoIndex + 1;
          if (index < videoList.length) {
            await player.reset();
            setState(() {
              videoIndex = index;
            });
            setVideoUrl(videoList[index].url);
          }
        },
      ),
    ),
    // 自定义小屏列表
    Container(
      width: double.infinity,
      height: 30,
      margin: const EdgeInsets.all(20),
      child: ListView.builder(
        scrollDirection: Axis.horizontal,
        padding: EdgeInsets.zero,
        itemCount: videoList.length,
        itemBuilder: (context, index) {
          bool isCurrent = videoIndex == index;
          Color textColor = Theme.of(context).primaryColor;
          Color bgColor = Theme.of(context).primaryColorDark;
          Color borderColor = Theme.of(context).primaryColor;
          if (isCurrent) {
            textColor = Theme.of(context).primaryColorDark;
            bgColor = Theme.of(context).primaryColor;
            borderColor = Theme.of(context).primaryColor;
          }
          return GestureDetector(
            onTap: () async {
              await player.reset();
              setState(() {
                videoIndex = index;
              });
              setVideoUrl(videoList[index].url);
            },
            child: Container(
              margin: EdgeInsets.only(left: index == 0 ? 0 : 10),
              padding: const EdgeInsets.symmetric(horizontal: 5),
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(5),
                color: bgColor,
                border: Border.all(
                  width: 1.5,
                  color: borderColor,
                ),
              ),
              alignment: Alignment.center,
              child: Text(
                videoList[index].title,
                style: TextStyle(
                  fontSize: 15,
                  color: textColor,
                ),
              ),
            ),
          );
        },
      ),
    ),
  ],
)

运行截图:

小屏模式全屏模式
多视频 小屏模式 demo 运行截图多视频 全屏模式 demo 运行截图

功能介绍

播放器功能包含基础功能与可配置功能,基础功能有手势等功能,手势包含横向拖动快退快进、纵向拖动调节亮度和声音、双击暂停播放以及长按快进松开恢复,下面主要是对可配置功能的介绍。

用法如下:

// 倍速列表
Map<String, double> speedList = {
  "2.0": 2.0,
  "1.5": 1.5,
  "1.0": 1.0,
  "0.5": 0.5,
};

// 清晰度列表
Map<String, ResolutionItem> resolutionList = {
  "480P": ResolutionItem(
    value: 480,
    url: 'https://www.runoob.com/try/demo_source/mov_bbb.mp4',
  ),
  "270P": ResolutionItem(
    value: 270,
    url: 'http://player.alicdn.com/video/aliyunmedia.mp4',
  ),
};

// 模拟播放记录视频初始化完需要跳转的进度
int seekTime = 100000;

fPanelBuilder(
  // 右下方截屏按钮
  isSnapShot: true,
  // 右上方按钮组开关
  isRightButton: true,
  // 右上方按钮组
  rightButtonList: [
    InkWell(
      onTap: () {},
      child: Container(
        padding: const EdgeInsets.all(10),
        decoration: BoxDecoration(
          color: Theme.of(context).primaryColorLight,
          borderRadius: const BorderRadius.vertical(
            top: Radius.circular(5),
          ),
        ),
        child: Icon(
          Icons.favorite,
          color: Theme.of(context).primaryColor,
        ),
      ),
    ),
    InkWell(
      onTap: () {},
      child: Container(
        padding: const EdgeInsets.all(10),
        decoration: BoxDecoration(
          color: Theme.of(context).primaryColorLight,
          borderRadius: const BorderRadius.vertical(
            bottom: Radius.circular(5),
          ),
        ),
        child: Icon(
          Icons.thumb_up,
          color: Theme.of(context).primaryColor,
        ),
      ),
    )
  ],
  // 自定义倍速列表
  speedList: speedList,
  // 清晰度开关
  isResolution: true,
  // 自定义清晰度列表
  resolutionList: resolutionList,
  settingFun: () {
    print('设置按钮点击事件');
  },
  // 视频播放错误点击刷新回调
  onError: () async {
    await player.reset();
    setVideoUrl(videoList[videoIndex].url);
  },
  onVideoTimeChange: () {
    // 视频时间变动则触发一次,可以保存视频历史
  },
  onVideoPrepared: () async {
    // 视频初始化完毕,如有历史记录时间段则可以触发快进
    try {
      if (seekTime >= 1) {
        /// seekTo必须在FState.prepared
        print('seekTo');
        await player.seekTo(seekTime);
        // print("视频快进-$seekTime");
        seekTime = 0;
      }
    } catch (error) {
      print("视频初始化完快进-异常: $error");
    }
  },
)

运行截图:

API

属性

属性名类型描述
titleString单视频模式视频标题
subTitleString单视频模式视频副标题
isSnapShotbool是否显示截图按钮,默认为false
isRightButtonbool是否显示全屏模式中间区域右上方按钮组,默认为false
rightButtonListList全屏模式中间区域右上方按钮组,建议不超过三个
isVideosbool是否为多视频模式,默认为false
videoListList多视频列表
speedListList倍速列表
isResolutionbool是否显示清晰度按钮,默认为false
resolutionListList清晰度列表

方法

方法名描述
playNextVideoFun多视频模式全屏状态下点击播放下一集按钮事件
settingFun点击右上角设置按钮事件
onError视频播放错误点击刷新回调
onVideoEnd视频播放完成回调
onVideoTimeChange视频时间变动则触发一次,可以保存视频播放历史
onVideoPrepared视频初始化完毕回调,如有历史记录时间段则可以触发快进