fix video attachment fullscreen
This commit is contained in:
@@ -94,7 +94,7 @@ class MyApp extends StatelessWidget {
|
|||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
theme: ThemeData.light(),
|
theme: ThemeData.light(),
|
||||||
darkTheme: ThemeData.dark(),
|
darkTheme: ThemeData.dark(),
|
||||||
themeMode: ThemeMode.system,
|
themeMode: ThemeMode.dark,
|
||||||
home: Container(
|
home: Container(
|
||||||
child: StreamChat(
|
child: StreamChat(
|
||||||
client: client,
|
client: client,
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ class _FullScreenVideoState extends State<FullScreenVideo> {
|
|||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Chewie(
|
return Chewie(
|
||||||
controller: _chewieController,
|
controller: _chewieController,
|
||||||
);
|
);
|
||||||
@@ -49,28 +48,28 @@ class _FullScreenVideoState extends State<FullScreenVideo> {
|
|||||||
_videoPlayerController.initialize().whenComplete(() {
|
_videoPlayerController.initialize().whenComplete(() {
|
||||||
setState(() {
|
setState(() {
|
||||||
initialized = true;
|
initialized = true;
|
||||||
|
_chewieController = ChewieController(
|
||||||
|
videoPlayerController: _videoPlayerController,
|
||||||
|
autoInitialize: false,
|
||||||
|
aspectRatio: _videoPlayerController.value.aspectRatio,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
_chewieController = ChewieController(
|
VoidCallback errorListener;
|
||||||
videoPlayerController: _videoPlayerController,
|
errorListener = () {
|
||||||
autoInitialize: false,
|
|
||||||
aspectRatio: _videoPlayerController.value.aspectRatio,
|
|
||||||
);
|
|
||||||
_videoPlayerController.addListener(() {
|
|
||||||
if (_videoPlayerController.value.hasError) {
|
if (_videoPlayerController.value.hasError) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
Navigator.pop(context);
|
||||||
Navigator.pop(context);
|
launchURL(context, widget.attachment.titleLink);
|
||||||
launchURL(context, widget.attachment.titleLink);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
_videoPlayerController.removeListener(errorListener);
|
||||||
|
};
|
||||||
|
_videoPlayerController.addListener(errorListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_videoPlayerController.dispose();
|
_videoPlayerController.dispose();
|
||||||
_chewieController.dispose();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,55 +32,61 @@ class ImageAttachment extends StatelessWidget {
|
|||||||
size: size,
|
size: size,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Hero(
|
Column(
|
||||||
tag: attachment.imageUrl ??
|
children: <Widget>[
|
||||||
attachment.assetUrl ??
|
Expanded(
|
||||||
attachment.thumbUrl,
|
child: Hero(
|
||||||
child: GestureDetector(
|
tag: attachment.imageUrl ??
|
||||||
onTap: () {
|
attachment.assetUrl ??
|
||||||
Navigator.push(context, MaterialPageRoute(builder: (_) {
|
attachment.thumbUrl,
|
||||||
return FullScreenImage(
|
child: GestureDetector(
|
||||||
url: attachment.imageUrl ??
|
onTap: () {
|
||||||
attachment.assetUrl ??
|
Navigator.push(context, MaterialPageRoute(builder: (_) {
|
||||||
attachment.thumbUrl,
|
return FullScreenImage(
|
||||||
);
|
url: attachment.imageUrl ??
|
||||||
}));
|
attachment.assetUrl ??
|
||||||
},
|
attachment.thumbUrl,
|
||||||
child: CachedNetworkImage(
|
);
|
||||||
height: size?.height,
|
}));
|
||||||
width: size?.width,
|
},
|
||||||
placeholder: (_, __) {
|
child: CachedNetworkImage(
|
||||||
return Container(
|
height: size?.height,
|
||||||
width: size?.width,
|
width: size?.width,
|
||||||
height: size?.height,
|
placeholder: (_, __) {
|
||||||
child: Center(
|
return Container(
|
||||||
child: CircularProgressIndicator(),
|
width: size?.width,
|
||||||
|
height: size?.height,
|
||||||
|
child: Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
imageUrl: attachment.thumbUrl ??
|
||||||
|
attachment.imageUrl ??
|
||||||
|
attachment.assetUrl,
|
||||||
|
errorWidget: (context, url, error) => AttachmentError(
|
||||||
|
attachment: attachment,
|
||||||
|
size: size,
|
||||||
|
),
|
||||||
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
imageUrl: attachment.thumbUrl ??
|
|
||||||
attachment.imageUrl ??
|
|
||||||
attachment.assetUrl,
|
|
||||||
errorWidget: (context, url, error) => AttachmentError(
|
|
||||||
attachment: attachment,
|
|
||||||
size: size,
|
|
||||||
),
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (attachment.title != null)
|
|
||||||
Positioned.fill(
|
|
||||||
child: Align(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
child: Material(
|
|
||||||
child: AttachmentTitle(
|
|
||||||
messageTheme: messageTheme,
|
|
||||||
attachment: attachment,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
if (attachment.title != null)
|
||||||
|
Positioned.fill(
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: Material(
|
||||||
|
child: AttachmentTitle(
|
||||||
|
messageTheme: messageTheme,
|
||||||
|
attachment: attachment,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
if (attachment.titleLink != null || attachment.ogScrapeUrl != null)
|
if (attachment.titleLink != null || attachment.ogScrapeUrl != null)
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: Material(
|
child: Material(
|
||||||
|
|||||||
@@ -13,41 +13,98 @@ import 'message_text.dart';
|
|||||||
|
|
||||||
typedef AttachmentBuilder = Widget Function(BuildContext, Message, Attachment);
|
typedef AttachmentBuilder = Widget Function(BuildContext, Message, Attachment);
|
||||||
|
|
||||||
|
/// The display behaviour of a widget
|
||||||
enum DisplayWidget {
|
enum DisplayWidget {
|
||||||
|
/// Hides the widget replacing its space with a spacer
|
||||||
hide,
|
hide,
|
||||||
|
|
||||||
|
/// Hides the widget not replacing its space
|
||||||
gone,
|
gone,
|
||||||
|
|
||||||
|
/// Shows the widget normally
|
||||||
show,
|
show,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 
|
||||||
|
/// 
|
||||||
|
///
|
||||||
|
/// It shows a message with reactions, replies and user avatar.
|
||||||
|
///
|
||||||
|
/// Usually you don't use this widget as it's the default message widget used by [MessageListView].
|
||||||
|
///
|
||||||
|
/// The widget components render the ui based on the first ancestor of type [StreamChatTheme].
|
||||||
|
/// Modify it to change the widget appearance.
|
||||||
class MessageWidget extends StatelessWidget {
|
class MessageWidget extends StatelessWidget {
|
||||||
|
/// Function called on mention tap
|
||||||
final void Function(User) onMentionTap;
|
final void Function(User) onMentionTap;
|
||||||
|
|
||||||
|
/// The function called when tapping on replies
|
||||||
final void Function(Message) onThreadTap;
|
final void Function(Message) onThreadTap;
|
||||||
final Widget Function(BuildContext, Message) editMessageInputBuilder;
|
final Widget Function(BuildContext, Message) editMessageInputBuilder;
|
||||||
final Widget Function(BuildContext, Message) textBuilder;
|
final Widget Function(BuildContext, Message) textBuilder;
|
||||||
|
|
||||||
|
/// Function called on long press
|
||||||
final void Function(BuildContext, Message) onMessageActions;
|
final void Function(BuildContext, Message) onMessageActions;
|
||||||
|
|
||||||
|
/// The message
|
||||||
final Message message;
|
final Message message;
|
||||||
|
|
||||||
|
/// The message theme
|
||||||
final MessageTheme messageTheme;
|
final MessageTheme messageTheme;
|
||||||
|
|
||||||
|
/// If true the widget will be mirrored
|
||||||
final bool reverse;
|
final bool reverse;
|
||||||
|
|
||||||
|
/// The shape of the message text
|
||||||
final ShapeBorder shape;
|
final ShapeBorder shape;
|
||||||
|
|
||||||
|
/// The shape of an attachment
|
||||||
final ShapeBorder attachmentShape;
|
final ShapeBorder attachmentShape;
|
||||||
|
|
||||||
|
/// The borderside of the message text
|
||||||
final BorderSide borderSide;
|
final BorderSide borderSide;
|
||||||
|
|
||||||
|
/// The borderside of an attachment
|
||||||
final BorderSide attachmentBorderSide;
|
final BorderSide attachmentBorderSide;
|
||||||
|
|
||||||
|
/// The border radius of the message text
|
||||||
final BorderRadiusGeometry borderRadiusGeometry;
|
final BorderRadiusGeometry borderRadiusGeometry;
|
||||||
|
|
||||||
|
/// The border radius of an attachment
|
||||||
final BorderRadiusGeometry attachmentBorderRadiusGeometry;
|
final BorderRadiusGeometry attachmentBorderRadiusGeometry;
|
||||||
|
|
||||||
|
/// The padding of the widget
|
||||||
final EdgeInsetsGeometry padding;
|
final EdgeInsetsGeometry padding;
|
||||||
|
|
||||||
|
/// The internal padding of the message text
|
||||||
final EdgeInsetsGeometry textPadding;
|
final EdgeInsetsGeometry textPadding;
|
||||||
|
|
||||||
|
/// The internal padding of an attachment
|
||||||
final EdgeInsetsGeometry attachmentPadding;
|
final EdgeInsetsGeometry attachmentPadding;
|
||||||
|
|
||||||
|
/// It controls the display behaviour of the user avatar
|
||||||
final DisplayWidget showUserAvatar;
|
final DisplayWidget showUserAvatar;
|
||||||
|
|
||||||
|
/// It controls the display behaviour of the sending indicator
|
||||||
final DisplayWidget showSendingIndicator;
|
final DisplayWidget showSendingIndicator;
|
||||||
|
|
||||||
|
/// If true the widget will show the reactions
|
||||||
final bool showReactions;
|
final bool showReactions;
|
||||||
|
|
||||||
|
/// If true the widget will show the reply indicator
|
||||||
final bool showReplyIndicator;
|
final bool showReplyIndicator;
|
||||||
final bool isParent;
|
|
||||||
|
/// The function called when tapping on UserAvatar
|
||||||
|
final void Function(User) onUserAvatarTap;
|
||||||
|
|
||||||
|
/// If true show the users username next to the timestamp of the message
|
||||||
final bool showUsername;
|
final bool showUsername;
|
||||||
final bool showTimestamp;
|
final bool showTimestamp;
|
||||||
final bool showDeleteMessage;
|
final bool showDeleteMessage;
|
||||||
final bool showEditMessage;
|
final bool showEditMessage;
|
||||||
final Map<String, AttachmentBuilder> attachmentBuilders;
|
final Map<String, AttachmentBuilder> attachmentBuilders;
|
||||||
final Map<String, String> reactionToEmoji = {
|
|
||||||
|
final Map<String, String> _reactionToEmoji = {
|
||||||
'love': '❤️️',
|
'love': '❤️️',
|
||||||
'haha': '😂',
|
'haha': '😂',
|
||||||
'like': '👍',
|
'like': '👍',
|
||||||
@@ -71,13 +128,13 @@ class MessageWidget extends StatelessWidget {
|
|||||||
this.showUserAvatar = DisplayWidget.show,
|
this.showUserAvatar = DisplayWidget.show,
|
||||||
this.showSendingIndicator = DisplayWidget.show,
|
this.showSendingIndicator = DisplayWidget.show,
|
||||||
this.showReplyIndicator = true,
|
this.showReplyIndicator = true,
|
||||||
this.isParent = false,
|
|
||||||
this.onThreadTap,
|
this.onThreadTap,
|
||||||
this.showUsername = true,
|
this.showUsername = true,
|
||||||
this.showTimestamp = true,
|
this.showTimestamp = true,
|
||||||
this.showReactions = true,
|
this.showReactions = true,
|
||||||
this.showDeleteMessage = true,
|
this.showDeleteMessage = true,
|
||||||
this.showEditMessage = true,
|
this.showEditMessage = true,
|
||||||
|
this.onUserAvatarTap,
|
||||||
this.onMessageActions,
|
this.onMessageActions,
|
||||||
this.editMessageInputBuilder,
|
this.editMessageInputBuilder,
|
||||||
this.textBuilder,
|
this.textBuilder,
|
||||||
@@ -338,7 +395,7 @@ class MessageWidget extends StatelessWidget {
|
|||||||
Text _buildReactionsText(BuildContext context) {
|
Text _buildReactionsText(BuildContext context) {
|
||||||
return Text(
|
return Text(
|
||||||
message.reactionCounts.keys.map((reactionType) {
|
message.reactionCounts.keys.map((reactionType) {
|
||||||
return reactionToEmoji[reactionType] ?? '?';
|
return _reactionToEmoji[reactionType] ?? '?';
|
||||||
}).join(' ') +
|
}).join(' ') +
|
||||||
' ${message.reactionCounts.values.fold(0, (t, v) => v + t).toString()}',
|
' ${message.reactionCounts.values.fold(0, (t, v) => v + t).toString()}',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -499,6 +556,7 @@ class MessageWidget extends StatelessWidget {
|
|||||||
Offset(0, messageTheme.avatarTheme.constraints.maxHeight / 2),
|
Offset(0, messageTheme.avatarTheme.constraints.maxHeight / 2),
|
||||||
child: UserAvatar(
|
child: UserAvatar(
|
||||||
user: message.user,
|
user: message.user,
|
||||||
|
onTap: onUserAvatarTap,
|
||||||
constraints: messageTheme.avatarTheme.constraints,
|
constraints: messageTheme.avatarTheme.constraints,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -24,7 +24,16 @@ class StreamChatTheme extends InheritedWidget {
|
|||||||
|
|
||||||
/// Use this method to get the current [StreamChatThemeData] instance
|
/// Use this method to get the current [StreamChatThemeData] instance
|
||||||
static StreamChatThemeData of(BuildContext context) {
|
static StreamChatThemeData of(BuildContext context) {
|
||||||
return context.dependOnInheritedWidgetOfExactType<StreamChatTheme>().data;
|
final streamChatTheme =
|
||||||
|
context.dependOnInheritedWidgetOfExactType<StreamChatTheme>();
|
||||||
|
|
||||||
|
if (streamChatTheme == null) {
|
||||||
|
throw Exception(
|
||||||
|
'You must have a StreamChatTheme widget at the top of your widget tree',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return streamChatTheme.data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class _VideoAttachmentState extends State<VideoAttachment> {
|
|||||||
}
|
}
|
||||||
_chewieController = ChewieController(
|
_chewieController = ChewieController(
|
||||||
videoPlayerController: _videoPlayerController,
|
videoPlayerController: _videoPlayerController,
|
||||||
autoInitialize: false,
|
autoInitialize: true,
|
||||||
showControls: false,
|
showControls: false,
|
||||||
aspectRatio: _videoPlayerController.value.aspectRatio,
|
aspectRatio: _videoPlayerController.value.aspectRatio,
|
||||||
errorBuilder: (_, e) {
|
errorBuilder: (_, e) {
|
||||||
@@ -137,8 +137,8 @@ class _VideoAttachmentState extends State<VideoAttachment> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_videoPlayerController = VideoPlayerController.network(
|
_videoPlayerController =
|
||||||
widget.attachment.localUri ?? widget.attachment.assetUrl);
|
VideoPlayerController.network(widget.attachment.assetUrl);
|
||||||
_videoPlayerController.initialize().whenComplete(() {
|
_videoPlayerController.initialize().whenComplete(() {
|
||||||
setState(() {
|
setState(() {
|
||||||
initialized = true;
|
initialized = true;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export 'src/channels_bloc.dart';
|
|||||||
export 'src/date_divider.dart';
|
export 'src/date_divider.dart';
|
||||||
export 'src/deleted_message.dart';
|
export 'src/deleted_message.dart';
|
||||||
export 'src/file_attachment.dart';
|
export 'src/file_attachment.dart';
|
||||||
|
export 'src/full_screen_video.dart';
|
||||||
export 'src/giphy_attachment.dart';
|
export 'src/giphy_attachment.dart';
|
||||||
export 'src/image_attachment.dart';
|
export 'src/image_attachment.dart';
|
||||||
export 'src/message_input.dart';
|
export 'src/message_input.dart';
|
||||||
|
|||||||
+2
-2
@@ -15,10 +15,10 @@ dependencies:
|
|||||||
flutter_portal: ^0.1.0
|
flutter_portal: ^0.1.0
|
||||||
cached_network_image: ^2.2.0+1
|
cached_network_image: ^2.2.0+1
|
||||||
flutter_markdown: ^0.3.5
|
flutter_markdown: ^0.3.5
|
||||||
url_launcher: ^5.4.5
|
url_launcher: ^5.4.7
|
||||||
video_player: ^0.10.10
|
video_player: ^0.10.10
|
||||||
chewie: ^0.9.10
|
chewie: ^0.9.10
|
||||||
file_picker: ^1.8.0+2
|
file_picker: ^1.9.0+1
|
||||||
image_picker: ^0.6.6+1
|
image_picker: ^0.6.6+1
|
||||||
flutter_keyboard_visibility: ^2.0.0
|
flutter_keyboard_visibility: ^2.0.0
|
||||||
stream_chat:
|
stream_chat:
|
||||||
|
|||||||
Reference in New Issue
Block a user