lint changes image
This commit is contained in:
@@ -192,29 +192,30 @@ class AttachmentActionsModal extends StatelessWidget {
|
|||||||
VoidCallback? onTap, {
|
VoidCallback? onTap, {
|
||||||
Color? color,
|
Color? color,
|
||||||
Key? key,
|
Key? key,
|
||||||
}) => Material(
|
}) =>
|
||||||
key: key,
|
Material(
|
||||||
color: StreamChatTheme.of(context).colorTheme.white,
|
key: key,
|
||||||
child: InkWell(
|
color: StreamChatTheme.of(context).colorTheme.white,
|
||||||
onTap: onTap,
|
child: InkWell(
|
||||||
child: Padding(
|
onTap: onTap,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
child: Padding(
|
||||||
child: Row(
|
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
||||||
children: [
|
child: Row(
|
||||||
icon,
|
children: [
|
||||||
SizedBox(width: 16),
|
icon,
|
||||||
Text(
|
SizedBox(width: 16),
|
||||||
title,
|
Text(
|
||||||
style: StreamChatTheme.of(context)
|
title,
|
||||||
.textTheme
|
style: StreamChatTheme.of(context)
|
||||||
.body
|
.textTheme
|
||||||
.copyWith(color: color),
|
.body
|
||||||
),
|
.copyWith(color: color),
|
||||||
],
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildDownloadProgressDialog(
|
Widget _buildDownloadProgressDialog(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
|||||||
|
|
||||||
/// Back button implementation
|
/// Back button implementation
|
||||||
class StreamBackButton extends StatelessWidget {
|
class StreamBackButton extends StatelessWidget {
|
||||||
|
|
||||||
/// Constructor for creating back button
|
/// Constructor for creating back button
|
||||||
const StreamBackButton({
|
const StreamBackButton({
|
||||||
Key? key,
|
Key? key,
|
||||||
@@ -25,37 +24,37 @@ class StreamBackButton extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Stack(
|
Widget build(BuildContext context) => Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
RawMaterialButton(
|
RawMaterialButton(
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
highlightElevation: 0,
|
highlightElevation: 0,
|
||||||
focusElevation: 0,
|
focusElevation: 0,
|
||||||
hoverElevation: 0,
|
hoverElevation: 0,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (onPressed != null) {
|
if (onPressed != null) {
|
||||||
onPressed!();
|
onPressed!();
|
||||||
} else {
|
} else {
|
||||||
Navigator.maybePop(context);
|
Navigator.maybePop(context);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
padding: const EdgeInsets.all(14),
|
padding: const EdgeInsets.all(14),
|
||||||
child: StreamSvgIcon.left(
|
child: StreamSvgIcon.left(
|
||||||
size: 24,
|
size: 24,
|
||||||
color: StreamChatTheme.of(context).colorTheme.black,
|
color: StreamChatTheme.of(context).colorTheme.black,
|
||||||
),
|
|
||||||
),
|
|
||||||
if (showUnreads)
|
|
||||||
Positioned(
|
|
||||||
top: 7,
|
|
||||||
right: 7,
|
|
||||||
child: UnreadIndicator(
|
|
||||||
cid: cid,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
if (showUnreads)
|
||||||
);
|
Positioned(
|
||||||
|
top: 7,
|
||||||
|
right: 7,
|
||||||
|
child: UnreadIndicator(
|
||||||
|
cid: cid,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,23 +98,23 @@ class ChannelImage extends StatelessWidget {
|
|||||||
(users) => users[otherMember?.userId] ?? otherMember!.user!),
|
(users) => users[otherMember?.userId] ?? otherMember!.user!),
|
||||||
initialData: otherMember!.user,
|
initialData: otherMember!.user,
|
||||||
builder: (context, snapshot) => UserAvatar(
|
builder: (context, snapshot) => UserAvatar(
|
||||||
borderRadius: borderRadius ??
|
borderRadius: borderRadius ??
|
||||||
StreamChatTheme.of(context)
|
StreamChatTheme.of(context)
|
||||||
.channelPreviewTheme
|
.channelPreviewTheme
|
||||||
.avatarTheme
|
.avatarTheme
|
||||||
?.borderRadius,
|
?.borderRadius,
|
||||||
user: snapshot.data ?? otherMember.user!,
|
user: snapshot.data ?? otherMember.user!,
|
||||||
constraints: constraints ??
|
constraints: constraints ??
|
||||||
StreamChatTheme.of(context)
|
StreamChatTheme.of(context)
|
||||||
.channelPreviewTheme
|
.channelPreviewTheme
|
||||||
.avatarTheme
|
.avatarTheme
|
||||||
?.constraints,
|
?.constraints,
|
||||||
onTap: onTap != null ? (_) => onTap!() : null,
|
onTap: onTap != null ? (_) => onTap!() : null,
|
||||||
selected: selected,
|
selected: selected,
|
||||||
selectionColor: selectionColor ??
|
selectionColor: selectionColor ??
|
||||||
StreamChatTheme.of(context).colorTheme.accentBlue,
|
StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
selectionThickness: selectionThickness,
|
selectionThickness: selectionThickness,
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
final images = channel.state?.members
|
final images = channel.state?.members
|
||||||
.where((member) =>
|
.where((member) =>
|
||||||
@@ -167,16 +167,16 @@ class ChannelImage extends StatelessWidget {
|
|||||||
CachedNetworkImage(
|
CachedNetworkImage(
|
||||||
imageUrl: image,
|
imageUrl: image,
|
||||||
errorWidget: (_, __, ___) => Center(
|
errorWidget: (_, __, ___) => Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
snapshot.data?.containsKey('name') ?? false
|
snapshot.data?.containsKey('name') ?? false
|
||||||
? snapshot.data!['name'][0]
|
? snapshot.data!['name'][0]
|
||||||
: '',
|
: '',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: StreamChatTheme.of(context).colorTheme.white,
|
color: StreamChatTheme.of(context).colorTheme.white,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -217,64 +217,65 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildConnectingTitleState(BuildContext context) => Row(
|
Widget _buildConnectingTitleState(BuildContext context) => Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 16,
|
height: 16,
|
||||||
width: 16,
|
width: 16,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(width: 10),
|
||||||
const SizedBox(width: 10),
|
Text(
|
||||||
Text(
|
'Searching for Network',
|
||||||
'Searching for Network',
|
|
||||||
style: StreamChatTheme.of(context)
|
|
||||||
.channelListHeaderTheme
|
|
||||||
.title
|
|
||||||
?.copyWith(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildDisconnectedTitleState(
|
|
||||||
BuildContext context,
|
|
||||||
StreamChatClient client,
|
|
||||||
) => Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Offline...',
|
|
||||||
style: StreamChatTheme.of(context)
|
|
||||||
.channelListHeaderTheme
|
|
||||||
.title
|
|
||||||
?.copyWith(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
await client.disconnect();
|
|
||||||
await client.connect();
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
'Try Again',
|
|
||||||
style: StreamChatTheme.of(context)
|
style: StreamChatTheme.of(context)
|
||||||
.channelListHeaderTheme
|
.channelListHeaderTheme
|
||||||
.title
|
.title
|
||||||
?.copyWith(
|
?.copyWith(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
);
|
||||||
);
|
|
||||||
|
Widget _buildDisconnectedTitleState(
|
||||||
|
BuildContext context,
|
||||||
|
StreamChatClient client,
|
||||||
|
) =>
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Offline...',
|
||||||
|
style: StreamChatTheme.of(context)
|
||||||
|
.channelListHeaderTheme
|
||||||
|
.title
|
||||||
|
?.copyWith(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await client.disconnect();
|
||||||
|
await client.connect();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
'Try Again',
|
||||||
|
style: StreamChatTheme.of(context)
|
||||||
|
.channelListHeaderTheme
|
||||||
|
.title
|
||||||
|
?.copyWith(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: StreamChatTheme.of(context).colorTheme.accentBlue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
|
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import 'package:stream_chat_flutter/src/utils.dart';
|
|||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
|
|
||||||
/// Callback called when tapping on a channel
|
/// Callback called when tapping on a channel
|
||||||
typedef ChannelTapCallback = void Function(Channel, Widget?);
|
typedef ChannelTapCallback = void Function(Channel, Widget?);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import 'package:flutter/rendering.dart';
|
|||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
|
|
||||||
/// It shows the current [Channel] name using a [Text] widget.
|
/// It shows the current [Channel] name using a [Text] widget.
|
||||||
///
|
///
|
||||||
/// The widget uses a [StreamBuilder] to render the channel information
|
/// The widget uses a [StreamBuilder] to render the channel information
|
||||||
@@ -60,9 +59,8 @@ class ChannelName extends StatelessWidget {
|
|||||||
|
|
||||||
final exceedingMembers =
|
final exceedingMembers =
|
||||||
otherMembers.length - currentMembers.length;
|
otherMembers.length - currentMembers.length;
|
||||||
title =
|
title = '${currentMembers.map((e) => e.user?.name).join(', ')} '
|
||||||
'${currentMembers.map((e) => e.user?.name).join(', ')} '
|
'${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}';
|
||||||
'${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}';
|
|
||||||
} else {
|
} else {
|
||||||
title = 'No title';
|
title = 'No title';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
|||||||
/// [StreamChatTheme].
|
/// [StreamChatTheme].
|
||||||
/// Modify it to change the widget appearance.
|
/// Modify it to change the widget appearance.
|
||||||
class ChannelPreview extends StatelessWidget {
|
class ChannelPreview extends StatelessWidget {
|
||||||
|
|
||||||
/// Constructor for creating [ChannelPreview]
|
/// Constructor for creating [ChannelPreview]
|
||||||
const ChannelPreview({
|
const ChannelPreview({
|
||||||
required this.channel,
|
required this.channel,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
|||||||
|
|
||||||
/// It shows a date divider depending on the date difference
|
/// It shows a date divider depending on the date difference
|
||||||
class DateDivider extends StatelessWidget {
|
class DateDivider extends StatelessWidget {
|
||||||
|
|
||||||
/// Constructor for creating a [DateDivider]
|
/// Constructor for creating a [DateDivider]
|
||||||
const DateDivider({
|
const DateDivider({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
|||||||
|
|
||||||
/// Widget to display deleted message
|
/// Widget to display deleted message
|
||||||
class DeletedMessage extends StatelessWidget {
|
class DeletedMessage extends StatelessWidget {
|
||||||
|
|
||||||
/// Constructor to create [DeletedMessage]
|
/// Constructor to create [DeletedMessage]
|
||||||
const DeletedMessage({
|
const DeletedMessage({
|
||||||
Key? key,
|
Key? key,
|
||||||
@@ -33,43 +32,43 @@ class DeletedMessage extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Transform(
|
Widget build(BuildContext context) => Transform(
|
||||||
transform: Matrix4.rotationY(reverse ? pi : 0),
|
transform: Matrix4.rotationY(reverse ? pi : 0),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Material(
|
child: Material(
|
||||||
color: messageTheme.messageBackgroundColor,
|
color: messageTheme.messageBackgroundColor,
|
||||||
shape: shape ??
|
shape: shape ??
|
||||||
RoundedRectangleBorder(
|
RoundedRectangleBorder(
|
||||||
borderRadius: borderRadiusGeometry ?? BorderRadius.zero,
|
borderRadius: borderRadiusGeometry ?? BorderRadius.zero,
|
||||||
side: borderSide ??
|
side: borderSide ??
|
||||||
BorderSide(
|
BorderSide(
|
||||||
color: Theme.of(context).brightness == Brightness.dark
|
color: Theme.of(context).brightness == Brightness.dark
|
||||||
? StreamChatTheme.of(context)
|
? StreamChatTheme.of(context)
|
||||||
.colorTheme
|
.colorTheme
|
||||||
.white
|
.white
|
||||||
.withAlpha(24)
|
.withAlpha(24)
|
||||||
: StreamChatTheme.of(context)
|
: StreamChatTheme.of(context)
|
||||||
.colorTheme
|
.colorTheme
|
||||||
.black
|
.black
|
||||||
.withAlpha(24),
|
.withAlpha(24),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 8,
|
||||||
|
horizontal: 16,
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Transform(
|
||||||
padding: const EdgeInsets.symmetric(
|
transform: Matrix4.rotationY(reverse ? pi : 0),
|
||||||
vertical: 8,
|
alignment: Alignment.center,
|
||||||
horizontal: 16,
|
child: Text(
|
||||||
),
|
'Message deleted',
|
||||||
child: Transform(
|
style: messageTheme.messageText?.copyWith(
|
||||||
transform: Matrix4.rotationY(reverse ? pi : 0),
|
fontStyle: FontStyle.italic,
|
||||||
alignment: Alignment.center,
|
color: messageTheme.createdAt?.color,
|
||||||
child: Text(
|
),
|
||||||
'Message deleted',
|
|
||||||
style: messageTheme.messageText?.copyWith(
|
|
||||||
fontStyle: FontStyle.italic,
|
|
||||||
color: messageTheme.createdAt?.color,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import 'package:video_player/video_player.dart';
|
|||||||
|
|
||||||
/// Return action for coming back from pages
|
/// Return action for coming back from pages
|
||||||
enum ReturnActionType {
|
enum ReturnActionType {
|
||||||
|
|
||||||
/// No return action
|
/// No return action
|
||||||
none,
|
none,
|
||||||
|
|
||||||
@@ -252,10 +251,10 @@ class _FullScreenMediaState extends State<FullScreenMedia>
|
|||||||
class VideoPackage {
|
class VideoPackage {
|
||||||
/// Constructor for creating [VideoPackage]
|
/// Constructor for creating [VideoPackage]
|
||||||
VideoPackage(
|
VideoPackage(
|
||||||
Attachment attachment, {
|
Attachment attachment, {
|
||||||
bool showControls = false,
|
bool showControls = false,
|
||||||
bool autoInitialize = true,
|
bool autoInitialize = true,
|
||||||
}) : _showControls = showControls,
|
}) : _showControls = showControls,
|
||||||
_autoInitialize = autoInitialize,
|
_autoInitialize = autoInitialize,
|
||||||
_videoPlayerController = attachment.localUri != null
|
_videoPlayerController = attachment.localUri != null
|
||||||
? VideoPlayerController.file(File.fromUri(attachment.localUri!))
|
? VideoPlayerController.file(File.fromUri(attachment.localUri!))
|
||||||
|
|||||||
@@ -12,7 +12,22 @@ import 'package:stream_chat_flutter/src/video_thumbnail_image.dart';
|
|||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
|
/// Footer widget for media display
|
||||||
class ImageFooter extends StatefulWidget implements PreferredSizeWidget {
|
class ImageFooter extends StatefulWidget implements PreferredSizeWidget {
|
||||||
|
/// Creates a channel header
|
||||||
|
const ImageFooter({
|
||||||
|
Key? key,
|
||||||
|
required this.message,
|
||||||
|
this.onBackPressed,
|
||||||
|
this.onTitleTap,
|
||||||
|
this.onImageTap,
|
||||||
|
this.currentPage = 0,
|
||||||
|
this.totalPages = 0,
|
||||||
|
this.mediaAttachments = const [],
|
||||||
|
this.mediaSelectedCallBack,
|
||||||
|
}) : preferredSize = const Size.fromHeight(kToolbarHeight),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
/// Callback to call when pressing the back button.
|
/// Callback to call when pressing the back button.
|
||||||
/// By default it calls [Navigator.pop]
|
/// By default it calls [Navigator.pop]
|
||||||
final VoidCallback? onBackPressed;
|
final VoidCallback? onBackPressed;
|
||||||
@@ -23,28 +38,21 @@ class ImageFooter extends StatefulWidget implements PreferredSizeWidget {
|
|||||||
/// Callback to call when the image is tapped.
|
/// Callback to call when the image is tapped.
|
||||||
final VoidCallback? onImageTap;
|
final VoidCallback? onImageTap;
|
||||||
|
|
||||||
|
/// Stores the current index of media shown
|
||||||
final int currentPage;
|
final int currentPage;
|
||||||
|
|
||||||
|
/// Total number of pages of media
|
||||||
final int totalPages;
|
final int totalPages;
|
||||||
|
|
||||||
|
/// All attachments to show
|
||||||
final List<Attachment> mediaAttachments;
|
final List<Attachment> mediaAttachments;
|
||||||
|
|
||||||
|
/// Message which attachments are attached to
|
||||||
final Message message;
|
final Message message;
|
||||||
|
|
||||||
|
/// Callback when media is selected
|
||||||
final ValueChanged<int>? mediaSelectedCallBack;
|
final ValueChanged<int>? mediaSelectedCallBack;
|
||||||
|
|
||||||
/// Creates a channel header
|
|
||||||
ImageFooter({
|
|
||||||
Key? key,
|
|
||||||
required this.message,
|
|
||||||
this.onBackPressed,
|
|
||||||
this.onTitleTap,
|
|
||||||
this.onImageTap,
|
|
||||||
this.currentPage = 0,
|
|
||||||
this.totalPages = 0,
|
|
||||||
this.mediaAttachments = const [],
|
|
||||||
this.mediaSelectedCallBack,
|
|
||||||
}) : preferredSize = Size.fromHeight(kToolbarHeight),
|
|
||||||
super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_ImageFooterState createState() => _ImageFooterState();
|
_ImageFooterState createState() => _ImageFooterState();
|
||||||
|
|
||||||
@@ -119,7 +127,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
),
|
),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: widget.onTitleTap,
|
onTap: widget.onTitleTap,
|
||||||
child: Container(
|
child: SizedBox(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -169,115 +177,115 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
expand: false,
|
expand: false,
|
||||||
initialChildSize: initialChildSize / size.height,
|
initialChildSize: initialChildSize / size.height,
|
||||||
minChildSize: initialChildSize / size.height,
|
minChildSize: initialChildSize / size.height,
|
||||||
builder: (context, scrollController) {
|
builder: (context, scrollController) => SingleChildScrollView(
|
||||||
return SingleChildScrollView(
|
controller: scrollController,
|
||||||
controller: scrollController,
|
child: Column(
|
||||||
child: Column(
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisSize: MainAxisSize.min,
|
children: [
|
||||||
children: [
|
Stack(
|
||||||
Stack(
|
children: [
|
||||||
children: [
|
Center(
|
||||||
Center(
|
child: Padding(
|
||||||
child: Padding(
|
padding: const EdgeInsets.all(16),
|
||||||
padding: const EdgeInsets.all(16),
|
child: Text(
|
||||||
child: Text(
|
'Photos',
|
||||||
'Photos',
|
style: StreamChatTheme.of(context)
|
||||||
style: StreamChatTheme.of(context)
|
.textTheme
|
||||||
.textTheme
|
.headlineBold,
|
||||||
.headlineBold,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Align(
|
),
|
||||||
alignment: Alignment.centerRight,
|
Align(
|
||||||
child: IconButton(
|
alignment: Alignment.centerRight,
|
||||||
icon: StreamSvgIcon.close(
|
child: IconButton(
|
||||||
color: StreamChatTheme.of(context).colorTheme.black,
|
icon: StreamSvgIcon.close(
|
||||||
),
|
color: StreamChatTheme.of(context).colorTheme.black,
|
||||||
onPressed: () => Navigator.maybePop(context),
|
|
||||||
),
|
),
|
||||||
|
onPressed: () => Navigator.maybePop(context),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
Flexible(
|
),
|
||||||
child: GridView.builder(
|
Flexible(
|
||||||
shrinkWrap: true,
|
child: GridView.builder(
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
shrinkWrap: true,
|
||||||
itemCount: widget.mediaAttachments.length,
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
padding: const EdgeInsets.all(1),
|
itemCount: widget.mediaAttachments.length,
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
padding: const EdgeInsets.all(1),
|
||||||
crossAxisCount: crossAxisCount,
|
// ignore: lines_longer_than_80_chars
|
||||||
mainAxisSpacing: 2,
|
gridDelegate:
|
||||||
crossAxisSpacing: 2,
|
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
),
|
crossAxisCount: crossAxisCount,
|
||||||
itemBuilder: (context, index) {
|
mainAxisSpacing: 2,
|
||||||
Widget media;
|
crossAxisSpacing: 2,
|
||||||
final attachment = widget.mediaAttachments[index];
|
),
|
||||||
if (attachment.type == 'video') {
|
itemBuilder: (context, index) {
|
||||||
media = InkWell(
|
Widget media;
|
||||||
onTap: () => widget.mediaSelectedCallBack!(index),
|
final attachment = widget.mediaAttachments[index];
|
||||||
child: FittedBox(
|
if (attachment.type == 'video') {
|
||||||
|
media = InkWell(
|
||||||
|
onTap: () => widget.mediaSelectedCallBack!(index),
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
child: VideoThumbnailImage(
|
||||||
|
video: (attachment.file?.path ??
|
||||||
|
attachment.assetUrl)!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
media = InkWell(
|
||||||
|
onTap: () => widget.mediaSelectedCallBack!(index),
|
||||||
|
child: AspectRatio(
|
||||||
|
aspectRatio: 1,
|
||||||
|
child: CachedNetworkImage(
|
||||||
|
imageUrl: attachment.imageUrl ??
|
||||||
|
attachment.assetUrl ??
|
||||||
|
attachment.thumbUrl!,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
child: VideoThumbnailImage(
|
|
||||||
video: (attachment.file?.path ??
|
|
||||||
attachment.assetUrl)!,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
} else {
|
);
|
||||||
media = InkWell(
|
}
|
||||||
onTap: () => widget.mediaSelectedCallBack!(index),
|
|
||||||
child: AspectRatio(
|
|
||||||
aspectRatio: 1,
|
|
||||||
child: CachedNetworkImage(
|
|
||||||
imageUrl: attachment.imageUrl ??
|
|
||||||
attachment.assetUrl ??
|
|
||||||
attachment.thumbUrl!,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
media,
|
media,
|
||||||
if (widget.message.user != null)
|
if (widget.message.user != null)
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: Container(
|
child: Container(
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
color: Colors.white.withOpacity(0.6),
|
color: Colors.white.withOpacity(0.6),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
blurRadius: 8,
|
blurRadius: 8,
|
||||||
color: StreamChatTheme.of(context)
|
color: StreamChatTheme.of(context)
|
||||||
.colorTheme
|
.colorTheme
|
||||||
.black
|
.black
|
||||||
.withOpacity(0.3),
|
.withOpacity(0.3),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.all(2),
|
padding: const EdgeInsets.all(2),
|
||||||
child: UserAvatar(
|
child: UserAvatar(
|
||||||
user: widget.message.user!,
|
user: widget.message.user!,
|
||||||
constraints:
|
constraints:
|
||||||
BoxConstraints.tight(Size(24, 24)),
|
BoxConstraints.tight(const Size(24, 24)),
|
||||||
showOnlineStatus: false,
|
showOnlineStatus: false,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
);
|
],
|
||||||
},
|
);
|
||||||
),
|
},
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
);
|
),
|
||||||
},
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -304,29 +312,3 @@ class _ImageFooterState extends State<ImageFooter> {
|
|||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used for clipping textfield prefix icon
|
|
||||||
class IconClipper extends CustomClipper<Path> {
|
|
||||||
@override
|
|
||||||
Path getClip(Size size) {
|
|
||||||
final leftX = size.width / 5;
|
|
||||||
final rightX = 4 * size.width / 5;
|
|
||||||
final topY = size.height / 5;
|
|
||||||
final bottomY = 4 * size.height / 5;
|
|
||||||
|
|
||||||
final path = Path();
|
|
||||||
path.moveTo(leftX, topY);
|
|
||||||
path.lineTo(leftX, bottomY);
|
|
||||||
path.lineTo(rightX, bottomY);
|
|
||||||
path.lineTo(rightX, topY);
|
|
||||||
path.lineTo(leftX, topY);
|
|
||||||
path.lineTo(0, 0);
|
|
||||||
path.close();
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool shouldReclip(CustomClipper oldClipper) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ import 'package:stream_chat_flutter/src/full_screen_media.dart';
|
|||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
|
/// Widget for constructing a group of images in message
|
||||||
class ImageGroup extends StatelessWidget {
|
class ImageGroup extends StatelessWidget {
|
||||||
|
/// Constructor for creating [ImageGroup] widget
|
||||||
const ImageGroup({
|
const ImageGroup({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.images,
|
required this.images,
|
||||||
@@ -13,91 +15,98 @@ class ImageGroup extends StatelessWidget {
|
|||||||
this.onShowMessage,
|
this.onShowMessage,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
/// List of attachments to show
|
||||||
final List<Attachment> images;
|
final List<Attachment> images;
|
||||||
|
|
||||||
|
/// Message which images are attached to
|
||||||
final Message message;
|
final Message message;
|
||||||
|
|
||||||
|
/// [MessageTheme] to apply to message
|
||||||
final MessageTheme messageTheme;
|
final MessageTheme messageTheme;
|
||||||
|
|
||||||
|
/// Size of iamges
|
||||||
final Size size;
|
final Size size;
|
||||||
|
|
||||||
|
/// Callback for when show message is tapped
|
||||||
final ShowMessageCallback? onShowMessage;
|
final ShowMessageCallback? onShowMessage;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => ConstrainedBox(
|
||||||
return ConstrainedBox(
|
constraints: BoxConstraints.loose(size),
|
||||||
constraints: BoxConstraints.loose(size),
|
child: Flex(
|
||||||
child: Flex(
|
direction: Axis.vertical,
|
||||||
direction: Axis.vertical,
|
children: <Widget>[
|
||||||
children: <Widget>[
|
|
||||||
Flexible(
|
|
||||||
fit: FlexFit.tight,
|
|
||||||
child: Flex(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
direction: Axis.horizontal,
|
|
||||||
children: [
|
|
||||||
Flexible(
|
|
||||||
fit: FlexFit.tight,
|
|
||||||
child: _buildImage(context, 0),
|
|
||||||
),
|
|
||||||
Flexible(
|
|
||||||
fit: FlexFit.tight,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 2),
|
|
||||||
child: _buildImage(context, 1),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (images.length >= 3)
|
|
||||||
Flexible(
|
Flexible(
|
||||||
fit: FlexFit.tight,
|
fit: FlexFit.tight,
|
||||||
child: Padding(
|
child: Flex(
|
||||||
padding: const EdgeInsets.only(top: 2),
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
child: Flex(
|
direction: Axis.horizontal,
|
||||||
direction: Axis.horizontal,
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
Flexible(
|
||||||
children: <Widget>[
|
fit: FlexFit.tight,
|
||||||
Flexible(
|
child: _buildImage(context, 0),
|
||||||
fit: FlexFit.tight,
|
),
|
||||||
child: _buildImage(context, 2),
|
Flexible(
|
||||||
|
fit: FlexFit.tight,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 2),
|
||||||
|
child: _buildImage(context, 1),
|
||||||
),
|
),
|
||||||
if (images.length >= 4)
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (images.length >= 3)
|
||||||
|
Flexible(
|
||||||
|
fit: FlexFit.tight,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 2),
|
||||||
|
child: Flex(
|
||||||
|
direction: Axis.horizontal,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: <Widget>[
|
||||||
Flexible(
|
Flexible(
|
||||||
fit: FlexFit.tight,
|
fit: FlexFit.tight,
|
||||||
child: Padding(
|
child: _buildImage(context, 2),
|
||||||
padding: const EdgeInsets.only(left: 2),
|
),
|
||||||
child: Stack(
|
if (images.length >= 4)
|
||||||
fit: StackFit.expand,
|
Flexible(
|
||||||
children: <Widget>[
|
fit: FlexFit.tight,
|
||||||
_buildImage(context, 3),
|
child: Padding(
|
||||||
if (images.length > 4)
|
padding: const EdgeInsets.only(left: 2),
|
||||||
Positioned.fill(
|
child: Stack(
|
||||||
child: GestureDetector(
|
fit: StackFit.expand,
|
||||||
onTap: () => _onTap(context, 3),
|
children: <Widget>[
|
||||||
child: Material(
|
_buildImage(context, 3),
|
||||||
color: Colors.black38,
|
if (images.length > 4)
|
||||||
child: Center(
|
Positioned.fill(
|
||||||
child: Text(
|
child: GestureDetector(
|
||||||
'+ ${images.length - 4}',
|
onTap: () => _onTap(context, 3),
|
||||||
style: TextStyle(
|
child: Material(
|
||||||
color: Colors.white,
|
color: Colors.black38,
|
||||||
fontSize: 26,
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'+ ${images.length - 4}',
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 26,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onTap(
|
void _onTap(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
@@ -122,13 +131,11 @@ class ImageGroup extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildImage(BuildContext context, int index) {
|
Widget _buildImage(BuildContext context, int index) => ImageAttachment(
|
||||||
return ImageAttachment(
|
attachment: images[index],
|
||||||
attachment: images[index],
|
size: size,
|
||||||
size: size,
|
message: message,
|
||||||
message: message,
|
messageTheme: messageTheme,
|
||||||
messageTheme: messageTheme,
|
onAttachmentTap: () => _onTap(context, index),
|
||||||
onAttachmentTap: () => _onTap(context, index),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,26 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/attachment_actions_modal.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||||
|
|
||||||
import 'attachment_actions_modal.dart';
|
/// Header/AppBar widget for media display screen
|
||||||
|
|
||||||
class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||||
|
/// Creates a channel header
|
||||||
|
const ImageHeader({
|
||||||
|
Key? key,
|
||||||
|
required this.message,
|
||||||
|
this.currentIndex = 0,
|
||||||
|
this.showBackButton = true,
|
||||||
|
this.onBackPressed,
|
||||||
|
this.onShowMessage,
|
||||||
|
this.onTitleTap,
|
||||||
|
this.onImageTap,
|
||||||
|
this.userName = '',
|
||||||
|
this.sentAt = '',
|
||||||
|
}) : preferredSize = const Size.fromHeight(kToolbarHeight),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
/// True if this header shows the leading back button
|
/// True if this header shows the leading back button
|
||||||
final bool showBackButton;
|
final bool showBackButton;
|
||||||
|
|
||||||
@@ -22,83 +37,72 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
/// Callback to call when the image is tapped.
|
/// Callback to call when the image is tapped.
|
||||||
final VoidCallback? onImageTap;
|
final VoidCallback? onImageTap;
|
||||||
|
|
||||||
|
/// Message which attachments are attached to
|
||||||
final Message message;
|
final Message message;
|
||||||
|
|
||||||
|
/// Username of sender
|
||||||
final String userName;
|
final String userName;
|
||||||
|
|
||||||
|
/// Text which connotes the time the message was sent
|
||||||
final String sentAt;
|
final String sentAt;
|
||||||
|
|
||||||
|
/// Stores the current index of media shown
|
||||||
final int currentIndex;
|
final int currentIndex;
|
||||||
|
|
||||||
/// Creates a channel header
|
|
||||||
ImageHeader({
|
|
||||||
Key? key,
|
|
||||||
required this.message,
|
|
||||||
this.currentIndex = 0,
|
|
||||||
this.showBackButton = true,
|
|
||||||
this.onBackPressed,
|
|
||||||
this.onShowMessage,
|
|
||||||
this.onTitleTap,
|
|
||||||
this.onImageTap,
|
|
||||||
this.userName = '',
|
|
||||||
this.sentAt = '',
|
|
||||||
}) : preferredSize = Size.fromHeight(kToolbarHeight),
|
|
||||||
super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => AppBar(
|
||||||
return AppBar(
|
brightness: Theme.of(context).brightness,
|
||||||
brightness: Theme.of(context).brightness,
|
elevation: 1,
|
||||||
elevation: 1,
|
leading: showBackButton
|
||||||
leading: showBackButton
|
? IconButton(
|
||||||
? IconButton(
|
icon: StreamSvgIcon.close(
|
||||||
icon: StreamSvgIcon.close(
|
color: StreamChatTheme.of(context).colorTheme.black,
|
||||||
color: StreamChatTheme.of(context).colorTheme.black,
|
size: 24,
|
||||||
size: 24,
|
|
||||||
),
|
|
||||||
onPressed: onBackPressed,
|
|
||||||
)
|
|
||||||
: SizedBox(),
|
|
||||||
backgroundColor:
|
|
||||||
StreamChatTheme.of(context).channelTheme.channelHeaderTheme.color,
|
|
||||||
actions: <Widget>[
|
|
||||||
if (message.type != 'ephemeral')
|
|
||||||
IconButton(
|
|
||||||
icon: StreamSvgIcon.iconMenuPoint(
|
|
||||||
color: StreamChatTheme.of(context).colorTheme.black,
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
_showMessageActionModalBottomSheet(context);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
centerTitle: true,
|
|
||||||
title: message.type != 'ephemeral'
|
|
||||||
? InkWell(
|
|
||||||
onTap: onTitleTap,
|
|
||||||
child: Container(
|
|
||||||
height: preferredSize.height,
|
|
||||||
width: preferredSize.width,
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: <Widget>[
|
|
||||||
Text(
|
|
||||||
userName,
|
|
||||||
style: StreamChatTheme.of(context).textTheme.headlineBold,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
sentAt,
|
|
||||||
style: StreamChatTheme.of(context)
|
|
||||||
.channelPreviewTheme
|
|
||||||
.subtitle,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
onPressed: onBackPressed,
|
||||||
|
)
|
||||||
|
: const SizedBox(),
|
||||||
|
backgroundColor:
|
||||||
|
StreamChatTheme.of(context).channelTheme.channelHeaderTheme.color,
|
||||||
|
actions: <Widget>[
|
||||||
|
if (message.type != 'ephemeral')
|
||||||
|
IconButton(
|
||||||
|
icon: StreamSvgIcon.iconMenuPoint(
|
||||||
|
color: StreamChatTheme.of(context).colorTheme.black,
|
||||||
),
|
),
|
||||||
)
|
onPressed: () {
|
||||||
: SizedBox(),
|
_showMessageActionModalBottomSheet(context);
|
||||||
);
|
},
|
||||||
}
|
),
|
||||||
|
],
|
||||||
|
centerTitle: true,
|
||||||
|
title: message.type != 'ephemeral'
|
||||||
|
? InkWell(
|
||||||
|
onTap: onTitleTap,
|
||||||
|
child: SizedBox(
|
||||||
|
height: preferredSize.height,
|
||||||
|
width: preferredSize.width,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
userName,
|
||||||
|
style:
|
||||||
|
StreamChatTheme.of(context).textTheme.headlineBold,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
sentAt,
|
||||||
|
style: StreamChatTheme.of(context)
|
||||||
|
.channelPreviewTheme
|
||||||
|
.subtitle,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const SizedBox(),
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Size preferredSize;
|
final Size preferredSize;
|
||||||
@@ -109,16 +113,14 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
final result = await showDialog(
|
final result = await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
barrierColor: StreamChatTheme.of(context).colorTheme.overlay,
|
barrierColor: StreamChatTheme.of(context).colorTheme.overlay,
|
||||||
builder: (context) {
|
builder: (context) => StreamChannel(
|
||||||
return StreamChannel(
|
channel: channel,
|
||||||
channel: channel,
|
child: AttachmentActionsModal(
|
||||||
child: AttachmentActionsModal(
|
message: message,
|
||||||
message: message,
|
currentIndex: currentIndex,
|
||||||
currentIndex: currentIndex,
|
onShowMessage: onShowMessage,
|
||||||
onShowMessage: onShowMessage,
|
),
|
||||||
),
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
|
|||||||
@@ -812,116 +812,123 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
final renderBox = context.findRenderObject() as RenderBox;
|
final renderBox = context.findRenderObject() as RenderBox;
|
||||||
final size = renderBox.size;
|
final size = renderBox.size;
|
||||||
|
|
||||||
return OverlayEntry(builder: (context) => Positioned(
|
return OverlayEntry(
|
||||||
bottom: size.height + MediaQuery.of(context).viewInsets.bottom,
|
builder: (context) => Positioned(
|
||||||
left: 0,
|
bottom: size.height + MediaQuery.of(context).viewInsets.bottom,
|
||||||
right: 0,
|
left: 0,
|
||||||
child: TweenAnimationBuilder<double>(
|
right: 0,
|
||||||
tween: Tween(begin: 0, end: 1),
|
child: TweenAnimationBuilder<double>(
|
||||||
duration: Duration(milliseconds: 300),
|
tween: Tween(begin: 0, end: 1),
|
||||||
curve: Curves.easeInOutExpo,
|
duration: Duration(milliseconds: 300),
|
||||||
builder: (context, val, wid) => Transform.scale(
|
curve: Curves.easeInOutExpo,
|
||||||
scale: val,
|
builder: (context, val, wid) => Transform.scale(
|
||||||
child: Padding(
|
scale: val,
|
||||||
padding: const EdgeInsets.all(8),
|
child: Padding(
|
||||||
child: Card(
|
padding: const EdgeInsets.all(8),
|
||||||
elevation: 2,
|
child: Card(
|
||||||
shape: RoundedRectangleBorder(
|
elevation: 2,
|
||||||
borderRadius: BorderRadius.circular(8),
|
shape: RoundedRectangleBorder(
|
||||||
),
|
borderRadius: BorderRadius.circular(8),
|
||||||
color: StreamChatTheme.of(context).colorTheme.white,
|
),
|
||||||
clipBehavior: Clip.antiAlias,
|
color: StreamChatTheme.of(context).colorTheme.white,
|
||||||
child: Container(
|
clipBehavior: Clip.antiAlias,
|
||||||
constraints: BoxConstraints.loose(Size.fromHeight(400)),
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
constraints:
|
||||||
color: StreamChatTheme.of(context).colorTheme.white,
|
BoxConstraints.loose(Size.fromHeight(400)),
|
||||||
borderRadius: BorderRadius.circular(8)),
|
decoration: BoxDecoration(
|
||||||
child: ListView(
|
color: StreamChatTheme.of(context)
|
||||||
padding: const EdgeInsets.all(0),
|
.colorTheme
|
||||||
shrinkWrap: true,
|
.white,
|
||||||
children: [
|
borderRadius: BorderRadius.circular(8)),
|
||||||
if (commands.isNotEmpty)
|
child: ListView(
|
||||||
Padding(
|
padding: const EdgeInsets.all(0),
|
||||||
padding: const EdgeInsets.only(top: 8),
|
shrinkWrap: true,
|
||||||
child: Row(
|
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
if (commands.isNotEmpty)
|
||||||
padding: const EdgeInsets.symmetric(
|
Padding(
|
||||||
horizontal: 8,
|
padding: const EdgeInsets.only(top: 8),
|
||||||
),
|
child: Row(
|
||||||
child: StreamSvgIcon.lightning(
|
children: [
|
||||||
color: StreamChatTheme.of(context)
|
Padding(
|
||||||
.colorTheme
|
padding: const EdgeInsets.symmetric(
|
||||||
.accentBlue,
|
horizontal: 8,
|
||||||
|
),
|
||||||
|
child: StreamSvgIcon.lightning(
|
||||||
|
color: StreamChatTheme.of(context)
|
||||||
|
.colorTheme
|
||||||
|
.accentBlue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Instant Commands',
|
||||||
|
style: TextStyle(
|
||||||
|
color: StreamChatTheme.of(context)
|
||||||
|
.colorTheme
|
||||||
|
.black
|
||||||
|
.withOpacity(.5),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 10,
|
||||||
),
|
),
|
||||||
Text(
|
...commands
|
||||||
'Instant Commands',
|
.map(
|
||||||
style: TextStyle(
|
(c) => InkWell(
|
||||||
color: StreamChatTheme.of(context)
|
onTap: () {
|
||||||
.colorTheme
|
_setCommand(c);
|
||||||
.black
|
},
|
||||||
.withOpacity(.5),
|
child: SizedBox(
|
||||||
),
|
height: 40,
|
||||||
)
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
width: 16,
|
||||||
|
),
|
||||||
|
_buildCommandIcon(c.name),
|
||||||
|
const SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
text: c.name.capitalize(),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight:
|
||||||
|
FontWeight.bold),
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text:
|
||||||
|
' /${c.name} ${c.args}',
|
||||||
|
style:
|
||||||
|
StreamChatTheme.of(
|
||||||
|
context)
|
||||||
|
.textTheme
|
||||||
|
.body
|
||||||
|
.copyWith(
|
||||||
|
color: StreamChatTheme.of(
|
||||||
|
context)
|
||||||
|
.colorTheme
|
||||||
|
.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
|
||||||
height: 10,
|
|
||||||
),
|
),
|
||||||
...commands
|
),
|
||||||
.map(
|
)),
|
||||||
(c) => InkWell(
|
));
|
||||||
onTap: () {
|
|
||||||
_setCommand(c);
|
|
||||||
},
|
|
||||||
child: SizedBox(
|
|
||||||
height: 40,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
const SizedBox(
|
|
||||||
width: 16,
|
|
||||||
),
|
|
||||||
_buildCommandIcon(c.name),
|
|
||||||
const SizedBox(
|
|
||||||
width: 8,
|
|
||||||
),
|
|
||||||
Text.rich(
|
|
||||||
TextSpan(
|
|
||||||
text: c.name.capitalize(),
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold),
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: ' /${c.name} ${c.args}',
|
|
||||||
style:
|
|
||||||
StreamChatTheme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.body
|
|
||||||
.copyWith(
|
|
||||||
color: StreamChatTheme
|
|
||||||
.of(context)
|
|
||||||
.colorTheme
|
|
||||||
.grey,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildFilePickerSection() {
|
Widget _buildFilePickerSection() {
|
||||||
|
|||||||
Reference in New Issue
Block a user