一個高自由度的Flutter 視頻播放器

寫在前面

咱們每每寫項目都是着急上手,總想着先寫後面有時間了再(說)來(的)優(好)化(聽),這樣寫出來的代碼每每都是跟業務系統耦合在一塊要麼就是質量不高,下次使用的時候就想重構,有人說項目急沒辦法(曾經我也這麼說過),可是一個好的編程習慣真的能夠作到事半功倍。既然是一種習慣,那麼確定是能夠改的,首先我要先習慣利用思惟導圖來明確明白本身要什麼,有什麼功能,開始設計接口以及參數,最後纔是分優先級而後根據優先級來執行下一步該作什麼,這樣能夠很大高效的避免踩坑,避免由於設計不合理須要重構就麻煩了。閒話很少說下面進入正題。git

那麼什麼叫高度自由的播放器呢?我是這麼認爲的:首先儘量的開放權限來更改配置,其次模塊化設計,同時還須要跟業務代碼解耦,光有這些還不算夠,最後咱們須要讓視頻播放器實現自定義拓展,也就是說現有播放器功能知足不了本身需求的時候,開發者能夠本身動手,例如視頻的字幕彈幕視頻頂部控制欄等。github

一張效果圖

快速通道

高自由度的播放器應該具有哪些功能?

  • 1.全部圖標都可自定義
  • 2.播放器自定義配置
    • 視頻開始播放的起始位置
    • 是否自動播放
    • 是否循環播放
    • 視頻比例
    • 進度條是否容許拖拽
    • 視頻快進/快退的單位秒數
  • 3.自定義拓展子元素
    • 字幕(也可使用內置字幕)
    • 彈幕
    • 其餘元素(例如:廣告覆蓋;自定義視頻頂部返回按鈕;快進或快退等操做的漸入漸出的提示等等)
  • 4.自定義播放器樣式
    • 播放按鈕是否顯示(視頻暫停時視圖中央的播放按鈕)
    • 自定義播放按鈕(視頻暫停時視圖中央的播放按鈕)
    • 控制欄自定義配置
      • 自定義順序(經過數組來控制元素)
      • 自定義元素(經過數組來控制順序)
      • 自定義背景顏色
      • 自定義圖標(播放、暫停、快進、快退、全屏、取消全屏)
      • 進度條樣式自由配置(背景顏色、緩存區顏色、進度條顏色)
      • 音量控制元素(還沒有完成)
      • 內置彈幕(還沒有完成,尚可自定義)
      • 控制欄拓展(還沒有完成)
    • 內置字幕自定義配置(或者經過自定義拓展來本身重寫字幕)
      • 設置主字幕或輔字幕的字體大小
      • 設置主字幕或輔字幕的字體顏色
  • 5.手勢支持
    • 單擊(菜單欄顯示或隱藏)
    • 雙擊(暫停或播放)
    • 滑動快進或快退(未完成)
  • 6.橫豎屏切換
  • 7.常亮避免鎖屏

一圖勝千言

經過上圖咱們能夠看一下播放器都有哪些屬性呢?編程

播放器屬性:數組

屬性 類型 描述
dataSource String 視頻URL或媒體文件的路徑
children List 自定義拓展的子元素,須要使用 WidgetAlign(字幕、彈幕、視頻頂部控制欄等)
onplay VideoCallback 視頻開始播放的回調
onpause VideoCallback 視頻暫停播放回調
ontimeupdate VideoCallback 視頻播放進度回調(經過返回的value進行字幕匹配)
onend VideoCallback 視頻播放結束回調
playOptions VideoPlayOptions 視頻播放自定義配置(詳情見下方的Useage
videoStyle VideoStyle 視頻播放器自定義樣式(詳情見下方的Useage

播放器自定義配置 (VideoPlayOptions):緩存

屬性 類型 描述
startPosition Duration 開始播放節點,例如:Duration(seconds: 0))
loop bool 是否循環播放
seekSeconds num 設置視頻快進/快退單位秒數
autoplay bool 是否自動播放
aspectRatio num 視頻播放比例,例如:16/9 或者 4/3
allowScrubbing bool 是否運行進度條拖拽

播放器自定義樣式 (VideoStyle):bash

屬性 類型 描述
playIcon Widget 視頻暫停播放時中央顯示的圖標,showPlayIcon爲false時,該屬性設置無效。
showPlayIcon bool 暫停時是否顯示播放按鈕
videoControlBarStyle VideoControlBarStyle 控制欄自定義樣式
videoSubtitlesStyle VideoSubtitles 字幕自定義樣式

控制欄自定義樣式 (VideoControlBarStyle):微信

屬性 類型 描述
barBackgroundColor Color 控制欄背景顏色,默認爲Color.fromRGBO(0, 0, 0, 0.5)
playedColor Color 已播放的進度條顏色(下圖1詳細說明)
bufferedColor Color 已緩衝的進度條顏色(下圖1詳細說明)
backgroundColor Color 進度條背景顏色(下圖1詳細說明)
playIcon Widget 控制欄播放圖標(下圖2詳細說明)
pauseIcon Widget 控制欄暫停圖標(下圖2詳細說明)
rewindIcon Widget 控制欄快退圖標(下圖2詳細說明)
forwardIcon Widget 控制欄快進圖標(下圖2詳細說明)
fullscreenIcon Widget 控制欄全屏圖標(下圖2詳細說明)
fullscreenExitIcon Widget 控制欄取消全屏圖標(下圖2詳細說明)
itemList List 控制欄自定義功能(下圖3詳細說明),默認爲["rewind", "play", "forward", "progress", "time", "fullscreen"]。若是咱們須要調整控制欄顯示的順序,僅須要調整 list 中字符串的順序,若是須要刪減,直接從 list 中移除改字符串便可,例如移除快進和快退,則講 list 設置爲 ["play", "progress", "time", "fullscreen"] 便可。後面會陸續開放自定義元素,也就是你把你的元素加入到 list 中。

控制欄顏色自定義

圖1

圖標自定義

圖2

控制欄進度條自定義元素

圖3

如何使用?

Install & Set up

  1. 添加依賴,打開根目錄的pubspec.yaml文件,在dependencies:下面添加如下代碼app

    awsome_video_player: #latest
    複製代碼
  2. 安裝依賴(若是已經自動安裝請忽略)ide

    cd 項目目錄
    flutter packages get
    複製代碼
  3. 在頁面中引入庫模塊化

    import 'package:awsome_video_player/awsome_video_player.dart';
    複製代碼

Useage

這是一個完整的

main.dart

import 'package:flutter/material.dart';

import 'package:awsome_video_player/awsome_video_player.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  String mainSubtitles = "主字幕";
  String subSubtitles = "輔字幕";

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Awsome video player'),
        ),
        body: Center(
          child: AwsomeVideoPlayer(
            "https://www.runoob.com/try/demo_source/movie.mp4",
            /// 視頻播放配置
            playOptions: VideoPlayOptions(
              seekSeconds: 30,
              aspectRatio: 16 / 9,
              loop: true,
              autoplay: true,
              allowScrubbing: true,
              startPosition: Duration(seconds: 0)),
            /// 自定義視頻樣式              
            videoStyle: VideoStyle(
            	/// 自定義視頻暫停時視頻中部的播放按鈕
              playIcon: Icon(
                Icons.play_circle_outline,
                size: 100,
                color: Colors.white,
              ),
              /// 暫停時是否顯示視頻中部播放按鈕              
              showPlayIcon: true,
              /// 自定義底部控制欄              
              videoControlBarStyle: VideoControlBarStyle(       
                /// 自定義顏色
                //playedColor: Colors.red,
                //bufferedColor: Colors.yellow,
                //backgroundColor: Colors.green,
                //barBackgroundColor: Colors.blue,
                /// 更改進度欄的播放按鈕
                playIcon: Icon(
                  Icons.play_arrow, 
                  color: Colors.white, 
                  size: 16
                ),
                /// 更改進度欄的暫停按鈕
                pauseIcon: Icon(
                  Icons.pause,
                  color: Colors.red,
                  size: 16,
                ),
                /// 更改進度欄的快退按鈕
                rewindIcon: Icon(
                  Icons.replay_30,
                  size: 16,
                  color: Colors.white,
                ),
                /// 更改進度欄的快進按鈕
                forwardIcon: Icon(
                  Icons.forward_30,
                  size: 16,
                  color: Colors.white,
                ),
                /// 更改進度欄的全屏按鈕
                fullscreenIcon: Icon(
                  Icons.fullscreen,
                  size: 16,
                  color: Colors.white,
                ),
                /// 更改進度欄的退出全屏按鈕
                fullscreenExitIcon: Icon(
                  Icons.fullscreen_exit,
                  size: 16,
                  color: Colors.red,
                ),
                /// 決定控制欄的元素以及排序,示例見上方圖3
                itemList: [
                  "rewind",
                  "play",
                  "forward",
                  "progress",
                  "time",
                  "fullscreen"
                ],
              ),
              /// 自定義字幕
              videoSubtitlesStyle: VideoSubtitles(
              	mianTitle: Align(
                  alignment: Alignment.bottomCenter,
                  child: Container(
                    padding: EdgeInsets.fromLTRB(10, 0, 10, 30),
                    child: Text(
                    		mainSubtitles,
                        maxLines: 2,
                        textAlign: TextAlign.center,
                        style: TextStyle(
                            color: Colors.white, fontSize: 14)),
                  ),
                ),
                subTitle: Align(
                  alignment: Alignment.bottomCenter,
                  child: Container(
                    padding: EdgeInsets.all(10),
                    child: Text(
                    		subSubtitles,
                        maxLines: 2,
                        textAlign: TextAlign.center,
                        style: TextStyle(
                            color: Colors.white, fontSize: 14)),
                  ),
                ),
              ),
            ),
            /// 自定義拓展元素
            children: [
            	/// 添加自定義視頻頂部返回按鈕
              Align(
                alignment: Alignment.topLeft,
                child: GestureDetector(
                  onTap: () {
                    print("This is test from children.");
                  },
                  child: Container(
                    margin: EdgeInsets.only(top: 5, left: 5),
                    width: 30,
                    height: 30,
                    decoration: BoxDecoration(
                      color: Color.fromRGBO(0, 0, 0, .5),
                      borderRadius: BorderRadius.all(Radius.circular(15)),
                    ),
                    child: Icon(Icons.arrow_back, size: 16, color: Colors.white,)
                  ),
                ),
              ),

              /// 這個將會覆蓋的視頻內容,由於這個層級是最高級,所以手勢會失效(慎用)
              /// 這個能夠用來作視頻廣告
              // Positioned(
              //   top: 0,
              //   left: 0,
              //   bottom: 0,
              //   right: 0,
              //   child: Text("data", style: TextStyle(color: Colors.white),),
              // ),
            ],
            /// 視頻暫停回調
            onpause: (value) {
              print("video paused");
            },
            /// 視頻播放回調
            onplay: (value) {
              print("video played");
            },
            /// 視頻播放結束回調
            onended: (value) {
              print("video ended");
            },
            /// 視頻播放進度回調
            /// 能夠用來匹配字幕
            ontimeupdate: (value) {
              print("timeupdate ${value}");
              var position = value.position.inMilliseconds / 1000;
              //根據 position 來判斷當前顯示的字幕
            },
          ),
        ),
      ),
    );
  }
}
複製代碼

示例展現

1. 自定義控制欄圖標

首先我來看一下控制欄的圖標自定義(見上圖2):

DEMO: main.dart

import 'package:flutter/material.dart';

import 'package:awsome_video_player/awsome_video_player.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Awsome video player'),
        ),
        body: Center(
          child: AwsomeVideoPlayer(
            "https://www.runoob.com/try/demo_source/movie.mp4",
            /// 視頻播放配置
            playOptions: VideoPlayOptions(
              seekSeconds: 30,
              aspectRatio: 16 / 9,
              loop: true,
              autoplay: true,
              allowScrubbing: true,
              startPosition: Duration(seconds: 0)),
            /// 自定義視頻樣式 - 請注意我要劃重點了
            videoStyle: VideoStyle(
              /// 自定義底部控制欄  - 這是重點了
              videoControlBarStyle: VideoControlBarStyle(       
                /// 更改進度欄的播放按鈕
                playIcon: Icon(
                  Icons.play_arrow, 
                  color: Colors.white, 
                  size: 16
                ),
                /// 更改進度欄的暫停按鈕
                pauseIcon: Icon(
                  Icons.pause,
                  color: Colors.red,
                  size: 16,
                ),
                /// 更改進度欄的快退按鈕
                rewindIcon: Icon(
                  Icons.replay_30,
                  size: 16,
                  color: Colors.white,
                ),
                /// 更改進度欄的快進按鈕
                forwardIcon: Icon(
                  Icons.forward_30,
                  size: 16,
                  color: Colors.white,
                ),
                /// 更改進度欄的全屏按鈕
                fullscreenIcon: Icon(
                  Icons.fullscreen,
                  size: 16,
                  color: Colors.white,
                ),
                /// 更改進度欄的退出全屏按鈕
                fullscreenExitIcon: Icon(
                  Icons.fullscreen_exit,
                  size: 16,
                  color: Colors.red,
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

複製代碼

2. 自定義控制欄元素和順序

咱們能夠根據videoStylevideoControlBarStyle來自定義控制欄的樣式:調整順序(見上方圖3)只須要調整 itemList中字符串的順序便可;控制顯示的元素,將不須要暫時的元素從 itemList 中刪除便可。

DEMO: main.dart

import 'package:flutter/material.dart';

import 'package:awsome_video_player/awsome_video_player.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Awsome video player'),
        ),
        body: Center(
          child: AwsomeVideoPlayer(
            "https://www.runoob.com/try/demo_source/movie.mp4",
            /// 視頻播放配置
            playOptions: VideoPlayOptions(
              seekSeconds: 30,
              aspectRatio: 16 / 9,
              loop: true,
              autoplay: true,
              allowScrubbing: true,
              startPosition: Duration(seconds: 0)),
            /// 自定義視頻樣式 - 請注意我要劃重點了
            videoStyle: VideoStyle(
              /// 自定義底部控制欄  - 這是重點了
              videoControlBarStyle: VideoControlBarStyle(       
                /// 決定控制欄的元素以及排序,示例見上方圖3
                itemList: [
                  "progress",// 這裏將進度條前置了,所以有了圖3的效果
                  "rewind",//若是須要刪除快退按鈕,將其註釋或刪除便可
                  "play",
                  "forward",
                  "time",
                  "fullscreen"
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}
複製代碼

3. 自定義進度條以及控制欄的背景顏色

一樣咱們仍是經過videoStylevideoControlBarStyle來自定義進度條的顏色(見上方圖3)。

DEMO: main.dart

import 'package:flutter/material.dart';

import 'package:awsome_video_player/awsome_video_player.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Awsome video player'),
        ),
        body: Center(
          child: AwsomeVideoPlayer(
            "https://www.runoob.com/try/demo_source/movie.mp4",
            /// 視頻播放配置
            playOptions: VideoPlayOptions(
              seekSeconds: 30,
              aspectRatio: 16 / 9,
              loop: true,
              autoplay: true,
              allowScrubbing: true,
              startPosition: Duration(seconds: 0)),
            /// 自定義視頻樣式              
            videoStyle: VideoStyle(
              /// 自定義底部控制欄              
              videoControlBarStyle: VideoControlBarStyle(       
                /// 自定義顏色
                playedColor: Colors.red,//已播放進度條的顏色
                bufferedColor: Colors.yellow,//已緩存進度條的顏色
                backgroundColor: Colors.green,//進度條的背景顏色
                barBackgroundColor: Colors.blue,//控制欄的背景顏色
              ),
            ),
          ),
        ),
      ),
    );
  }
}

複製代碼

注意事項(Q&A)

  • 視頻若是須要橫豎屏不能使用safeArea

  • 視頻的 dataSoure不能爲空,爲空時使用加載視圖,不然播放器會報錯

    import 'package:flutter/material.dart';
    
    import 'package:awsome_video_player/awsome_video_player.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatefulWidget {
      @override
      _MyAppState createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
    
      String videoUrl = "https://www.runoob.com/try/demo_source/movie.mp4";
    
      @override
      void initState() {
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(
              title: const Text('Awsome video player'),
            ),
            body: Center(
            	/// 通常videoUrl是從服務端返回,在沒有返回來以前,
            	/// 咱們可使用加載視圖來佔位
              child: videoUrl != "" ? AwsomeVideoPlayer(
                videoUrl,
                /// 視頻播放配置
                playOptions: VideoPlayOptions(
                  seekSeconds: 30,
                  aspectRatio: 16 / 9,
                  loop: true,
                  autoplay: true,
                  allowScrubbing: true,
                  startPosition: Duration(seconds: 0)),
              ) : AspectRatio(
                aspectRatio: 16 / 9,
                child: Center(
                  child: CircularProgressIndicator(strokeWidth: 2.0),
                ),
              ),
            ),
          ),
        );
      }
    }
    
    複製代碼
  • AwsomeVideoPlayer下面的children僅支持AlignPositioned,children的層級會高於下面,這個功能會持續更新,後面會陸續出一些針對自定義拓展的高階文檔。

寫在最後

開發過程當中遇到問題,請經過如下方式聯繫我,我會第一時間回覆你:

個人微信

掃碼進入技術進階交流羣

License

Copyright © 2020, Mark Chen. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 「Software」), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 「AS IS」, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

相關文章
相關標籤/搜索