Parcourir la source

添加动画视频control

hwh97 il y a 6 ans
Parent
commit
db85a5c5f8

+ 18 - 18
analysis_options.yaml

@@ -1,18 +1,18 @@
-analyzer:
-  strong-mode:
-    implicit-casts: false
-    implicit-dynamic: false
-
-linter:
-  rules:
-    - await_only_futures
-    - cancel_subscriptions
-    - close_sinks
-    - hash_and_equals
-    - iterable_contains_unrelated_type
-    - list_remove_unrelated_type
-    - sort_constructors_first
-    - test_types_in_equals
-    - unnecessary_new
-    - unrelated_type_equality_checks
-    - valid_regexps
+#analyzer:
+#  strong-mode:
+#    implicit-casts: false
+#    implicit-dynamic: false
+#
+#linter:
+#  rules:
+#    - await_only_futures
+#    - cancel_subscriptions
+#    - close_sinks
+#    - hash_and_equals
+#    - iterable_contains_unrelated_type
+#    - list_remove_unrelated_type
+#    - sort_constructors_first
+#    - test_types_in_equals
+#    - unnecessary_new
+#    - unrelated_type_equality_checks
+#    - valid_regexps

BIN
assets/dub_user_play/2.0x/back2.png


BIN
assets/dub_user_play/2.0x/leftji.png


BIN
assets/dub_user_play/2.0x/rightji.png


BIN
assets/dub_user_play/2.0x/rightyou.png


BIN
assets/dub_user_play/2.0x/top_bof.png


BIN
assets/dub_user_play/2.0x/top_stop.png


BIN
assets/dub_user_play/2.0x/xin.png


BIN
assets/dub_user_play/3.0x/back2.png


BIN
assets/dub_user_play/3.0x/leftji.png


BIN
assets/dub_user_play/3.0x/rightji.png


BIN
assets/dub_user_play/3.0x/rightyou.png


BIN
assets/dub_user_play/3.0x/top_bof.png


BIN
assets/dub_user_play/3.0x/top_stop.png


BIN
assets/dub_user_play/3.0x/xin.png


BIN
assets/dub_user_play/back2.png


BIN
assets/dub_user_play/leftji.png


BIN
assets/dub_user_play/rightji.png


BIN
assets/dub_user_play/rightyou.png


BIN
assets/dub_user_play/top_bof.png


BIN
assets/dub_user_play/top_stop.png


BIN
assets/dub_user_play/xin.png


+ 155 - 24
example/lib/main.dart

@@ -1,6 +1,5 @@
 import 'package:chewie/chewie.dart';
 import 'package:flutter/cupertino.dart';
-import 'package:flutter/gestures.dart';
 import 'package:flutter/material.dart';
 import 'package:video_player/video_player.dart';
 
@@ -45,28 +44,54 @@ class _ChewieDemoState extends State<ChewieDemo> {
   VideoPlayerController _videoPlayerController2;
   ChewieController _chewieController;
   double aspectRatio=1;
-  I2MaterialControls _i2materialControls;
+  CartoonMaterialControls _i2materialControls;
+  List<MoreVideo> lists = [];
+  int currentVideoIndex=0;
+  Future<void> _initializeVideoPlayerFuture;
+  bool _disposed = false;
+  var _isPlaying = false;
+  var _isEndPlaying = false;
+  bool _firstFullScreen = true;
 
   @override
   void initState() {
     super.initState();
-    _i2materialControls = I2MaterialControls(
-      enableQuickControl: true,
-    );
-    _videoPlayerController1 = VideoPlayerController.network(
-        'http://mirror.aarnet.edu.au/pub/TED-talks/911Mothers_2010W-480p.mp4')
-      ..initialize().then((_){
-        _chewieController = ChewieController(
-          videoPlayerController: _videoPlayerController1,
-          allowedScreenSleep: false,
-          aspectRatio: _videoPlayerController1.value.aspectRatio,
-          autoPlay: true,
-          looping: true,
-          customControls: _i2materialControls,
-        );
-        setState(() {
-        });
+    Future.delayed(Duration(milliseconds: 1000))
+        .then((_){
+      setState(() {
+        lists.addAll([
+          MoreVideo("http://www.i2edu.cn/images/pic04.png", "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",  1200, true),
+          MoreVideo("http://www.i2edu.cn/images/pic04.png", "https://v-cdn.zjol.com.cn/276985.mp4", 1200, false),
+          MoreVideo("http://www.i2edu.cn/images/pic04.png", "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",  1200, false),
+          MoreVideo("http://www.i2edu.cn/images/pic04.png", "https://v-cdn.zjol.com.cn/276985.mp4", 1200, false),
+          MoreVideo("http://www.i2edu.cn/images/pic04.png", "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",  1200, false),
+          MoreVideo("http://www.i2edu.cn/images/pic04.png", "https://v-cdn.zjol.com.cn/276985.mp4", 1200, false),
+          MoreVideo("http://www.i2edu.cn/images/pic04.png", "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",  1200, false),
+          MoreVideo("http://www.i2edu.cn/images/pic04.png", "https://v-cdn.zjol.com.cn/276985.mp4", 1200, false),
+        ]);
       });
+      _i2materialControls = CartoonMaterialControls(
+        enableQuickControl: true,
+        moreVideo: lists,
+        onTapMoreVideo: (index) {
+          print("onTapMoreVideo $index");
+          _startPlay(index);
+        },
+        onTapPrevious: () {
+          if (currentVideoIndex - 1 < 0) return;
+          _startPlay(currentVideoIndex - 1);
+        },
+        onTapNext: () {
+          if (currentVideoIndex + 1 >= lists.length) return;
+          _startPlay(currentVideoIndex + 1);
+        },
+        onTapLove: () {
+
+        },
+      );
+      _initializePlay(0);
+    });
+
     _videoPlayerController2 = VideoPlayerController.network(
         'https://v-cdn.zjol.com.cn/276985.mp4')
     ..initialize().then((_){
@@ -77,12 +102,20 @@ class _ChewieDemoState extends State<ChewieDemo> {
 
   @override
   void dispose() {
+    _disposed = true;
     _videoPlayerController1.dispose();
     _videoPlayerController2.dispose();
     _chewieController.dispose();
     super.dispose();
   }
 
+  void delayEnterFullScreen() async {
+    Future.delayed(Duration(milliseconds: 100))
+        .then((_){
+      _chewieController.toggleFullScreen();
+    });
+  }
+
   @override
   Widget build(BuildContext context) {
     return MaterialApp(
@@ -91,18 +124,14 @@ class _ChewieDemoState extends State<ChewieDemo> {
         platform: _platform ?? Theme.of(context).platform,
       ),
       home: Scaffold(
-//        appBar: AppBar(
-//          title: Text(widget.title),
-//        ),
+        backgroundColor: Colors.black,
         body: Column(
           children: <Widget>[
             Stack(
               children: <Widget>[
                 Container(
 //                  constraints: BoxConstraints(maxHeight: 200),
-                  child: _chewieController == null ? Container() : Chewie(
-                    controller: _chewieController,
-                  ),
+                  child: _playView(),
                   color: Colors.black,
                 ),
               ],
@@ -112,4 +141,106 @@ class _ChewieDemoState extends State<ChewieDemo> {
       ),
     );
   }
+
+  Future<bool> _clearPrevious() async {
+    await _videoPlayerController1?.pause();
+    _videoPlayerController1?.removeListener(_controllerListener);
+    return true;
+  }
+
+  // tracking status
+  Future<void> _controllerListener() async {
+    if (_videoPlayerController1 == null || _disposed) {
+      return;
+    }
+    if (!_videoPlayerController1.value.initialized) {
+      return;
+    }
+    final position = await _videoPlayerController1.position;
+    final duration = _videoPlayerController1.value.duration;
+    if (duration != null && position != null) {
+      final isPlaying = position.inMilliseconds < duration.inMilliseconds;
+      final isEndPlaying = position.inMilliseconds > 0 &&
+          position.inSeconds == duration.inSeconds;
+
+      if (_isPlaying != isPlaying || _isEndPlaying != isEndPlaying) {
+        _isPlaying = isPlaying;
+        _isEndPlaying = isEndPlaying;
+        print(
+            "$currentVideoIndex -----> isPlaying=$isPlaying / isCompletePlaying=$isEndPlaying");
+        if (isEndPlaying) {
+          final isComplete = currentVideoIndex == lists.length - 1;
+          if (isComplete) {
+            print("played all!!");
+          } else {
+            _startPlay(currentVideoIndex + 1);
+          }
+        }
+      }
+    }
+  }
+
+  Future<void> _startPlay(int index) async {
+    print("play ---------> $index");
+    setState(() {
+      _initializeVideoPlayerFuture = null;
+    });
+    Future.delayed(const Duration(milliseconds: 200), () {
+      _clearPrevious().then((_){
+        _initializePlay(index);
+      });
+    });
+  }
+
+  Future<void> _initializePlay(int index) async {
+    _videoPlayerController1 = VideoPlayerController.network(lists[index].videoUrl);
+    _videoPlayerController1.addListener(_controllerListener);
+
+    _chewieController = ChewieController(
+        videoPlayerController: _videoPlayerController1,
+        customControls: _i2materialControls,
+        fullScreenByDefault: true,
+        forceFullScreen: true,
+        allowedScreenSleep: false,
+        showControls: true,
+//        showControlsOnInitialize: true,
+    );
+    _chewieController?.addListener((){
+      if (_videoPlayerController1 != null && !_chewieController.isFullScreen) {
+        Navigator.of(context).maybePop();
+      }
+    });
+    _initializeVideoPlayerFuture = _videoPlayerController1.initialize();
+
+    setState(() {
+      currentVideoIndex = index;
+      for (int i = 0; i < lists.length; i++) {
+        lists[i].isSelect = (i == index);
+      }
+    });
+  }
+
+  // play view area
+  Widget _playView() {
+    // FutureBuilder to display a loading spinner until finishes initializing
+    return FutureBuilder(
+      future: _initializeVideoPlayerFuture,
+      builder: (BuildContext context, AsyncSnapshot snapshot) {
+        if (snapshot.connectionState == ConnectionState.done) {
+          _chewieController.play();
+          return AspectRatio(
+            aspectRatio: _videoPlayerController1.value.aspectRatio,
+            child: Chewie(controller: _chewieController),
+          );
+        } else {
+          return SizedBox(
+            height: MediaQuery.of(context).size.height,
+            child: Center(child: CircularProgressIndicator()),
+          );
+        }
+      },
+    );
+  }
+
+
 }

+ 1 - 1
example/pubspec.yaml

@@ -8,7 +8,7 @@ dependencies:
     #    path: ../plugins/chewie/
 #    git:
 #      url: https://git.i2erp.cn/plugins/video_chewie.git
-  video_player: ^0.10.0+2
+  video_player: ^0.10.1
   auto_orientation: ^0.0.2
   flutter:
     sdk: flutter

+ 3 - 1
lib/chewie.dart

@@ -3,4 +3,6 @@ library chewie;
 export 'src/chewie_player.dart';
 export 'src/chewie_progress_colors.dart';
 export 'src/i2_material_controls.dart';
-export 'src/i2_control_event.dart';
+export 'src/i2_control_event.dart';
+export 'src/more_video.dart';
+export 'src/cartoon_material_controls.dart';

+ 747 - 0
lib/src/cartoon_material_controls.dart

@@ -0,0 +1,747 @@
+import 'dart:async';
+
+import 'package:chewie/chewie.dart';
+import 'package:chewie/src/chewie_player.dart';
+import 'package:chewie/src/chewie_progress_colors.dart';
+import 'package:chewie/src/i2_control_event.dart';
+import 'package:chewie/src/material_progress_bar.dart';
+import 'package:chewie/src/utils.dart';
+import 'package:flutter/material.dart';
+import 'package:screen/screen.dart';
+import 'package:sys_volume/flutter_volume.dart';
+import 'package:video_player/video_player.dart';
+
+class CartoonMaterialControls extends StatefulWidget {
+  final bool enableQuickControl;
+  final List<MoreVideo> moreVideo;
+  final OnTapMoreVideo onTapMoreVideo;
+  final VoidCallback onTapNext;
+  final VoidCallback onTapPrevious;
+  final VoidCallback onTapLove;
+
+  const CartoonMaterialControls({Key key, this.enableQuickControl = false, this.moreVideo, this.onTapMoreVideo, this.onTapNext, this.onTapPrevious, this.onTapLove}) : super(key: key);
+
+  @override
+  State<StatefulWidget> createState() {
+    return CartoonMaterialControlsState();
+  }
+}
+
+enum CartoonVerticalMode {
+  Volume,
+  Bright
+}
+
+typedef void OnTapMoreVideo(int index);
+
+class CartoonMaterialControlsState extends State<CartoonMaterialControls> {
+  VideoPlayerValue _latestValue;
+  double _latestVolume;
+  bool _hideStuff = true;
+  Timer _hideTimer;
+  Timer _initTimer;
+  Timer _showAfterExpandCollapseTimer;
+  bool _dragging = false;
+  bool _displayTapped = false;
+
+  final barHeight = 36.0;
+  final marginSize = 5.0;
+
+  VideoPlayerController controller;
+  ChewieController chewieController;
+
+  /// 横向视频进度拖动 BiLiBiLi的逻辑 duration > 90 ? 90s : duration * 0.8
+  double startDx;
+  double lastDx;
+  Duration dragDuration;
+  Duration updateDragDuration;
+  /// 竖向音量亮度拖动 100%调整的逻辑
+  double startDy;
+  double endDy;
+  CartoonVerticalMode _CartoonVerticalMode;
+  double currentBright;
+  double currentVolume;
+  String valueText;
+  StreamSubscription _eventBusStreamSubscription;
+
+  @override
+  Widget build(BuildContext context) {
+    if (_latestValue.hasError) {
+      return chewieController.errorBuilder != null
+          ? chewieController.errorBuilder(
+        context,
+        chewieController.videoPlayerController.value.errorDescription,
+      )
+          : Center(
+        child: Icon(
+          Icons.error,
+          color: Colors.white,
+          size: 42,
+        ),
+      );
+    }
+
+    return MouseRegion(
+      onHover: (_) {
+        _cancelAndRestartTimer();
+      },
+      child: GestureDetector(
+        onTap: () => _cancelAndRestartTimer(),
+        onVerticalDragStart: (DragStartDetails details) async {
+          if (!widget.enableQuickControl) {
+            return;
+          }
+          // 判断距离 只响应2/5的左边和2/5的右边
+          if (details.globalPosition.dx < MediaQuery.of(context).size.width / 5 * 2) {
+            // 响应亮度
+            startDy = details.globalPosition.dy;
+            _CartoonVerticalMode = CartoonVerticalMode.Bright;
+            currentBright = await Screen.brightness;
+          } else if (details.globalPosition.dx > MediaQuery.of(context).size.width / 5 * 3) {
+            // 响应音量
+            startDy = details.globalPosition.dy;
+            _CartoonVerticalMode = CartoonVerticalMode.Volume;
+            currentVolume = await FlutterVolume.get();
+          } else {
+            // 中间位置不响应 reset
+            setState(() {
+              _CartoonVerticalMode = null;
+              currentBright = null;
+              currentVolume = null;
+              startDy = null;
+              endDy = null;
+            });
+          }
+        },
+        onVerticalDragUpdate: (DragUpdateDetails details) async {
+          if (!widget.enableQuickControl) {
+            return;
+          }
+          if (_CartoonVerticalMode != null) {
+            // 显示数值(百分比)
+            setState(() {
+              endDy = details.globalPosition.dy;
+            });
+            if (_CartoonVerticalMode != null && endDy != null && startDy != null) {
+              double value;
+              if (_CartoonVerticalMode == CartoonVerticalMode.Bright) {
+                if (currentBright == null) return;
+                if (endDy > startDy) {
+                  // 降低操作 竖屏环境?????
+                  value = (currentBright - (endDy - startDy) / MediaQuery.of(context).size.height) < 0
+                      ? 0
+                      : (currentBright - (endDy - startDy) / MediaQuery.of(context).size.height);
+                } else {
+                  // 拉高操作 竖屏环境?????
+                  value = (currentBright + (startDy - endDy) / MediaQuery.of(context).size.height) >= 1
+                      ? 1
+                      : (currentBright + (startDy - endDy) / MediaQuery.of(context).size.height);
+                }
+                Screen.setBrightness(value);
+              } else {
+                if (currentVolume == null) return;
+                if (endDy > startDy) {
+                  // 降低操作 竖屏环境?????
+                  value = (currentVolume - (endDy - startDy) / MediaQuery.of(context).size.height) < 0
+                      ? 0
+                      : (currentVolume - (endDy - startDy) / MediaQuery.of(context).size.height);
+                } else {
+                  // 拉高操作 竖屏环境?????
+                  value = (currentVolume + (startDy - endDy) / MediaQuery.of(context).size.height) >= 1
+                      ? 1
+                      : (currentVolume + (startDy - endDy) / MediaQuery.of(context).size.height);
+                }
+                FlutterVolume.set(value);
+              }
+              if (value == 1 || value == 0) {
+                // reset dy
+                startDy = endDy;
+                currentBright = await Screen.brightness;
+                currentVolume = await FlutterVolume.get();
+              }
+              setState(() {
+                valueText = (value * 100).toStringAsFixed(0);
+              });
+            }
+
+          }
+        },
+        onVerticalDragEnd: (DragEndDetails details){
+          if (!widget.enableQuickControl) {
+            return;
+          }
+          setState(() {
+            _CartoonVerticalMode = null;
+            currentBright = null;
+            startDy = null;
+            endDy = null;
+          });
+        },
+        onHorizontalDragStart: (DragStartDetails detail) {
+          if (!widget.enableQuickControl) {
+            return;
+          }
+          if (_latestValue.duration == null) return;
+          _cancelAndRestartTimer();
+          startDx = detail.localPosition.dx;
+          dragDuration = _latestValue.position;
+        },
+        onHorizontalDragUpdate: (DragUpdateDetails detail) {
+          if (!widget.enableQuickControl) {
+            return;
+          }
+          if (_latestValue.duration == null) return;
+          _cancelAndRestartTimer();
+          lastDx = detail.localPosition.dx;
+          // update progress bar
+          Duration finalDuration = getDragDuration();
+
+          setState(() {
+            updateDragDuration = finalDuration;
+          });
+        },
+        onHorizontalDragCancel: (){
+          if (!widget.enableQuickControl) {
+            return;
+          }
+          startDx = null;
+          lastDx = null;
+          dragDuration = null;
+          setState(() {
+            updateDragDuration = null;
+          });
+        },
+        onHorizontalDragEnd: (DragEndDetails detail) {
+          if (!widget.enableQuickControl) {
+            return;
+          }
+          if (startDx != null && lastDx != null && _latestValue.duration != null && dragDuration != null) {
+            Duration finalDuration = getDragDuration();
+            chewieController.seekTo(finalDuration);
+          }
+          startDx = null;
+          lastDx = null;
+          dragDuration = null;
+          setState(() {
+            updateDragDuration = null;
+          });
+        },
+        child: AbsorbPointer(
+          absorbing: _hideStuff,
+          child: Stack(
+            children: <Widget>[
+              Column(
+                children: <Widget>[
+                  _latestValue != null &&
+                      !_latestValue.isPlaying &&
+                      _latestValue.duration == null ||
+                      _latestValue.isBuffering
+                      ? const Expanded(
+                    child: const Center(
+                      child: const CircularProgressIndicator(),
+                    ),
+                  )
+                      : _buildHitArea(),
+                  _buildBottomBar(context),
+                ],
+              ),
+              _buildTopBar(context),
+              _buildDragProgress(),
+              _buildVerticalWidget(),
+            ],
+          )
+        ),
+      ),
+    );
+  }
+
+  @override
+  void initState() {
+    super.initState();
+    _initState();
+  }
+
+  void _initState() async {
+    FlutterVolume.disableUI();
+    _eventBusStreamSubscription = i2ControlEventBus.on<I2ControllerEvent>().listen((event){
+      if (event == I2ControllerEvent.ShowControl) {
+        _showControlWithoutTimer();
+      }
+    });
+  }
+
+  @override
+  void dispose() {
+    _eventBusStreamSubscription.cancel();
+    Screen.resetAndroidBrightness();
+    _dispose();
+    super.dispose();
+  }
+
+  void _dispose() async {
+    controller.removeListener(_updateState);
+    _hideTimer?.cancel();
+    _initTimer?.cancel();
+    _showAfterExpandCollapseTimer?.cancel();
+  }
+
+  @override
+  void didChangeDependencies() {
+    final _oldController = chewieController;
+    chewieController = ChewieController.of(context);
+    controller = chewieController.videoPlayerController;
+
+    if (_oldController != chewieController) {
+      _dispose();
+      _initialize();
+    }
+
+    super.didChangeDependencies();
+  }
+
+  Duration getDragDuration() {
+    if (lastDx - startDx > 0) {
+      // 前进
+      int forwardSec;
+      if (_latestValue.duration.inSeconds > 90) {
+        forwardSec = ((lastDx - startDx) / MediaQuery.of(context).size.width * 90).toInt();
+      } else {
+        forwardSec = ((lastDx - startDx) / MediaQuery.of(context).size.width * _latestValue.duration.inSeconds * 0.8).toInt();
+      }
+      Duration duration = dragDuration + Duration(seconds: forwardSec);
+      return duration >= _latestValue.duration ? _latestValue.duration : duration;
+    } else {
+      // 后退
+      int backwardSec;
+      if (_latestValue.duration.inSeconds > 90) {
+        backwardSec = ((lastDx - startDx) * -1 / MediaQuery.of(context).size.width * 90).toInt();
+      } else {
+        backwardSec = ((lastDx - startDx) * -1 / MediaQuery.of(context).size.width * _latestValue.duration.inSeconds * 0.8).toInt();
+      }
+      Duration duration = (Duration(seconds: backwardSec) > dragDuration) ? Duration.zero : dragDuration - Duration(seconds: backwardSec);
+      return duration;
+    }
+  }
+
+  Widget _buildDragProgress() {
+    final position = updateDragDuration ?? (_latestValue != null && _latestValue.position != null
+        ? _latestValue.position
+        : Duration.zero);
+    return Offstage(
+      offstage: updateDragDuration == null,
+      child: Align(
+        alignment: Alignment.center,
+        child: Container(
+          width: 96,
+          height: 34,
+          decoration: BoxDecoration(
+            color: Color(0xB0000000),
+            borderRadius: BorderRadius.circular(4),
+          ),
+          alignment: Alignment.center,
+          child: Text("${formatDuration(position)}/${formatDuration(_latestValue.duration)}", style: TextStyle(color: Colors.white, fontSize: 12),),
+        ),
+      ),
+    );
+  }
+
+  Widget _buildVerticalWidget() {
+//    ((endDy - startDy) / widgetHeight * 100).toStringAsFixed(2)
+    // 正方圆角
+    return Offstage(
+      offstage: _CartoonVerticalMode == null,
+      child: Align(
+        alignment: Alignment.center,
+        child: Container(
+          width: 80,
+          height: 80,
+          decoration: BoxDecoration(
+            color: Color(0xB0000000),
+            borderRadius: BorderRadius.circular(4),
+          ),
+          alignment: Alignment.center,
+          child: Column(
+            mainAxisAlignment: MainAxisAlignment.center,
+            mainAxisSize: MainAxisSize.max,
+            children: <Widget>[
+              Icon(_CartoonVerticalMode == CartoonVerticalMode.Volume ? Icons.volume_up : Icons.brightness_6, color: Colors.white, size: 24,),
+              Padding(
+                padding: const EdgeInsets.only(top: 10),
+                child: Text("${valueText}%", style: TextStyle(color: Colors.white, fontSize: 15,),),
+              ),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  AnimatedOpacity _buildTopBar(
+      BuildContext context,
+      ) {
+    return AnimatedOpacity(
+      opacity: _hideStuff ? 0.0 : 1.0,
+      duration: Duration(milliseconds: 300),
+      child: Container(
+          decoration: BoxDecoration(
+            color: Colors.white,
+            borderRadius: BorderRadius.only(bottomRight: Radius.circular(10), bottomLeft: Radius.circular(10)),
+          ),
+//          color: chewieController.isFullScreen ? Colors.black26 : Colors.transparent,
+          alignment: Alignment.center,
+          margin: EdgeInsets.only(left: (MediaQuery.of(context).size.width - 280) / 2),
+          width: 300,
+          height: 46,
+//          margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top != 0 ? MediaQuery.of(context).padding.top : 0),
+          padding: EdgeInsets.only(left: 13,),
+          child: Row(
+            children: <Widget>[
+              Expanded(
+                child: GestureDetector(
+                  child: Image(image:  AssetImage("assets/dub_user_play/back2.png", package: 'chewie'), width: 20, height: 20,),
+                  onTap: onTapBack,
+                ),
+              ),
+              Expanded(
+                  child: GestureDetector(
+                    child: Image(image:  AssetImage("assets/dub_user_play/rightyou.png", package: 'chewie'), width: 20, height: 20,),
+                    onTap: widget.onTapPrevious,
+                  ),
+              ),
+              Expanded(
+                 child: GestureDetector(
+                   child: Image(image:  AssetImage(controller.value.isPlaying ? "assets/dub_user_play/top_stop.png" : "assets/dub_user_play/top_bof.png" , package: 'chewie'), width: 20, height: 20,),
+                   onTap: _playPause,
+                 ),
+              ),
+              Expanded(
+                  child: GestureDetector(
+                    child: Image(image:  AssetImage("assets/dub_user_play/rightji.png", package: 'chewie'), width: 20, height: 20,),
+                    onTap: widget.onTapNext,
+                  ),
+              ),
+              Expanded(
+                  child: GestureDetector(
+                    child: Image(image:  AssetImage("assets/dub_user_play/xin.png", package: 'chewie'), width: 20, height: 20,),
+                    onTap: widget.onTapLove,
+                  ),
+              ),
+            ],
+          )
+      ),
+    );
+  }
+
+  AnimatedOpacity _buildBottomBar(
+      BuildContext context,
+      ) {
+    final iconColor = Theme.of(context).textTheme.button.color;
+
+    return AnimatedOpacity(
+      opacity: _hideStuff ? 0.0 : 1.0,
+      duration: Duration(milliseconds: 300),
+      child: Column(
+        children: <Widget>[
+          Container(
+            height: barHeight + (widget.moreVideo.length == 0 ? 0 : 80),
+            decoration: BoxDecoration(
+              gradient: const LinearGradient(
+                  colors: [Colors.black38, Colors.transparent], begin: Alignment.bottomCenter, end: Alignment.topCenter, stops: [0.0, 1]),
+            ),
+            child: Column(
+              children: <Widget>[
+                SizedBox(
+                  height: barHeight,
+                  child: Row(
+                    children: <Widget>[
+                      chewieController.isLive
+                          ? Expanded(child: const Text('LIVE'))
+                          : _buildPosition(iconColor),
+                      chewieController.isLive ? const SizedBox() : _buildProgressBar(),
+                      chewieController.isLive
+                          ? const SizedBox()
+                          : _buildFullPosition(iconColor),
+                    ],
+                  ),
+                ),
+                Offstage(
+                  offstage: widget.moreVideo.length == 0,
+                  child: SizedBox(
+                      height: 80,
+                      child: Listener(
+                        child: ListView.builder(
+                          scrollDirection: Axis.horizontal,
+                          shrinkWrap: true,
+                          itemCount: widget.moreVideo.length,
+                          itemBuilder: (ctx, index){
+                            return GestureDetector(
+                              child: Container(
+                                width: 100,
+                                height: 60,
+                                alignment: Alignment.center,
+                                decoration: BoxDecoration(
+                                  color: Colors.white,
+                                  borderRadius: BorderRadius.circular(15),
+                                  border: widget.moreVideo[index].isSelect ? Border.all(color: Color(0xFFEF765E), width: 3) : null,
+                                ),
+                                margin: EdgeInsets.only(left: 10, right: 10, bottom: 10),
+                                child: ClipRRect(
+                                  borderRadius: BorderRadius.circular(15),
+                                  child: Image.network(widget.moreVideo[index].imageUrl, fit: BoxFit.fill,),
+                                ),
+                              ),
+                              onTap: () {
+                                widget?.onTapMoreVideo(index);
+                              },
+                            );
+                          },
+                        ),
+                        onPointerDown: onMoreVideoPointerDown,
+                        onPointerUp: onMoreVideoPointerUp,
+                      )
+                  ),
+                ),
+              ],
+            )
+          ),
+        ],
+      )
+    );
+  }
+
+  GestureDetector _buildExpandButton() {
+    return GestureDetector(
+      onTap: _onExpandCollapse,
+      child: AnimatedOpacity(
+        opacity: _hideStuff ? 0.0 : 1.0,
+        duration: Duration(milliseconds: 300),
+        child: Container(
+          height: barHeight,
+          margin: EdgeInsets.only(right: 12.0),
+          padding: EdgeInsets.only(
+            left: 8.0,
+            right: 8.0,
+          ),
+          child: Center(
+              child: Image(image: AssetImage("assets/dub_user_play/${chewieController.isFullScreen ? "shousuo.png" : "fangda.png"}", package: 'chewie'), width: 20, height: 20,),
+          ),
+        ),
+      ),
+    );
+  }
+
+  Expanded _buildHitArea() {
+    return Expanded(
+      child: GestureDetector(
+        onTap: () {
+          if (_latestValue != null && _latestValue.isPlaying) {
+            if (_displayTapped) {
+              setState(() {
+                _hideStuff = true;
+              });
+            } else
+              _cancelAndRestartTimer();
+          } else {
+            _playPause();
+            setState(() {
+              _hideStuff = true;
+            });
+          }
+        },
+        child: Container(
+          color: Colors.transparent,
+          child: Center(
+            child: AnimatedOpacity(
+              opacity:
+              _latestValue != null && !_latestValue.isPlaying && !_dragging
+                  ? 1.0
+                  : 0.0,
+              duration: Duration(milliseconds: 300),
+              child: GestureDetector(
+                child: Container(
+                  decoration: BoxDecoration(
+                    color: Theme.of(context).dialogBackgroundColor,
+                    borderRadius: BorderRadius.circular(48.0),
+                  ),
+                  child: Padding(
+                    padding: EdgeInsets.all(12.0),
+                    child: Icon(Icons.play_arrow, size: 32.0, color: Colors.black,),
+                  ),
+                ),
+              ),
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+
+  Widget _buildPosition(Color iconColor) {
+    final position = updateDragDuration ?? (_latestValue != null && _latestValue.position != null
+        ? _latestValue.position
+        : Duration.zero);
+    final duration = _latestValue != null && _latestValue.duration != null
+        ? _latestValue.duration
+        : Duration.zero;
+
+    return Padding(
+      padding: EdgeInsets.only(left: 20.0, right: 14.0),
+      child: Text(
+        '${formatDuration(position)}',
+        style: TextStyle(
+          color: Colors.white,
+          fontSize: 12.0,
+        ),
+      ),
+    );
+  }
+
+  Widget _buildFullPosition(Color iconColor) {
+    final position = _latestValue != null && _latestValue.position != null
+        ? _latestValue.position
+        : Duration.zero;
+    final duration = _latestValue != null && _latestValue.duration != null
+        ? _latestValue.duration
+        : Duration.zero;
+
+    return Padding(
+      padding: EdgeInsets.only(left: 14.0, right: 20.0),
+      child: Text(
+        '${formatDuration(duration)}',
+        style: TextStyle(
+          color: Colors.white,
+          fontSize: 12.0,
+        ),
+      ),
+    );
+  }
+
+  void _cancelAndRestartTimer() {
+    _hideTimer?.cancel();
+    _startHideTimer();
+    setState(() {
+      _hideStuff = false;
+      _displayTapped = true;
+    });
+  }
+
+  void _showControlWithoutTimer() {
+    _hideTimer?.cancel();
+    setState(() {
+      _hideStuff = false;
+      _displayTapped = true;
+    });
+  }
+
+  Future<Null> _initialize() async {
+    controller.addListener(_updateState);
+
+    _updateState();
+
+    if ((controller.value != null && controller.value.isPlaying) ||
+        chewieController.autoPlay) {
+      _startHideTimer();
+    }
+
+    if (chewieController.showControlsOnInitialize) {
+      _initTimer = Timer(Duration(milliseconds: 200), () {
+        setState(() {
+          _hideStuff = false;
+        });
+      });
+    }
+  }
+
+  void _onExpandCollapse() {
+    setState(() {
+      _hideStuff = true;
+      chewieController.toggleFullScreen();
+      _showAfterExpandCollapseTimer = Timer(Duration(milliseconds: 300), () {
+        setState(() {
+          _cancelAndRestartTimer();
+        });
+      });
+    });
+  }
+
+  void _playPause() {
+    bool isFinished = _latestValue.position >= _latestValue.duration;
+
+    setState(() {
+      if (controller.value.isPlaying) {
+        _hideStuff = false;
+        _hideTimer?.cancel();
+        controller.pause();
+      } else {
+        _cancelAndRestartTimer();
+
+        if (!controller.value.initialized) {
+          controller.initialize().then((_) {
+            controller.play();
+          });
+        } else {
+          if (isFinished) {
+            controller.seekTo(Duration(seconds: 0));
+          }
+          controller.play();
+        }
+      }
+    });
+  }
+
+  void _startHideTimer() {
+    _hideTimer = Timer(const Duration(seconds: 3), () {
+      setState(() {
+        _hideStuff = true;
+      });
+    });
+  }
+
+  void _updateState() {
+    setState(() {
+      _latestValue = controller.value;
+    });
+  }
+
+  Widget _buildProgressBar() {
+    return Expanded(
+      child: MaterialVideoProgressBar(
+        controller,
+        onDragStart: () {
+          setState(() {
+            _dragging = true;
+          });
+
+          _hideTimer?.cancel();
+        },
+        onDragEnd: () {
+          setState(() {
+            _dragging = false;
+          });
+
+          _startHideTimer();
+        },
+        colors: chewieController.materialProgressColors ??
+            ChewieProgressColors(
+                playedColor: Color(0xFFC7000B),
+                handleColor: Color(0xFFC7000B),
+                bufferedColor:  Color(0x5AC7000B),
+                backgroundColor: Color(0x5AFFFFFF)),
+        millionSec: updateDragDuration?.inMilliseconds,
+      ),
+    );
+  }
+
+  void onTapBack() {
+    print("onTapBack");
+    chewieController.exitFullScreen();
+  }
+
+  void onMoreVideoPointerDown(PointerDownEvent event) {
+    _hideTimer?.cancel();
+  }
+
+  void onMoreVideoPointerUp(PointerUpEvent event) {
+    _startHideTimer();
+  }
+}

+ 34 - 9
lib/src/chewie_player.dart

@@ -42,6 +42,12 @@ class ChewieState extends State<Chewie> {
   @override
   void initState() {
     super.initState();
+    if (widget.controller.fullScreenByDefault) {
+      Future.delayed(Duration.zero).then((_){
+        _isFullScreen = true;
+        _pushFullScreenWidget(context);
+      });
+    }
     widget.controller.addListener(listener);
   }
 
@@ -64,7 +70,9 @@ class ChewieState extends State<Chewie> {
       _isFullScreen = true;
       await _pushFullScreenWidget(context);
     } else if (_isFullScreen) {
-      Navigator.of(context, rootNavigator: true).pop();
+      if (!widget.controller.forceFullScreen) {
+        Navigator.of(context, rootNavigator: true).pop();
+      }
       _isFullScreen = false;
     }
   }
@@ -81,16 +89,23 @@ class ChewieState extends State<Chewie> {
       BuildContext context,
       Animation<double> animation,
       _ChewieControllerProvider controllerProvider) {
-    return Scaffold(
-      resizeToAvoidBottomPadding: false,
-      body: Container(
-        alignment: Alignment.center,
-        color: Colors.black,
-        child: controllerProvider,
-      ),
+    return WillPopScope(
+        child: Scaffold(
+          resizeToAvoidBottomPadding: false,
+          body: Container(
+            alignment: Alignment.center,
+            color: Colors.black,
+            child: controllerProvider,
+          ),
+        ),
+        onWillPop: _onPop,
     );
   }
 
+  Future<bool> _onPop() async {
+    return true;
+  }
+
   AnimatedWidget _defaultRoutePageBuilder(
       BuildContext context,
       Animation<double> animation,
@@ -142,10 +157,16 @@ class ChewieState extends State<Chewie> {
       Wakelock.enable();
     }
 
-    await Navigator.of(context, rootNavigator: true).push(route);
+    /// not work yet
+//    if (widget.controller.forceFullScreen) {
+//      await Navigator.pushAndRemoveUntil(context, route, (Route route) => route == null);
+//    } else {
+      await Navigator.of(context, rootNavigator: true).push(route);
+//    }
     _isFullScreen = false;
     widget.controller.exitFullScreen();
 
+
     // The wakelock plugins checks whether it needs to perform an action internally,
     // so we do not need to check Wakelock.isEnabled.
     Wakelock.disable();
@@ -188,6 +209,7 @@ class ChewieController extends ChangeNotifier {
     this.isLive = false,
     this.allowFullScreen = true,
     this.allowMuting = true,
+    this.forceFullScreen = false,
     this.systemOverlaysAfterFullScreen = SystemUiOverlay.values,
     this.deviceOrientationsAfterFullScreen = const [
       DeviceOrientation.portraitUp,
@@ -266,6 +288,9 @@ class ChewieController extends ChangeNotifier {
   /// Defines if the mute control should be shown
   final bool allowMuting;
 
+  /// Defines if force full screen
+  final bool forceFullScreen;
+
   /// Defines the system overlays visible after exiting fullscreen
   final List<SystemUiOverlay> systemOverlaysAfterFullScreen;
 

+ 9 - 0
lib/src/more_video.dart

@@ -0,0 +1,9 @@
+
+class MoreVideo {
+  String imageUrl;
+  String videoUrl;
+  int timing;
+  bool isSelect;
+
+  MoreVideo(this.imageUrl, this.videoUrl, this.timing, this.isSelect);
+}

+ 1 - 0
lib/src/utils.dart

@@ -1,4 +1,5 @@
 String formatDuration(Duration position) {
+  if (position == null) return "null";
   final ms = position.inMilliseconds;
 
   int seconds = ms ~/ 1000;

+ 1 - 2
pubspec.yaml

@@ -7,8 +7,7 @@ authors:
   - Ben Hagen <ben@ottomatic.io>
 
 environment:
-  sdk: ">=2.0.0-dev.28.0 <3.0.0"
-  flutter: ">=1.9.1 <2.0.0"
+  sdk: ">=2.2.2 <3.0.0"
 
 dependencies:
   open_iconic_flutter: ">=0.3.0 <0.4.0"