quick_preview.dart 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import 'package:fis_ui/index.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:fis_measure/index.dart';
  4. import 'package:fis_measure/view/player/controller_old.dart';
  5. import 'package:fis_measure/view/player/enums.dart';
  6. import 'package:fis_measure/view/player/events.dart';
  7. import 'package:fis_vid/data_host/data_host.dart';
  8. /// 用于图像快速预览的简单播放器
  9. /// 构造结束后自动播放一遍,然后显示第一帧
  10. class FQuickPreview extends StatefulWidget {
  11. final String vidUrl;
  12. final String imageUrl;
  13. final double? width;
  14. final double? height;
  15. FQuickPreview(
  16. {required this.vidUrl, required this.imageUrl, this.width, this.height});
  17. @override
  18. _FQuickPreviewState createState() => _FQuickPreviewState();
  19. }
  20. class _FQuickPreviewState extends State<FQuickPreview> {
  21. late final VidPlayerControllerNoSharing _playerController;
  22. bool isShowCover = true;
  23. bool isShowLoading = true;
  24. @override
  25. void initState() {
  26. final dataHost = VidDataHost(widget.vidUrl);
  27. final playerController = VidPlayerControllerNoSharing(dataHost: dataHost);
  28. _playerController = playerController;
  29. loadVidDataHost(_playerController);
  30. _playerController.eventHandler.addListener(onControllerEvent);
  31. super.initState();
  32. }
  33. @override
  34. void dispose() {
  35. _playerController.eventHandler.removeListener(onControllerEvent);
  36. _playerController.dispose();
  37. super.dispose();
  38. }
  39. void onControllerEvent(Object sender, VidPlayerEvent e) {
  40. if (e is VidPlayerStatusChangeEvent) {
  41. if (e.status == VidPlayStatus.pause) {
  42. setState(() {
  43. isShowCover = true;
  44. });
  45. }
  46. }
  47. }
  48. void loadVidDataHost(VidPlayerControllerNoSharing playerController) async {
  49. int retryCount = 0;
  50. while (retryCount < 3) {
  51. try {
  52. bool success = await playerController.load();
  53. if (!success) {
  54. throw "loadVidDataHost failed";
  55. }
  56. setState(() {
  57. isShowLoading = false;
  58. });
  59. if (playerController.totalFramesCount <= 1) {
  60. return; // 单帧图只显示封面,不播放
  61. }
  62. playerController.play();
  63. await Future.delayed(const Duration(milliseconds: 100));
  64. setState(() {
  65. isShowCover = false;
  66. });
  67. return;
  68. } catch (e) {
  69. retryCount++;
  70. if (retryCount == 3) {
  71. setState(() {
  72. isShowLoading = false;
  73. });
  74. } else if (!mounted) {
  75. return; // 如果组件已经被销毁,则停止重试
  76. }
  77. }
  78. }
  79. }
  80. @override
  81. Widget build(BuildContext context) {
  82. return Stack(children: <Widget>[
  83. Offstage(
  84. offstage: !isShowCover,
  85. child: Container(
  86. child: Image.network(
  87. widget.imageUrl,
  88. width: widget.width,
  89. height: widget.height,
  90. fit: BoxFit.contain,
  91. loadingBuilder: (context, child, loadingProgress) =>
  92. loadingProgress == null
  93. ? child
  94. : Center(
  95. child: CircularProgressIndicator(
  96. value: loadingProgress.expectedTotalBytes != null
  97. ? loadingProgress.cumulativeBytesLoaded /
  98. loadingProgress.expectedTotalBytes!
  99. : null,
  100. ),
  101. ),
  102. ),
  103. ),
  104. ),
  105. // 使用 Offstage 组件来控制第二个组件的可见性
  106. Offstage(
  107. offstage: isShowCover,
  108. child: Center(
  109. child: RepaintBoundary(
  110. child: VidPlayer(_playerController),
  111. ),
  112. )),
  113. Align(
  114. alignment: Alignment.topRight,
  115. child: Offstage(
  116. child: Container(
  117. padding: EdgeInsets.all(10),
  118. width: 50,
  119. height: 50,
  120. child: CircularProgressIndicator(),
  121. ),
  122. offstage: !isShowLoading,
  123. )),
  124. ]);
  125. }
  126. }