ソースを参照

Merge pull request #9 from CaiJingLong/dev

Full screen button and effects
Caijinglong 6 年 前
コミット
355648a3a7

+ 5 - 5
TODOLIST.md

@@ -49,11 +49,11 @@
   - [x] 允许根据情况禁用各种控制手势
   - [x] 当一个视频是直播视频时,进度条应该隐藏,且无拖动进度相关手势
   - [ ] 全屏切换
-    - [ ] UI(控制器 UI 内)
-    - [ ] android
-    - [ ] iOS
-    - [ ] 利用 ShowDialog 开发一个全屏的播放界面,不仅仅在 Example 中
-      - [ ] 根据屏幕宽高确定是横屏全屏还是竖屏全屏(比如:常规电影是横屏,类似抖音自拍之类的为竖屏)
+    - [x] UI(控制器 UI 内)
+    - [x] android
+    - [x] iOS
+    - [x] 利用 ShowDialog 开发一个全屏的播放界面,不仅仅在 Example 中
+      - [x] 根据屏幕宽高确定是横屏全屏还是竖屏全屏(比如:常规电影是横屏,常规自拍类视频为竖屏)
 - [x] 根据视频角度自动旋转
 - [x] 保证图片宽高比不失真
 - [x] 允许自定义控制器 UI

+ 61 - 0
example/lib/page/dialog_video_page.dart

@@ -0,0 +1,61 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_ijkplayer/flutter_ijkplayer.dart';
+
+class DialogVideoPage extends StatefulWidget {
+  @override
+  _DialogVideoPageState createState() => _DialogVideoPageState();
+}
+
+class _DialogVideoPageState extends State<DialogVideoPage> {
+  IjkMediaController controller = IjkMediaController();
+
+  @override
+  void dispose() {
+    controller?.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(),
+      body: Center(
+        child: Column(
+          children: <Widget>[
+            AspectRatio(
+              aspectRatio: 1,
+              child: _buildIJKPlayer(),
+            ),
+            FlatButton(
+              child: Text("显示dialog"),
+              onPressed: showIJKDialog,
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+
+  void showIJKDialog() async {
+    await controller.setDataSource(
+      DataSource.network(
+          "http://img.ksbbs.com/asset/Mon_1703/05cacb4e02f9d9e.mp4"),
+    );
+    await controller.play();
+
+    await Future.delayed(Duration(seconds: 2));
+
+    showDialog(
+      context: context,
+      builder: (_) => IjkPlayer(
+            mediaController: controller,
+          ),
+    );
+  }
+
+  _buildIJKPlayer() {
+    return IjkPlayer(
+      mediaController: controller,
+    );
+  }
+}

+ 1 - 1
example/lib/page/full_screen.dart

@@ -154,7 +154,7 @@ class _FullScreen2State extends State<FullScreen2> {
             child: IjkPlayer(
               mediaController: controller,
               controllerWidgetBuilder: (ctl) {
-                return DefaultControllerWidget(
+                return DefaultIJKControllerWidget(
                   controller: ctl,
                   verticalGesture: false,
                 );

+ 2 - 0
example/lib/page/index.dart

@@ -1,5 +1,6 @@
 import 'package:flutter/material.dart';
 import 'package:ijkplayer_example/page/asset_page.dart';
+import 'package:ijkplayer_example/page/dialog_video_page.dart';
 import 'package:ijkplayer_example/page/full_screen.dart';
 import 'package:ijkplayer_example/page/gallery_page.dart';
 import 'package:ijkplayer_example/page/network.dart';
@@ -25,6 +26,7 @@ class _IndexPageState extends State<IndexPage> {
           buildButton("ListView中插入视频(未完成)", VideoList()),
           buildButton("全屏切换示例(自动)", FullScreen()),
           buildButton("全屏切换示例(手动)", FullScreen2()),
+          buildButton("在dialog中播放显示视频", DialogVideoPage()),
         ],
       ),
     );

+ 1 - 1
example/lib/page/video_list.dart

@@ -140,7 +140,7 @@ class _VideoItemState extends State<VideoItem> {
   }
 
   Widget _buildControllerWidget(IjkMediaController controller) {
-    return DefaultControllerWidget(
+    return DefaultIJKControllerWidget(
       controller: controller,
       verticalGesture: false,
     );

+ 1 - 1
lib/flutter_ijkplayer.dart

@@ -1,5 +1,5 @@
 export 'src/error.dart';
 export 'src/ijkplayer.dart';
 export 'package:flutter_ijkplayer/src/entity/video_info.dart';
-export 'src/widget/controller_widget_builder.dart' show DefaultControllerWidget;
+export 'src/widget/controller_widget_builder.dart' show DefaultIJKControllerWidget;
 export 'package:flutter_ijkplayer/src/helper/config.dart';

+ 5 - 0
lib/src/controller.dart

@@ -92,6 +92,10 @@ class IjkMediaController {
   /// video volume, not system volume
   int get volume => _volume;
 
+  VideoInfo _info = VideoInfo.fromMap(null);
+
+  VideoInfo get info => _info;
+
   /// create ijk texture id from native
   Future<void> _initIjk() async {
     try {
@@ -272,6 +276,7 @@ class IjkMediaController {
   /// request info and notify
   Future<void> refreshVideoInfo() async {
     var info = await getVideoInfo();
+    _info = info;
     isPlaying = info.isPlaying;
     if (info.hasData) {
       _videoInfoController?.add(info);

+ 4 - 1
lib/src/engine/manager.dart

@@ -56,7 +56,10 @@ class IjkManager {
   static setLandScape() async {
     if (Platform.isAndroid) {
       SystemChrome.setPreferredOrientations(
-        [DeviceOrientation.landscapeLeft],
+        [
+          DeviceOrientation.landscapeLeft,
+          DeviceOrientation.landscapeRight,
+        ],
       );
       SystemChrome.setEnabledSystemUIOverlays([]);
     } else if (Platform.isIOS) {

+ 2 - 1
lib/src/ijkplayer.dart

@@ -19,7 +19,7 @@ part 'engine/manager.dart';
 class IjkPlayer extends StatefulWidget {
   final IjkMediaController mediaController;
 
-  /// See [DefaultControllerWidget]
+  /// See [DefaultIJKControllerWidget]
   final ControllerWidgetBuilder controllerWidgetBuilder;
 
   /// See [buildDefaultIjkPlayer]
@@ -70,6 +70,7 @@ class IjkPlayerState extends State<IjkPlayer> {
         var id = snapshot.data;
         return StreamBuilder<VideoInfo>(
             stream: controller.videoInfoStream,
+            initialData: controller.info,
             builder: (context, videoInfoSnapShot) {
               return _buildTexture(id, videoInfoSnapShot.data);
             });

+ 92 - 6
lib/src/widget/controller_widget_builder.dart

@@ -2,6 +2,7 @@ import 'dart:async';
 
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
 import 'package:flutter_ijkplayer/flutter_ijkplayer.dart';
 import 'package:flutter_ijkplayer/src/helper/time_helper.dart';
 import 'package:flutter_ijkplayer/src/helper/logutil.dart';
@@ -13,7 +14,7 @@ typedef Widget ControllerWidgetBuilder(IjkMediaController controller);
 
 /// default create IJK Controller UI
 Widget defaultBuildIjkControllerWidget(IjkMediaController controller) {
-  return DefaultControllerWidget(
+  return DefaultIJKControllerWidget(
     controller: controller,
 //    verticalGesture: false,
 //    horizontalGesture: false,
@@ -23,7 +24,7 @@ Widget defaultBuildIjkControllerWidget(IjkMediaController controller) {
 /// Default Controller Widget
 ///
 /// see [IjkPlayer] and [ControllerWidgetBuilder]
-class DefaultControllerWidget extends StatefulWidget {
+class DefaultIJKControllerWidget extends StatefulWidget {
   final IjkMediaController controller;
 
   /// If [doubleTapPlay] is true, can double tap to play or pause media.
@@ -40,23 +41,31 @@ class DefaultControllerWidget extends StatefulWidget {
 
   final bool playWillPauseOther;
 
+  /// Control whether there is a full-screen button.
+  final bool showFullScreenButton;
+
+  /// The current full-screen button style should not be changed by users.
+  final bool currentFullScreenState;
+
   /// The UI of the controller.
-  const DefaultControllerWidget({
+  const DefaultIJKControllerWidget({
     @required this.controller,
     this.doubleTapPlay = false,
     this.verticalGesture = true,
     this.horizontalGesture = true,
     this.volumeType = VolumeType.system,
     this.playWillPauseOther = true,
+    this.currentFullScreenState = false,
+    this.showFullScreenButton = true,
     Key key,
   }) : super(key: key);
 
   @override
-  _DefaultControllerWidgetState createState() =>
-      _DefaultControllerWidgetState();
+  _DefaultIJKControllerWidgetState createState() =>
+      _DefaultIJKControllerWidgetState();
 }
 
-class _DefaultControllerWidgetState extends State<DefaultControllerWidget>
+class _DefaultIJKControllerWidgetState extends State<DefaultIJKControllerWidget>
     implements TooltipDelegate {
   IjkMediaController get controller => widget.controller;
 
@@ -157,12 +166,32 @@ class _DefaultControllerWidgetState extends State<DefaultControllerWidget>
     );
   }
 
+  Widget _buildFullScreenButton() {
+    if (widget.showFullScreenButton != true) {
+      return Container();
+    }
+    var isFull = widget.currentFullScreenState;
+    return IconButton(
+      color: Colors.white,
+      icon: Icon(isFull ? Icons.fullscreen_exit : Icons.fullscreen),
+      onPressed: () {
+        // todo: 这里加入控制全屏和取消全屏的代码, 还需要根据视频宽高决定是竖向全屏还是横向全屏
+        if (isFull) {
+          Navigator.pop(context);
+        } else {
+          showFullScreenIJKPlayer(context, controller);
+        }
+      },
+    );
+  }
+
   Widget buildPortrait(VideoInfo info) {
     return PortraitController(
       controller: controller,
       info: info,
       tooltipDelegate: this,
       playWillPauseOther: widget.playWillPauseOther,
+      fullScreenWidget: _buildFullScreenButton(),
     );
   }
 
@@ -419,6 +448,7 @@ class PortraitController extends StatelessWidget {
   final VideoInfo info;
   final TooltipDelegate tooltipDelegate;
   final bool playWillPauseOther;
+  final Widget fullScreenWidget;
 
   const PortraitController({
     Key key,
@@ -426,6 +456,7 @@ class PortraitController extends StatelessWidget {
     this.info,
     this.tooltipDelegate,
     this.playWillPauseOther = true,
+    this.fullScreenWidget,
   }) : super(key: key);
 
   bool get haveTime {
@@ -455,6 +486,8 @@ class PortraitController extends StatelessWidget {
 
     var playButton = buildPlayButton(context);
 
+    var fullScreenButton = buildFullScreenButton();
+
     Widget widget = Row(
       children: <Widget>[
         playButton,
@@ -467,6 +500,7 @@ class PortraitController extends StatelessWidget {
           padding: const EdgeInsets.all(8.0),
           child: maxTime,
         ),
+        fullScreenButton,
       ],
     );
     widget = DefaultTextStyle(
@@ -565,6 +599,10 @@ class PortraitController extends StatelessWidget {
     var tooltip = tooltipDelegate?.createTooltipWidgetWrapper(text);
     tooltipDelegate?.showTooltip(tooltip);
   }
+
+  Widget buildFullScreenButton() {
+    return fullScreenWidget ?? Container();
+  }
 }
 
 abstract class TooltipDelegate {
@@ -579,3 +617,51 @@ enum VolumeType {
   system,
   media,
 }
+
+showFullScreenIJKPlayer(
+    BuildContext context, IjkMediaController controller) async {
+  showDialog(
+    context: context,
+    builder: (ctx) => IjkPlayer(
+          mediaController: controller,
+          controllerWidgetBuilder: (ctl) =>
+              _buildFullScreenMediaController(ctl, true),
+        ),
+  ).then((_) {
+    IjkManager.unlockOrientation();
+    IjkManager.setCurrentOrientation(DeviceOrientation.portraitUp);
+  });
+  var info = await controller.getVideoInfo();
+
+  Axis axis;
+
+  if (info.width == 0 || info.height == 0) {
+    axis = Axis.horizontal;
+  } else if (info.width > info.height) {
+    if (info.degree == 90 || info.degree == 270) {
+      axis = Axis.vertical;
+    } else {
+      axis = Axis.horizontal;
+    }
+  } else {
+    if (info.degree == 90 || info.degree == 270) {
+      axis = Axis.horizontal;
+    } else {
+      axis = Axis.vertical;
+    }
+  }
+
+  if (axis == Axis.horizontal) {
+    IjkManager.setLandScape();
+  } else {
+    IjkManager.setPortrait();
+  }
+}
+
+Widget _buildFullScreenMediaController(
+    IjkMediaController controller, bool fullScreen) {
+  return DefaultIJKControllerWidget(
+    controller: controller,
+    currentFullScreenState: true,
+  );
+}

+ 59 - 37
lib/src/widget/ijkplayer_builder.dart

@@ -14,54 +14,76 @@ Widget buildDefaultIjkPlayer(
   IjkMediaController controller,
   VideoInfo info,
 ) {
-  double ratio = info?.ratio ?? 1280 / 720;
+  return DefaultIJKPlayerWrapper(
+    controller: controller,
+    info: info,
+  );
+}
 
-  var id = controller.textureId;
+/// Default IJKPlayer Wrapper
+///
+/// This widget solves the aspect ratio problem in video direction.
+class DefaultIJKPlayerWrapper extends StatelessWidget {
+  final IjkMediaController controller;
+  final VideoInfo info;
 
-  if (id == null) {
-    return AspectRatio(
-      aspectRatio: ratio,
-      child: Container(
-        color: Colors.black,
+  const DefaultIJKPlayerWrapper({
+    Key key,
+    this.controller,
+    this.info,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    double ratio = info?.ratio ?? 1280 / 720;
+
+    var id = controller.textureId;
+
+    if (id == null) {
+      return AspectRatio(
+        aspectRatio: ratio,
+        child: Container(
+          color: Colors.black,
+        ),
+      );
+    }
+
+    Widget w = Container(
+      color: Colors.black,
+      child: Texture(
+        textureId: id,
       ),
     );
-  }
 
-  Widget w = Container(
-    color: Colors.black,
-    child: Texture(
-      textureId: id,
-    ),
-  );
+    if (!controller.autoRotate) {
+      return AspectRatio(
+        aspectRatio: null,
+        child: w,
+      );
+    }
 
-  if (!controller.autoRotate) {
-    return AspectRatio(
-      aspectRatio: null,
-      child: w,
-    );
-  }
+    int degree = info?.degree ?? 0;
 
-  int degree = info?.degree ?? 0;
+    if (ratio == 0) {
+      ratio = 1280 / 720;
+    }
 
-  if (ratio == 0) {
-    ratio = 1280 / 720;
-  }
+    w = AspectRatio(
+      aspectRatio: ratio,
+      child: w,
+    );
 
-  w = AspectRatio(
-    aspectRatio: ratio,
-    child: w,
-  );
+    if (degree != 0) {
+      w = RotatedBox(
+        quarterTurns: degree ~/ 90,
+        child: w,
+      );
+    }
 
-  if (degree != 0) {
-    w = RotatedBox(
-      quarterTurns: degree ~/ 90,
+    return Container(
       child: w,
+      alignment: Alignment.center,
+      color: Colors.black,
     );
   }
-
-  return Container(
-    child: w,
-    alignment: Alignment.center,
-    color: Colors.black,
-  );
 }