Skip to content

Commit 237c70a

Browse files
committed
Use video_thumbnail instead of player
1 parent e591be5 commit 237c70a

File tree

2 files changed

+51
-57
lines changed

2 files changed

+51
-57
lines changed

packages/flyer_chat_video_message/lib/src/flyer_chat_video_message.dart

Lines changed: 50 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import 'dart:convert';
22
import 'dart:io';
33

4+
import 'package:flutter/foundation.dart';
45
import 'package:flutter/material.dart';
6+
import 'package:flutter/services.dart';
57
import 'package:flutter_chat_core/flutter_chat_core.dart';
68
import 'package:provider/provider.dart';
79
import 'package:thumbhash/thumbhash.dart'
810
show rgbaToBmp, thumbHashToApproximateAspectRatio, thumbHashToRGBA;
911
import 'package:video_player/video_player.dart';
12+
import 'package:video_thumbnail/video_thumbnail.dart';
1013
import 'helpers/is_network_source.dart';
1114
import 'widgets/full_screen_video_player.dart';
1215
import 'widgets/hero_video_route.dart';
@@ -30,14 +33,11 @@ class FlyerChatVideoMessage extends StatefulWidget {
3033
/// Constraints for the video size.
3134
final BoxConstraints? constraints;
3235

33-
/// Color of the overlay shown during video loading for a sent message
34-
final Color? sentLoadingOverlayColor;
36+
/// Background color for a sent message while image cover is generated
37+
final Color? sentBackGroundColor;
3538

36-
/// Color of the overlay shown during video loading for a received message
37-
final Color? receivedLoadingOverlayColor;
38-
39-
/// Color of the circular progress indicator shown during video loading.
40-
final Color? loadingIndicatorColor;
39+
/// Background color for a received message while image cover is generated
40+
final Color? receivedBackgroundColor;
4141

4242
/// Color of the overlay shown during video upload.
4343
final Color? uploadOverlayColor;
@@ -78,19 +78,15 @@ class FlyerChatVideoMessage extends StatefulWidget {
7878
/// Color of the play icon.
7979
final Color playIconColor;
8080

81-
/// Background color used while the image thumbnail is visible.
82-
final Color? placeholderColor;
83-
8481
/// Creates a widget to display an video message.
8582
const FlyerChatVideoMessage({
8683
super.key,
8784
required this.message,
8885
this.headers,
8986
this.borderRadius,
9087
this.constraints = const BoxConstraints(maxHeight: 300),
91-
this.sentLoadingOverlayColor,
92-
this.receivedLoadingOverlayColor,
93-
this.loadingIndicatorColor,
88+
this.sentBackGroundColor,
89+
this.receivedBackgroundColor,
9490
this.uploadOverlayColor,
9591
this.uploadIndicatorColor,
9692
this.timeStyle,
@@ -104,7 +100,6 @@ class FlyerChatVideoMessage extends StatefulWidget {
104100
this.playIcon = Icons.play_circle_fill,
105101
this.playIconSize = 48,
106102
this.playIconColor = Colors.white,
107-
this.placeholderColor,
108103
});
109104

110105
@override
@@ -115,7 +110,6 @@ class FlyerChatVideoMessage extends StatefulWidget {
115110
/// State for [FlyerChatVideoMessage].
116111
class _FlyerChatVideoMessageState extends State<FlyerChatVideoMessage> {
117112
late final ChatController _chatController;
118-
VideoPlayerController? _videoPlayerController;
119113
ImageProvider? _placeholderProvider;
120114
late double _aspectRatio;
121115

@@ -144,38 +138,51 @@ class _FlyerChatVideoMessageState extends State<FlyerChatVideoMessage> {
144138
}
145139

146140
_chatController = context.read<ChatController>();
147-
_initalizeVideoPlayerAsync();
141+
if (!false) {
142+
try {
143+
_generateImageCover();
144+
} catch (e) {
145+
debugPrint('Could not generate image cover: ${e.toString()}');
146+
}
147+
}
148148
}
149149

150-
Future<void> _initalizeVideoPlayerAsync() async {
151-
if (isNetworkSource(widget.message.source)) {
152-
_videoPlayerController = VideoPlayerController.networkUrl(
153-
Uri.parse(widget.message.source),
154-
httpHeaders: widget.headers ?? {},
155-
);
156-
} else {
157-
_videoPlayerController = VideoPlayerController.file(
158-
File(widget.message.source),
159-
);
160-
}
161-
await _videoPlayerController!.initialize().then((_) {
162-
setState(() {
163-
_aspectRatio = _videoPlayerController!.value.aspectRatio;
164-
});
150+
Future<void> _generateImageCover() async {
151+
final coverImageBytes = await VideoThumbnail.thumbnailData(
152+
video: widget.message.source,
153+
imageFormat: ImageFormat.WEBP,
154+
quality: 25,
155+
headers: widget.headers,
156+
);
157+
setState(() {
158+
// TODO should we add 'image' package to decode and get height and width
159+
// to update _aspectRatio?
160+
161+
// import 'package:image/image.dart' as img;
162+
163+
// final decoded = img.decodeImage(coverImageBytes!);
164+
// if (decoded != null) {
165+
// final width = decoded.width;
166+
// final height = decoded.height;
167+
// }
168+
169+
_placeholderProvider = MemoryImage(coverImageBytes!);
165170
});
166171
}
167172

168173
@override
169174
void didUpdateWidget(FlyerChatVideoMessage oldWidget) {
170175
super.didUpdateWidget(oldWidget);
171176
if (oldWidget.message.source != widget.message.source) {
172-
_initalizeVideoPlayerAsync();
177+
_generateImageCover();
173178
}
174179
}
175180

176181
@override
177182
void dispose() {
178-
_videoPlayerController?.dispose();
183+
_placeholderProvider?.evict();
184+
// Evicting the image on dispose will result in images flickering
185+
// PaintingBinding.instance.imageCache.evict(_imageProvider);
179186
super.dispose();
180187
}
181188

@@ -229,38 +236,25 @@ class _FlyerChatVideoMessageState extends State<FlyerChatVideoMessage> {
229236
child: Stack(
230237
fit: StackFit.expand,
231238
children: [
232-
_placeholderProvider != null
233-
? Image(image: _placeholderProvider!, fit: BoxFit.fill)
234-
: Container(
235-
color:
236-
widget.placeholderColor ??
237-
theme.colors.surfaceContainerLow,
238-
),
239239
Hero(
240240
tag: widget.message.id,
241241
child:
242-
_videoPlayerController?.value.isInitialized == true
243-
? VideoPlayer(_videoPlayerController!)
242+
_placeholderProvider != null
243+
? Image(
244+
image: _placeholderProvider!,
245+
fit: BoxFit.fill,
246+
)
244247
: Container(
245-
color: _resolveBackgroundColor(isSentByMe, theme),
246-
child: Center(
247-
child: CircularProgressIndicator(
248-
color:
249-
widget
250-
.fullScreenPlayerLoadingIndicatorColor ??
251-
theme.colors.onSurface.withValues(
252-
alpha: 0.8,
253-
),
254-
),
255-
),
248+
color:
249+
_resolveBackgroundColor(isSentByMe, theme) ??
250+
theme.colors.surfaceContainerLow,
256251
),
257252
),
258253
Icon(
259254
widget.playIcon,
260255
size: widget.playIconSize,
261256
color: widget.playIconColor,
262257
),
263-
264258
if (_chatController is UploadProgressMixin)
265259
StreamBuilder<double>(
266260
stream: (_chatController as UploadProgressMixin)
@@ -316,8 +310,8 @@ class _FlyerChatVideoMessageState extends State<FlyerChatVideoMessage> {
316310

317311
Color? _resolveBackgroundColor(bool isSentByMe, ChatTheme theme) {
318312
if (isSentByMe) {
319-
return widget.sentLoadingOverlayColor ?? theme.colors.primary;
313+
return widget.sentBackGroundColor ?? theme.colors.primary;
320314
}
321-
return widget.receivedLoadingOverlayColor ?? theme.colors.surfaceContainer;
315+
return widget.receivedBackgroundColor ?? theme.colors.surfaceContainer;
322316
}
323317
}

packages/flyer_chat_video_message/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ dependencies:
1616
flutter_chat_core: ^2.4.0
1717
provider: ^6.1.4
1818
thumbhash: ^0.1.0+1
19-
video_player: ^2.9.5
19+
video_thumbnail: ^0.5.6
2020

2121
dev_dependencies:
2222
flutter_lints: ^5.0.0

0 commit comments

Comments
 (0)