Flutter插件式解耦架构在播放器领域中的应用

2021-12-06 16:04:46 浏览数 (1)

1. 项目背景

播放器作为应用内使用最频繁的SDK之一,尝尝需要应对多种应用场景,因此如何通过合理设计框架,从而根据各个团队的需求开发出针对性的业务插件,是播放器SDK应用使用多种实用场景的重要思路。

2. 解耦架构

解耦架构解耦架构

可以看到,在解耦架构下,所有的第三方功能均从内核中拆出,如画中画、换链、控制面板、上报等,使得内核保持纯粹,并且在需要的时候可以组合多个插件。

这里以UI控制面板为例,阐述一下Flutter场景下的播放器UI插件化开发的应用。

UI控制面板UI控制面板
代码语言:txt复制
├── base
│   └── ftp_panel_base.dart
├── components
│   ├── ftp_hide_timer.dart
│   ├── ftp_play_pause.dart
│   └── ftp_progress_bar.dart
└── utils
│   └── ftp_ui_utils.dart
├── ftp_panel.dart

其中ftp_panel_base.dart为基础抽象类,提供FTPController给插件访问,最终由组合出来的StatefulWidget实现方法。

代码语言:txt复制
abstract class FTPPanelBase {
  FTPPlayerController get playerController;
  FTPPlayerValue get playerValue => playerController.value;
}

而播放按钮PlayPauseMixin采用Dart中mixin的写法,mix的对象为State<T>,这样最后封装Panel的时候可以任意组合相关插件,而通过声明实现FTPPanelBase抽象类,即可访问playerController实例,直接获取播放状态,并控制播放和暂停。

代码语言:txt复制
mixin PlayPauseMixin<T extends StatefulWidget> on State<T>
    implements HideTimer, FTPPanelBase {
	GestureDetector buildPlayPause(Color iconColor) {
    return GestureDetector(
      onTap: playPause,
      child: Container(
        height: 48.0,
        color: Colors.transparent,
        margin: const EdgeInsets.only(left: 8.0, right: 4.0),
        padding: const EdgeInsets.only(
          left: 12.0,
          right: 12.0,
        ),
        child: Icon(
            playerController.value.isPlaying ? Icons.pause : Icons.play_arrow,
            color: iconColor),
      ),
    );
  }
}

而在使用这些UI组件时,通过with的方式对mixin和抽象接口类进行组合,即可访问UI组件的实现方法,快速组合出可用的控制面板UI。

代码语言:txt复制
class FTPDefaultPanelState extends State<FTPDefaultPanel>
    with
        FTPPanelBase,
        CenterPlayPauseMixin,
        HideTimerMixin,
        HideTimer,
        ExpandButtonMixin,
        MuteButtonMixin,
        SpeedButtonMixin,
        TotalPositionMixin,
        CurrentPositionMixin,
        ProgressBarMixin,
        PlayPauseMixin,
        SingleTickerProviderStateMixin {
  @override
  FTPPlayerController get playerController => widget.playerController;
  
  AnimatedOpacity _buildBottomBar(
    BuildContext context,
  ) {
    final iconColor = Theme.of(context).textTheme.button!.color;
    return AnimatedOpacity(
      opacity: hideStuff ? 0.0 : 1.0,
      duration: const Duration(milliseconds: 300),
      child: Container(
        color: Colors.white,
        height: barHeight,
        child: Row(
          children: <Widget>[
            buildPlayPause(iconColor!),
            const SizedBox(width: 12),
            buildCurPosition(iconColor),
            buildProgressBar(null),
            buildTotalPosition(iconColor),
            buildSpeedButton(iconColor),
            buildMuteButton(iconColor),
            buildExpandButton(iconColor),
          ],
        ),
      ),
    );
  }

  @override
  late bool isFullScreen = false;

  @override
  void setPlayPauseListener(FTPPlayPauseListener ftpPlayPauseListener) {
    super.setPlayPauseListener(widget.ftpPlayPauseListener);
  }

接下来以换链为例阐述非UI控制插件的实现方式。

这里通过extension的方式为FTPPlayerController增加了使用腾讯云vid换链功能的方法,只要import对应的扩展方法,即可无缝实用插件方法。

代码语言:txt复制
extension VideoInfoExtension on FTPPlayerController {
  static const String BASEURL = 'https://playvideo.zijiebao.com/getplayinfo/v4/';

  /// 使用设置播放地址
  Future<int?> setDataSourceWithVideoId(
      SuperPlayerVideoModel superPlayerVideoId) async {
    if (superPlayerVideoId.fileId!.isEmpty ||
        superPlayerVideoId.appId!.isEmpty) {
      return -1;
    }
    var videoInfo = await VideoInfo.getVideoInfo(superPlayerVideoId);
    return setDataSource(videoInfo.videoUrl ?? '');
  }
}

0 人点赞