題記 —— 執劍天涯,從你的點滴積累開始,所及之處,必精益求精,便是折騰每一天。java
重要消息git
> 本小節講述: > 1 VideoPlayer 視頻播放組件使用 > 2 VideoPlayerController 的使用分析 > 3 FutureBuilder 的使用分析 > 4 PageView構建上下滑動的整屏切換頁面 > 5 TabBar 與 TabBarView 構建左右滑動切換的頁面緩存
經過 TabBar 與 TabBarView 實現左右切換的頁面網絡
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; class MainFind3Page extends StatefulWidget { @override State<statefulwidget> createState() { return MainFindPage3State(); } } class MainFindPage3State extends State with SingleTickerProviderStateMixin { List<string> tabTextList = ["關注", "推薦"]; List<tab> tabWidgetList = []; TabController tabController; @override void initState() { super.initState(); for (var value in tabTextList) { tabWidgetList.add(Tab( text: "$value", )); } tabController = new TabController(length: tabTextList.length, vsync: this); } @override Widget build(BuildContext context) { return buildRootBody(); } Widget buildRootBody() { return Scaffold( body: Stack( children: <widget>[ Positioned( left: 0, right: 0, top: 0, bottom: 0, child: Container( color: Colors.black, ), ), Positioned( left: 0, right: 0, top: 0, bottom: 0, child: buildTableViewWidget(), ), Positioned( left: 0, right: 0, bottom: 0, top: 54, child: buildTabBarWidget(), ), ], ), ); } ///構建 TabBarView buildTableViewWidget() { return TabBarView( controller: tabController, children: tabTextList .map((value) => Container( alignment: Alignment.center, child: Text("$value",style: TextStyle(color: Colors.white),), )) .toList(), ); } ///構建頂部標籤部分 buildTabBarWidget() { return Container( ///對齊在頂部中間 alignment: Alignment.topCenter, child: TabBar( controller: tabController, tabs: tabWidgetList, ///指示器的顏色 indicatorColor: Colors.white, ///指示器的高度 indicatorWeight: 2.0, isScrollable: true, ///指示器的寬度與文字對齊 indicatorSize: TabBarIndicatorSize.label, ), ); } }
在這裏是經過 幀佈局 將 TabBar 與 TabBarView 疊在一塊兒的。 效果以下 異步
文章《flutter跨平臺開發一點一滴分析系列文章》中 1.3.3 有記錄 PageView 的使用案例ide
咱們將上述 【構建 TabBarView】 處代碼替換,使用 PageView 來構建 上下整屏頁面切換效果函數
///構建 TabBarView buildTableViewWidget() { return TabBarView( controller: tabController, children: tabTextList .map((value) => buildTableViewItemWidget(value)) .toList(), ); } /// 用來建立上下滑動的頁面 Widget buildTableViewItemWidget(String value) { List<videomodel> list =[]; if(value == "推薦"){ list= videoList; }else{ list = videoList2; } return PageView.builder( /// pageview中 子條目的個數 itemCount:list.length , /// 上下滑動 scrollDirection: Axis.vertical, itemBuilder: (BuildContext context,int index){ VideoModel videoModel = list[index]; return buildPageViewItemWidget(value,videoModel); }); }
這裏面用到了 videoList 與 videoList2,保存的數據模型,是在 initState函數中初始化的oop
///推薦模擬數據 List <videomodel> videoList =[]; ///關注模擬數據 List <videomodel> videoList2 =[]; @override void initState() { super.initState(); ... ///建立模擬數據 for (int i = 0; i < 10; i++) { VideoModel videoModel = new VideoModel(); videoModel.videoName = "推薦測試數據$i"; videoModel.pariseCount = i * 22; if (i % 3 == 0) { videoModel.isAttention = true; videoModel.isLike = true; } else { videoModel.isAttention = false; videoModel.isLike = false; } videoModel.videoImag =""; videoModel.videoUrl =""; videoList.add(videoModel); } for (int i = 0; i < 3; i++) { VideoModel videoModel = new VideoModel(); videoModel.videoName = "關注測試數據$i"; videoModel.pariseCount = i * 22; videoModel.isAttention = true; if (i % 3 == 0) { videoModel.isLike = true; } else { videoModel.isLike = false; } videoModel.videoImag =""; videoModel.videoUrl =""; videoList2.add(videoModel); } }
對於 VideoModel 來說,就是咱們保存視頻信息的數據模型了佈局
class VideoModel { ///視頻名稱 String videoName =''; ///視頻連接 String videoUrl =''; ///視頻截圖 String videoImag =''; ///是否關注 bool isAttention =false; ///關注的個數 num attentCount =0; ///是否喜歡 bool isLike = false; ///點讚的個數 num pariseCount = 0; ///分享的次數 num shareCount=0; }
在上述代碼中咱們也使用到了 buildPageViewItemWidget 函數,以下測試
buildPageViewItemWidget(String value, VideoModel videoModel) { return FindVideoItemPage(value,videoModel); }
在這裏直接構建 的 FindVideoItemPage ,看以下 FindVideoItemPage 的定義
///播放視頻的頁面 class FindVideoItemPage extends StatefulWidget { String tabValue; VideoModel videoModel; FindVideoItemPage(this.tabValue, this.videoModel); @override State<statefulwidget> createState() { return FindVideoItemPageState(); } } class FindVideoItemPageState extends State<findvideoitempage> { ///建立視頻播放控制 器 VideoPlayerController videoPlayerController; ///控制更新視頻加載初始化完成狀態更新 Future videoPlayFuture; @override void initState() { super.initState(); videoPlayerController = VideoPlayerController.network(widget.videoModel.videoUrl); videoPlayFuture = videoPlayerController.initialize().then((_) { ///視頻初始完成後 ///調用播放 videoPlayerController.play(); setState(() {}); }); } @override Widget build(BuildContext context) { return Stack( children: <widget>[ ///播放視頻 buildVideoWidget(), ///控制播放視頻按鈕 buildControllWidget(), ///底部區域的視頻介紹 buildBottmFlagWidget(), ///右側的用戶信息按鈕區域 buildRightUserWidget(), ], ); } @override void dispose() { super.dispose(); videoPlayerController.dispose(); } }
其實 FindVideoItemPage 就是咱們 PageView 中構建的子視圖了,咱們能夠看到 在 初始化函數 initState 中 建立了 VideoPlayerController,顧名思義 VideoPlayerController 是用來控制當前頁面視頻的播放的,在 dispose 中銷燬 VideoPlayerController,這個也好理解,就是當前頁面都釋放掉了,播放的視頻固然要中止播放了。
在這裏這建立了一個 videoPlayFuture ,是用來監聽 VideoPlayerController 初始化狀態的,結合 FutureBuilder 來實時更新頁面 State,如在方法 buildVideoWidget() 中
///播放視頻 buildVideoWidget() { return FutureBuilder( future: videoPlayFuture, builder: (BuildContext contex, value) { if (value.connectionState == ConnectionState.done) { ///點擊事件 return InkWell( onTap: () { if (videoPlayerController.value.initialized) { /// 視頻已初始化 if (videoPlayerController.value.isPlaying) { /// 正播放 --- 暫停 videoPlayerController.pause(); } else { ///暫停 ----播放 videoPlayerController.play(); } setState(() {}); } else { ///未初始化 videoPlayerController.initialize().then((_) { videoPlayerController.play(); setState(() {}); }); } }, ///居中 child: Center( /// AspectRatio 組件用來設定子組件寬高比 child: AspectRatio( ///設置視頻的大小 寬高比。長寬比表示爲寬高比。例如,16:9寬高比的值爲16.0/9.0 aspectRatio: videoPlayerController.value.aspectRatio, ///播放視頻的組件 child: VideoPlayer(videoPlayerController), ), ), ); } else { return Container( alignment: Alignment.center, ///圓形加載進度 child: CircularProgressIndicator(), ); } }, ); }
FutureBuilder會依賴一個Future,對於FutureBuilder來說
FutureBuilder({ this.future, this.initialData, @required this.builder, })
future ,FutureBuilder 中依賴的 Future ,一般是一個異步耗時任務,如這裏的 videoPlayFuture 是指向 videoPlayerController 的初始化函數initialize(),這是一個異步的耗時操做, builder ,Widget構建器,該構建器會在Future執行的不一樣階段被屢次調用,構建格式以下
Function (BuildContext context, AsyncSnapshot snapshot) /** * snapshot會包含當前異步任務的狀態信息及結果信息 , * 好比咱們能夠經過snapshot.connectionState獲取異步任務的狀態信息、 * 經過snapshot.hasError判斷異步任務是否有錯誤等等 */
而經過 snapshot.connectionState 獲取的 ConnectionState 狀態有如下值:
enum ConnectionState { /// 當前沒有異步任務,好比[FutureBuilder]的[future]爲null時 none, /// 異步任務處於等待狀態 waiting, /// Stream處於激活狀態(流上已經有數據傳遞了),對於FutureBuilder沒有該狀態。 active, /// 異步任務已經終止. done, }
使用 VideoPlayer,咱們首先須要添加依賴
video_player: ^0.6.4
對於 VideoPlayer 來說,它只接收一個 VideoPlayerController,咱們能夠經過 VideoPlayerController 來綁定要播放的視頻地址
///網絡連接 videoPlayerController = VideoPlayerController.network(widget.videoModel.videoUrl); ///本地連接 VideoPlayerController videoPlayerController2 = VideoPlayerController.asset(widget.videoModel.videoUrl); ///File形式的視頻 VideoPlayerController videoPlayerController3 = VideoPlayerController.file(File(widget.videoModel.videoUrl));
當 綁定了播放的地址後,能夠VideoPlayerController來預加載初始化播放器
videoPlayerController.initialize().then((_) { ///視頻初始完成後 ///調用播放 videoPlayerController.play(); setState(() {}); });
對於 initialize() 方法來說,這是一個異步的耗時操做.
VideoPlayerValue 記錄了當前視頻播放的一些狀態信息
VideoPlayerValue videoPlayerValue = videoPlayerController.value; ///是否初始化完成 bool initialized = videoPlayerValue.initialized; ///是否正在播放 bool isPlaying = videoPlayerValue.isPlaying; ///當前播放的視頻的寬高比例 double aspectRatio = videoPlayerValue.aspectRatio; ///當前視頻是否緩存 bool isBuffer = videoPlayerValue.isBuffering; ///當前視頻是否循環 bool isLoop = videoPlayerValue.isLooping; ///當前播放視頻的總時長 Duration totalDuration = videoPlayerValue.duration; ///當前播放視頻的位置 Duration currentDuration = videoPlayerValue.position;
在這裏,咱們經過 totalDuration 與 currentDuration 就可實現播放進度的進度條繪製。
完畢</widget></findvideoitempage></statefulwidget></videomodel></videomodel></videomodel></widget></tab></string></statefulwidget>