feat: add GalleryHeader/GalleryFooter theme classes (#546)
* chore: Add ImageHeaderTheme and ImageHeaderThemeData to stream_chat_theme.dart This commit adds the following classes to `stream_chat_theme.dart`: 1. `ImageHeaderTheme`, an `InheritedTheme` widget that can wrap other widgets and provide that sub-tree with an `ImageHeaderThemeData` 2. `ImageHeaderThemeData`, a class that allows for the customization of `ImageHeader` widgets * Make ImageHeaderThemeData constructor const * test: initial ImageHeaderThemeData tests Tests the copyWith, ==, and hashcode functions * test: add tests for default ImageHeaderThemeData values Tests the default values of ImageHeaderThemeData that gets built in a StreamChatThemeData against control values. * Update test name and add comments describing control * fix: use ImageHeaderTheme.of(context) instead of StreamChatTheme.of(context) This ensures that ImageHeader will always look for the closest ImageHeaderThemeData in the widget tree. * test: add dark theme default values test * run dartfmt on test file * test: lerping between ImageHeaderThemeData Added tests that lerp from one ImageHeaderThemeData to another (light to dark and dark to light) * test: merging ImageHeaderThemeData Added tests that merge light and dark ImageHeaderThemeData together (light + dark = dark, dark + light = light) * test: add a half-lerp test Added a test that lerps halway between light and dark ImageHeaderThemeData * chore: Add ImageFooterTheme and ImageFooterThemeData to stream_chat_theme.dart This commit adds the following classes to `stream_chat_theme.dart`: 1. `ImageFooterTheme`, an `InheritedTheme` widget that can wrap other widgets and provide that sub-tree with an `ImageFooterThemeData` 2. `ImageFooterThemeData`, a class that allows for the customization of `ImageFooter` widget * chore: apply ImageFooterThemeData to image_footer.dart * fix: make ImageFooterThemeData constructor const * test: add tests for ImageFooterThemeData * add a missing trailing comma * chore: rename ImageHeader & ImageFooter to GalleryHeader and GalleryFooter The respective theme classes and tests have also been renamed * Run dartfmt on test * test: fix failing tests due to renaming
This commit is contained in:
@@ -6,8 +6,8 @@ import 'package:chewie/chewie.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:jiffy/jiffy.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
import 'package:stream_chat_flutter/src/image_footer.dart';
|
||||
import 'package:stream_chat_flutter/src/image_header.dart';
|
||||
import 'package:stream_chat_flutter/src/gallery_footer.dart';
|
||||
import 'package:stream_chat_flutter/src/gallery_header.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
@@ -180,7 +180,7 @@ class _FullScreenMediaState extends State<FullScreenMedia>
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ImageHeader(
|
||||
GalleryHeader(
|
||||
userName: widget.userName,
|
||||
sentAt:
|
||||
// ignore: lines_longer_than_80_chars
|
||||
@@ -198,7 +198,7 @@ class _FullScreenMediaState extends State<FullScreenMedia>
|
||||
},
|
||||
),
|
||||
if (widget.message.type != 'ephemeral')
|
||||
ImageFooter(
|
||||
GalleryFooter(
|
||||
currentPage: _currentPage,
|
||||
totalPages: widget.mediaAttachments.length,
|
||||
mediaAttachments: widget.mediaAttachments,
|
||||
|
||||
+16
-13
@@ -13,9 +13,9 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
/// Footer widget for media display
|
||||
class ImageFooter extends StatefulWidget implements PreferredSizeWidget {
|
||||
class GalleryFooter extends StatefulWidget implements PreferredSizeWidget {
|
||||
/// Creates a channel header
|
||||
const ImageFooter({
|
||||
const GalleryFooter({
|
||||
Key? key,
|
||||
required this.message,
|
||||
this.onBackPressed,
|
||||
@@ -54,13 +54,13 @@ class ImageFooter extends StatefulWidget implements PreferredSizeWidget {
|
||||
final ValueChanged<int>? mediaSelectedCallBack;
|
||||
|
||||
@override
|
||||
_ImageFooterState createState() => _ImageFooterState();
|
||||
_GalleryFooterState createState() => _GalleryFooterState();
|
||||
|
||||
@override
|
||||
final Size preferredSize;
|
||||
}
|
||||
|
||||
class _ImageFooterState extends State<ImageFooter> {
|
||||
class _GalleryFooterState extends State<GalleryFooter> {
|
||||
final TextEditingController _messageController = TextEditingController();
|
||||
final FocusNode _messageFocusNode = FocusNode();
|
||||
|
||||
@@ -78,7 +78,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
Widget build(BuildContext context) {
|
||||
const showShareButton = !kIsWeb;
|
||||
final mediaQueryData = MediaQuery.of(context);
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
final galleryFooterThemeData = GalleryFooterTheme.of(context);
|
||||
return SizedBox.fromSize(
|
||||
size: Size(
|
||||
mediaQueryData.size.width,
|
||||
@@ -88,7 +88,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
context: context,
|
||||
removeTop: true,
|
||||
child: BottomAppBar(
|
||||
color: chatThemeData.colorTheme.barsBg,
|
||||
color: galleryFooterThemeData.backgroundColor,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
@@ -100,7 +100,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
IconButton(
|
||||
icon: StreamSvgIcon.iconShare(
|
||||
size: 24,
|
||||
color: chatThemeData.colorTheme.textHighEmphasis,
|
||||
color: galleryFooterThemeData.shareIconColor,
|
||||
),
|
||||
onPressed: () async {
|
||||
final attachment =
|
||||
@@ -136,7 +136,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'${widget.currentPage + 1} of ${widget.totalPages}',
|
||||
style: chatThemeData.textTheme.headlineBold,
|
||||
style: galleryFooterThemeData.titleTextStyle,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -144,7 +144,7 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
),
|
||||
IconButton(
|
||||
icon: StreamSvgIcon.iconGrid(
|
||||
color: chatThemeData.colorTheme.textHighEmphasis,
|
||||
color: galleryFooterThemeData.gridIconButtonColor,
|
||||
),
|
||||
onPressed: () => _showPhotosModal(context),
|
||||
),
|
||||
@@ -157,10 +157,11 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
|
||||
void _showPhotosModal(context) {
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
final galleryFooterThemeData = GalleryFooterTheme.of(context);
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
barrierColor: chatThemeData.colorTheme.overlay,
|
||||
backgroundColor: chatThemeData.colorTheme.barsBg,
|
||||
barrierColor: galleryFooterThemeData.bottomSheetBarrierColor,
|
||||
backgroundColor: galleryFooterThemeData.bottomSheetBackgroundColor,
|
||||
isScrollControlled: true,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
@@ -191,7 +192,8 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Text(
|
||||
'Photos',
|
||||
style: chatThemeData.textTheme.headlineBold,
|
||||
style:
|
||||
galleryFooterThemeData.bottomSheetPhotosTextStyle,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -199,7 +201,8 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
alignment: Alignment.centerRight,
|
||||
child: IconButton(
|
||||
icon: StreamSvgIcon.close(
|
||||
color: chatThemeData.colorTheme.textHighEmphasis,
|
||||
color:
|
||||
galleryFooterThemeData.bottomSheetCloseIconColor,
|
||||
),
|
||||
onPressed: () => Navigator.maybePop(context),
|
||||
),
|
||||
+11
-9
@@ -5,9 +5,9 @@ import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
/// Header/AppBar widget for media display screen
|
||||
class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
class GalleryHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
/// Creates a channel header
|
||||
const ImageHeader({
|
||||
const GalleryHeader({
|
||||
Key? key,
|
||||
required this.message,
|
||||
this.currentIndex = 0,
|
||||
@@ -51,7 +51,7 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final chatThemeData = StreamChatTheme.of(context);
|
||||
final galleryHeaderThemeData = GalleryHeaderTheme.of(context);
|
||||
return AppBar(
|
||||
textTheme: Theme.of(context).textTheme,
|
||||
brightness: Theme.of(context).brightness,
|
||||
@@ -59,18 +59,18 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
leading: showBackButton
|
||||
? IconButton(
|
||||
icon: StreamSvgIcon.close(
|
||||
color: chatThemeData.colorTheme.textHighEmphasis,
|
||||
color: galleryHeaderThemeData.closeButtonColor,
|
||||
size: 24,
|
||||
),
|
||||
onPressed: onBackPressed,
|
||||
)
|
||||
: const SizedBox(),
|
||||
backgroundColor: chatThemeData.channelTheme.channelHeaderTheme.color,
|
||||
backgroundColor: galleryHeaderThemeData.backgroundColor,
|
||||
actions: <Widget>[
|
||||
if (message.type != 'ephemeral')
|
||||
IconButton(
|
||||
icon: StreamSvgIcon.iconMenuPoint(
|
||||
color: chatThemeData.colorTheme.textHighEmphasis,
|
||||
color: galleryHeaderThemeData.iconMenuPointColor,
|
||||
),
|
||||
onPressed: () {
|
||||
_showMessageActionModalBottomSheet(context);
|
||||
@@ -90,11 +90,11 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
children: <Widget>[
|
||||
Text(
|
||||
userName,
|
||||
style: chatThemeData.textTheme.headlineBold,
|
||||
style: galleryHeaderThemeData.titleTextStyle,
|
||||
),
|
||||
Text(
|
||||
sentAt,
|
||||
style: chatThemeData.channelPreviewTheme.subtitle,
|
||||
style: galleryHeaderThemeData.subtitleTextStyle,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -109,11 +109,13 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
|
||||
void _showMessageActionModalBottomSheet(BuildContext context) async {
|
||||
final channel = StreamChannel.of(context).channel;
|
||||
final galleryHeaderThemeData =
|
||||
StreamChatTheme.of(context).galleryHeaderTheme;
|
||||
|
||||
final result = await showDialog(
|
||||
useRootNavigator: false,
|
||||
context: context,
|
||||
barrierColor: StreamChatTheme.of(context).colorTheme.overlay,
|
||||
barrierColor: galleryHeaderThemeData.bottomSheetBarrierColor,
|
||||
builder: (context) => StreamChannel(
|
||||
channel: channel,
|
||||
child: AttachmentActionsModal(
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/src/channel_header.dart';
|
||||
import 'package:stream_chat_flutter/src/channel_preview.dart';
|
||||
@@ -58,6 +59,8 @@ class StreamChatThemeData {
|
||||
Widget Function(BuildContext, User)? defaultUserImage,
|
||||
IconThemeData? primaryIconTheme,
|
||||
List<ReactionIcon>? reactionIcons,
|
||||
GalleryHeaderThemeData? imageHeaderTheme,
|
||||
GalleryFooterThemeData? imageFooterTheme,
|
||||
}) {
|
||||
brightness ??= colorTheme?.brightness ?? Brightness.light;
|
||||
final isDark = brightness == Brightness.dark;
|
||||
@@ -80,6 +83,8 @@ class StreamChatThemeData {
|
||||
defaultUserImage: defaultUserImage,
|
||||
primaryIconTheme: primaryIconTheme,
|
||||
reactionIcons: reactionIcons,
|
||||
galleryHeaderTheme: imageHeaderTheme,
|
||||
galleryFooterTheme: imageFooterTheme,
|
||||
);
|
||||
|
||||
return defaultData.merge(customizedData);
|
||||
@@ -107,6 +112,8 @@ class StreamChatThemeData {
|
||||
required this.defaultUserImage,
|
||||
required this.primaryIconTheme,
|
||||
required this.reactionIcons,
|
||||
required this.galleryHeaderTheme,
|
||||
required this.galleryFooterTheme,
|
||||
});
|
||||
|
||||
/// Create a theme from a Material [Theme]
|
||||
@@ -136,6 +143,14 @@ class StreamChatThemeData {
|
||||
/// Theme of the chat widgets dedicated to a channel
|
||||
final ChannelTheme channelTheme;
|
||||
|
||||
/// The default style for [GalleryHeader]s below the overall
|
||||
/// [StreamChatTheme].
|
||||
final GalleryHeaderThemeData galleryHeaderTheme;
|
||||
|
||||
/// The default style for [GalleryFooter]s below the overall
|
||||
/// [StreamChatTheme].
|
||||
final GalleryFooterThemeData galleryFooterTheme;
|
||||
|
||||
/// Theme of the current user messages
|
||||
final MessageTheme ownMessageTheme;
|
||||
|
||||
@@ -172,6 +187,8 @@ class StreamChatThemeData {
|
||||
IconThemeData? primaryIconTheme,
|
||||
ChannelListHeaderTheme? channelListHeaderTheme,
|
||||
List<ReactionIcon>? reactionIcons,
|
||||
GalleryHeaderThemeData? galleryHeaderTheme,
|
||||
GalleryFooterThemeData? galleryFooterTheme,
|
||||
}) =>
|
||||
StreamChatThemeData.raw(
|
||||
channelListHeaderTheme:
|
||||
@@ -188,6 +205,8 @@ class StreamChatThemeData {
|
||||
otherMessageTheme: this.otherMessageTheme.merge(otherMessageTheme),
|
||||
messageInputTheme: this.messageInputTheme.merge(messageInputTheme),
|
||||
reactionIcons: reactionIcons ?? this.reactionIcons,
|
||||
galleryHeaderTheme: galleryHeaderTheme ?? this.galleryHeaderTheme,
|
||||
galleryFooterTheme: galleryFooterTheme ?? this.galleryFooterTheme,
|
||||
);
|
||||
|
||||
/// Merge themes
|
||||
@@ -207,6 +226,8 @@ class StreamChatThemeData {
|
||||
otherMessageTheme: otherMessageTheme.merge(other.otherMessageTheme),
|
||||
messageInputTheme: messageInputTheme.merge(other.messageInputTheme),
|
||||
reactionIcons: other.reactionIcons,
|
||||
galleryHeaderTheme: galleryHeaderTheme.merge(other.galleryHeaderTheme),
|
||||
galleryFooterTheme: galleryFooterTheme.merge(other.galleryFooterTheme),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -219,6 +240,40 @@ class StreamChatThemeData {
|
||||
final accentColor = colorTheme.accentPrimary;
|
||||
final iconTheme =
|
||||
IconThemeData(color: colorTheme.textHighEmphasis.withOpacity(.5));
|
||||
final channelTheme = ChannelTheme(
|
||||
channelHeaderTheme: ChannelHeaderTheme(
|
||||
avatarTheme: AvatarTheme(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
constraints: const BoxConstraints.tightFor(
|
||||
height: 40,
|
||||
width: 40,
|
||||
),
|
||||
),
|
||||
color: colorTheme.barsBg,
|
||||
title: textTheme.headlineBold,
|
||||
subtitle: textTheme.footnote.copyWith(
|
||||
color: const Color(0xff7A7A7A),
|
||||
),
|
||||
),
|
||||
);
|
||||
final channelPreviewTheme = ChannelPreviewTheme(
|
||||
unreadCounterColor: colorTheme.accentError,
|
||||
avatarTheme: AvatarTheme(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
constraints: const BoxConstraints.tightFor(
|
||||
height: 40,
|
||||
width: 40,
|
||||
),
|
||||
),
|
||||
title: textTheme.bodyBold,
|
||||
subtitle: textTheme.footnote.copyWith(
|
||||
color: const Color(0xff7A7A7A),
|
||||
),
|
||||
lastMessageAt: textTheme.footnote.copyWith(
|
||||
color: colorTheme.textHighEmphasis.withOpacity(.5),
|
||||
),
|
||||
indicatorIconSize: 16,
|
||||
);
|
||||
return StreamChatThemeData.raw(
|
||||
textTheme: textTheme,
|
||||
colorTheme: colorTheme,
|
||||
@@ -231,24 +286,7 @@ class StreamChatThemeData {
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
channelPreviewTheme: ChannelPreviewTheme(
|
||||
unreadCounterColor: colorTheme.accentError,
|
||||
avatarTheme: AvatarTheme(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
constraints: const BoxConstraints.tightFor(
|
||||
height: 40,
|
||||
width: 40,
|
||||
),
|
||||
),
|
||||
title: textTheme.bodyBold,
|
||||
subtitle: textTheme.footnote.copyWith(
|
||||
color: const Color(0xff7A7A7A),
|
||||
),
|
||||
lastMessageAt: textTheme.footnote.copyWith(
|
||||
color: colorTheme.textHighEmphasis.withOpacity(.5),
|
||||
),
|
||||
indicatorIconSize: 16,
|
||||
),
|
||||
channelPreviewTheme: channelPreviewTheme,
|
||||
channelListHeaderTheme: ChannelListHeaderTheme(
|
||||
avatarTheme: AvatarTheme(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
@@ -260,22 +298,7 @@ class StreamChatThemeData {
|
||||
color: colorTheme.barsBg,
|
||||
title: textTheme.headlineBold,
|
||||
),
|
||||
channelTheme: ChannelTheme(
|
||||
channelHeaderTheme: ChannelHeaderTheme(
|
||||
avatarTheme: AvatarTheme(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
constraints: const BoxConstraints.tightFor(
|
||||
height: 40,
|
||||
width: 40,
|
||||
),
|
||||
),
|
||||
color: colorTheme.barsBg,
|
||||
title: textTheme.headlineBold,
|
||||
subtitle: textTheme.footnote.copyWith(
|
||||
color: const Color(0xff7A7A7A),
|
||||
),
|
||||
),
|
||||
),
|
||||
channelTheme: channelTheme,
|
||||
ownMessageTheme: MessageTheme(
|
||||
messageAuthor:
|
||||
textTheme.footnote.copyWith(color: colorTheme.textLowEmphasis),
|
||||
@@ -407,6 +430,24 @@ class StreamChatThemeData {
|
||||
},
|
||||
),
|
||||
],
|
||||
galleryHeaderTheme: GalleryHeaderThemeData(
|
||||
closeButtonColor: colorTheme.textHighEmphasis,
|
||||
backgroundColor: channelTheme.channelHeaderTheme.color,
|
||||
iconMenuPointColor: colorTheme.textHighEmphasis,
|
||||
titleTextStyle: textTheme.headlineBold,
|
||||
subtitleTextStyle: channelPreviewTheme.subtitle,
|
||||
bottomSheetBarrierColor: colorTheme.overlay,
|
||||
),
|
||||
galleryFooterTheme: GalleryFooterThemeData(
|
||||
backgroundColor: colorTheme.barsBg,
|
||||
shareIconColor: colorTheme.textHighEmphasis,
|
||||
titleTextStyle: textTheme.headlineBold,
|
||||
gridIconButtonColor: colorTheme.textHighEmphasis,
|
||||
bottomSheetBarrierColor: colorTheme.overlay,
|
||||
bottomSheetBackgroundColor: colorTheme.barsBg,
|
||||
bottomSheetPhotosTextStyle: textTheme.headlineBold,
|
||||
bottomSheetCloseIconColor: colorTheme.textHighEmphasis,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1287,3 +1328,393 @@ class Effect {
|
||||
blur: blur ?? this.blur,
|
||||
);
|
||||
}
|
||||
|
||||
/// Overrides the default style of [GalleryHeader] descendants.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [GalleryHeaderThemeData], which is used to configure this theme.
|
||||
class GalleryHeaderTheme extends InheritedTheme {
|
||||
/// Creates an [GalleryHeaderTheme].
|
||||
///
|
||||
/// The [data] parameter must not be null.
|
||||
const GalleryHeaderTheme({
|
||||
Key? key,
|
||||
required this.data,
|
||||
required Widget child,
|
||||
}) : super(key: key, child: child);
|
||||
|
||||
/// The configuration of this theme.
|
||||
final GalleryHeaderThemeData data;
|
||||
|
||||
/// The closest instance of this class that encloses the given context.
|
||||
///
|
||||
/// If there is no enclosing [GalleryHeaderTheme] widget, then
|
||||
/// [StreamChatThemeData.galleryHeaderTheme] is used.
|
||||
///
|
||||
/// Typical usage is as follows:
|
||||
///
|
||||
/// ```dart
|
||||
/// ImageHeaderTheme theme = ImageHeaderTheme.of(context);
|
||||
/// ```
|
||||
static GalleryHeaderThemeData of(BuildContext context) {
|
||||
final galleryHeaderTheme =
|
||||
context.dependOnInheritedWidgetOfExactType<GalleryHeaderTheme>();
|
||||
return galleryHeaderTheme?.data ??
|
||||
StreamChatTheme.of(context).galleryHeaderTheme;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget wrap(BuildContext context, Widget child) =>
|
||||
GalleryHeaderTheme(data: data, child: child);
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(GalleryHeaderTheme oldWidget) =>
|
||||
data != oldWidget.data;
|
||||
}
|
||||
|
||||
/// A style that overrides the default appearance of [GalleryHeader]s when used
|
||||
/// with [GalleryHeaderTheme] or with the overall [StreamChatTheme]'s
|
||||
/// [StreamChatThemeData.galleryHeaderTheme].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [GalleryHeaderTheme], the theme which is configured with this class.
|
||||
/// * [StreamChatThemeData.galleryHeaderTheme], which can be used to override
|
||||
/// the default style for [GalleryHeader]s below the overall [StreamChatTheme].
|
||||
class GalleryHeaderThemeData with Diagnosticable {
|
||||
/// Creates an [GalleryHeaderThemeData].
|
||||
const GalleryHeaderThemeData({
|
||||
this.closeButtonColor,
|
||||
this.backgroundColor,
|
||||
this.iconMenuPointColor,
|
||||
this.titleTextStyle,
|
||||
this.subtitleTextStyle,
|
||||
this.bottomSheetBarrierColor,
|
||||
});
|
||||
|
||||
/// The color of the "close" button.
|
||||
///
|
||||
/// Defaults to [ColorTheme.textHighEmphasis].
|
||||
final Color? closeButtonColor;
|
||||
|
||||
/// The background color of the [GalleryHeader] widget.
|
||||
///
|
||||
/// Defaults to [ChannelHeaderTheme.color].
|
||||
final Color? backgroundColor;
|
||||
|
||||
/// Defaults to [ColorTheme.textHighEmphasis].
|
||||
final Color? iconMenuPointColor;
|
||||
|
||||
/// The [TextStyle] to use for the [GalleryHeader] title text.
|
||||
///
|
||||
/// Defaults to [TextTheme.headlineBold].
|
||||
final TextStyle? titleTextStyle;
|
||||
|
||||
/// The [TextStyle] to use for the [GalleryHeader] subtitle text.
|
||||
///
|
||||
/// Defaults to [ChannelPreviewTheme.subtitle].
|
||||
final TextStyle? subtitleTextStyle;
|
||||
|
||||
///
|
||||
final Color? bottomSheetBarrierColor;
|
||||
|
||||
/// Copies this [GalleryHeaderThemeData] to another.
|
||||
GalleryHeaderThemeData copyWith({
|
||||
Color? closeButtonColor,
|
||||
Color? backgroundColor,
|
||||
Color? iconMenuPointColor,
|
||||
TextStyle? titleTextStyle,
|
||||
TextStyle? subtitleTextStyle,
|
||||
Color? bottomSheetBarrierColor,
|
||||
}) =>
|
||||
GalleryHeaderThemeData(
|
||||
closeButtonColor: closeButtonColor ?? this.closeButtonColor,
|
||||
backgroundColor: backgroundColor ?? this.backgroundColor,
|
||||
iconMenuPointColor: iconMenuPointColor ?? this.iconMenuPointColor,
|
||||
titleTextStyle: titleTextStyle ?? this.titleTextStyle,
|
||||
subtitleTextStyle: subtitleTextStyle ?? this.subtitleTextStyle,
|
||||
bottomSheetBarrierColor:
|
||||
bottomSheetBarrierColor ?? this.bottomSheetBarrierColor,
|
||||
);
|
||||
|
||||
/// Linearly interpolate between two [GalleryHeader] themes.
|
||||
///
|
||||
/// All the properties must be non-null.
|
||||
GalleryHeaderThemeData lerp(
|
||||
GalleryHeaderThemeData a,
|
||||
GalleryHeaderThemeData b,
|
||||
double t,
|
||||
) =>
|
||||
GalleryHeaderThemeData(
|
||||
closeButtonColor: Color.lerp(a.closeButtonColor, b.closeButtonColor, t),
|
||||
backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t),
|
||||
iconMenuPointColor:
|
||||
Color.lerp(a.iconMenuPointColor, b.iconMenuPointColor, t),
|
||||
titleTextStyle: TextStyle.lerp(a.titleTextStyle, b.titleTextStyle, t),
|
||||
subtitleTextStyle:
|
||||
TextStyle.lerp(a.subtitleTextStyle, b.subtitleTextStyle, t),
|
||||
bottomSheetBarrierColor:
|
||||
Color.lerp(a.bottomSheetBarrierColor, b.bottomSheetBarrierColor, t),
|
||||
);
|
||||
|
||||
/// Merges one [GalleryHeaderThemeData] with the another
|
||||
GalleryHeaderThemeData merge(GalleryHeaderThemeData? other) {
|
||||
if (other == null) return this;
|
||||
return copyWith(
|
||||
closeButtonColor: other.closeButtonColor,
|
||||
backgroundColor: other.backgroundColor,
|
||||
iconMenuPointColor: other.iconMenuPointColor,
|
||||
titleTextStyle: other.titleTextStyle,
|
||||
subtitleTextStyle: other.subtitleTextStyle,
|
||||
bottomSheetBarrierColor: other.bottomSheetBarrierColor,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is GalleryHeaderThemeData &&
|
||||
runtimeType == other.runtimeType &&
|
||||
closeButtonColor == other.closeButtonColor &&
|
||||
backgroundColor == other.backgroundColor &&
|
||||
iconMenuPointColor == other.iconMenuPointColor &&
|
||||
titleTextStyle == other.titleTextStyle &&
|
||||
subtitleTextStyle == other.subtitleTextStyle &&
|
||||
bottomSheetBarrierColor == other.bottomSheetBarrierColor;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
closeButtonColor.hashCode ^
|
||||
backgroundColor.hashCode ^
|
||||
iconMenuPointColor.hashCode ^
|
||||
titleTextStyle.hashCode ^
|
||||
subtitleTextStyle.hashCode ^
|
||||
bottomSheetBarrierColor.hashCode;
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties
|
||||
..add(ColorProperty('closeButtonColor', closeButtonColor))
|
||||
..add(ColorProperty('backgroundColor', backgroundColor))
|
||||
..add(ColorProperty('iconMenuPointColor', iconMenuPointColor))
|
||||
..add(DiagnosticsProperty('titleTextStyle', titleTextStyle))
|
||||
..add(DiagnosticsProperty('subtitleTextStyle', subtitleTextStyle))
|
||||
..add(ColorProperty('bottomSheetBarrierColor', bottomSheetBarrierColor));
|
||||
}
|
||||
}
|
||||
|
||||
/// Overrides the default style of [GalleryFooter] descendants.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [GalleryFooterThemeData], which is used to configure this theme.
|
||||
class GalleryFooterTheme extends InheritedTheme {
|
||||
/// Creates an [GalleryFooterTheme].
|
||||
///
|
||||
/// The [data] parameter must not be null.
|
||||
const GalleryFooterTheme({
|
||||
Key? key,
|
||||
required this.data,
|
||||
required Widget child,
|
||||
}) : super(key: key, child: child);
|
||||
|
||||
/// The configuration of this theme.
|
||||
final GalleryFooterThemeData data;
|
||||
|
||||
/// The closest instance of this class that encloses the given context.
|
||||
///
|
||||
/// If there is no enclosing [GalleryFooterTheme] widget, then
|
||||
/// [StreamChatThemeData.galleryFooterTheme] is used.
|
||||
///
|
||||
/// Typical usage is as follows:
|
||||
///
|
||||
/// ```dart
|
||||
/// ImageFooterTheme theme = ImageFooterTheme.of(context);
|
||||
/// ```
|
||||
static GalleryFooterThemeData of(BuildContext context) {
|
||||
final imageFooterTheme =
|
||||
context.dependOnInheritedWidgetOfExactType<GalleryFooterTheme>();
|
||||
return imageFooterTheme?.data ??
|
||||
StreamChatTheme.of(context).galleryFooterTheme;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget wrap(BuildContext context, Widget child) =>
|
||||
GalleryFooterTheme(data: data, child: child);
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(GalleryFooterTheme oldWidget) =>
|
||||
data != oldWidget.data;
|
||||
}
|
||||
|
||||
/// A style that overrides the default appearance of [GalleryFooter]s when used
|
||||
/// with [GalleryFooterTheme] or with the overall [StreamChatTheme]'s
|
||||
/// [StreamChatThemeData.galleryFooterTheme].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [GalleryFooterTheme], the theme which is configured with this class.
|
||||
/// * [StreamChatThemeData.galleryFooterTheme], which can be used to override
|
||||
/// the default style for [GalleryFooter]s below the overall [StreamChatTheme].
|
||||
class GalleryFooterThemeData with Diagnosticable {
|
||||
/// Creates an [GalleryFooterThemeData].
|
||||
const GalleryFooterThemeData({
|
||||
this.backgroundColor,
|
||||
this.shareIconColor,
|
||||
this.titleTextStyle,
|
||||
this.gridIconButtonColor,
|
||||
this.bottomSheetBarrierColor,
|
||||
this.bottomSheetBackgroundColor,
|
||||
this.bottomSheetPhotosTextStyle,
|
||||
this.bottomSheetCloseIconColor,
|
||||
});
|
||||
|
||||
/// The background color for the [GalleryFooter] widget.
|
||||
///
|
||||
/// Defaults to [ColorTheme.barsBg].
|
||||
final Color? backgroundColor;
|
||||
|
||||
/// The color for the "share" icon.
|
||||
///
|
||||
/// Defaults to [ColorTheme.textHighEmphasis].
|
||||
final Color? shareIconColor;
|
||||
|
||||
/// The [TextStyle] to use for the [GalleryFooter] title text.
|
||||
///
|
||||
/// Defaults to [TextTheme.headlineBold].
|
||||
final TextStyle? titleTextStyle;
|
||||
|
||||
/// The color to use for the "grid" icon.
|
||||
///
|
||||
/// Defaults to [ColorTheme.textHighEmphasis].
|
||||
final Color? gridIconButtonColor;
|
||||
|
||||
/// The color to use behind the bottom sheet.
|
||||
///
|
||||
/// Defaults to [ColorTheme.overlay].
|
||||
final Color? bottomSheetBarrierColor;
|
||||
|
||||
/// The background color to use for the bottom sheet.
|
||||
///
|
||||
/// Defaults to [ColorTheme.barsBg].
|
||||
final Color? bottomSheetBackgroundColor;
|
||||
|
||||
/// The [TextStyle] to use for the "photos" text in the bottom sheet.
|
||||
///
|
||||
/// Defaults to [TextTheme.headlineBold].
|
||||
final TextStyle? bottomSheetPhotosTextStyle;
|
||||
|
||||
/// The color to use for the "close" icon.
|
||||
///
|
||||
/// Defaults to [ColorTheme.textHighEmphasis].
|
||||
final Color? bottomSheetCloseIconColor;
|
||||
|
||||
/// Copies this [GalleryFooterThemeData] to another.
|
||||
GalleryFooterThemeData copyWith({
|
||||
Color? backgroundColor,
|
||||
Color? shareIconColor,
|
||||
TextStyle? titleTextStyle,
|
||||
Color? gridIconButtonColor,
|
||||
Color? bottomSheetBarrierColor,
|
||||
Color? bottomSheetBackgroundColor,
|
||||
TextStyle? bottomSheetPhotosTextStyle,
|
||||
Color? bottomSheetCloseIconColor,
|
||||
}) =>
|
||||
GalleryFooterThemeData(
|
||||
backgroundColor: backgroundColor ?? this.backgroundColor,
|
||||
shareIconColor: shareIconColor ?? this.shareIconColor,
|
||||
titleTextStyle: titleTextStyle ?? this.titleTextStyle,
|
||||
gridIconButtonColor: gridIconButtonColor ?? this.gridIconButtonColor,
|
||||
bottomSheetBarrierColor:
|
||||
bottomSheetBarrierColor ?? this.bottomSheetBarrierColor,
|
||||
bottomSheetBackgroundColor:
|
||||
bottomSheetBackgroundColor ?? this.bottomSheetBackgroundColor,
|
||||
bottomSheetPhotosTextStyle:
|
||||
bottomSheetPhotosTextStyle ?? this.bottomSheetPhotosTextStyle,
|
||||
bottomSheetCloseIconColor:
|
||||
bottomSheetCloseIconColor ?? this.bottomSheetCloseIconColor,
|
||||
);
|
||||
|
||||
/// Linearly interpolate between two [GalleryFooter] themes.
|
||||
///
|
||||
/// All the properties must be non-null.
|
||||
GalleryFooterThemeData lerp(
|
||||
GalleryFooterThemeData a,
|
||||
GalleryFooterThemeData b,
|
||||
double t,
|
||||
) =>
|
||||
GalleryFooterThemeData(
|
||||
backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t),
|
||||
shareIconColor: Color.lerp(a.shareIconColor, b.shareIconColor, t),
|
||||
titleTextStyle: TextStyle.lerp(a.titleTextStyle, b.titleTextStyle, t),
|
||||
gridIconButtonColor:
|
||||
Color.lerp(a.gridIconButtonColor, b.gridIconButtonColor, t),
|
||||
bottomSheetBarrierColor:
|
||||
Color.lerp(a.bottomSheetBarrierColor, b.bottomSheetBarrierColor, t),
|
||||
bottomSheetBackgroundColor: Color.lerp(
|
||||
a.bottomSheetBackgroundColor, b.bottomSheetBackgroundColor, t),
|
||||
bottomSheetPhotosTextStyle: TextStyle.lerp(
|
||||
a.bottomSheetPhotosTextStyle, b.bottomSheetPhotosTextStyle, t),
|
||||
bottomSheetCloseIconColor: Color.lerp(
|
||||
a.bottomSheetCloseIconColor, b.bottomSheetCloseIconColor, t),
|
||||
);
|
||||
|
||||
/// Merges one [GalleryFooterThemeData] with the another
|
||||
GalleryFooterThemeData merge(GalleryFooterThemeData? other) {
|
||||
if (other == null) return this;
|
||||
return copyWith(
|
||||
backgroundColor: other.backgroundColor,
|
||||
bottomSheetBarrierColor: other.bottomSheetBarrierColor,
|
||||
bottomSheetBackgroundColor: other.bottomSheetBackgroundColor,
|
||||
bottomSheetCloseIconColor: other.bottomSheetCloseIconColor,
|
||||
bottomSheetPhotosTextStyle: other.bottomSheetPhotosTextStyle,
|
||||
gridIconButtonColor: other.gridIconButtonColor,
|
||||
titleTextStyle: other.titleTextStyle,
|
||||
shareIconColor: other.shareIconColor,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is GalleryFooterThemeData &&
|
||||
runtimeType == other.runtimeType &&
|
||||
backgroundColor == other.backgroundColor &&
|
||||
shareIconColor == other.shareIconColor &&
|
||||
titleTextStyle == other.titleTextStyle &&
|
||||
gridIconButtonColor == other.gridIconButtonColor &&
|
||||
bottomSheetBarrierColor == other.bottomSheetBarrierColor &&
|
||||
bottomSheetBackgroundColor == other.bottomSheetBackgroundColor &&
|
||||
bottomSheetPhotosTextStyle == other.bottomSheetPhotosTextStyle &&
|
||||
bottomSheetCloseIconColor == other.bottomSheetCloseIconColor;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
backgroundColor.hashCode ^
|
||||
shareIconColor.hashCode ^
|
||||
titleTextStyle.hashCode ^
|
||||
gridIconButtonColor.hashCode ^
|
||||
bottomSheetBarrierColor.hashCode ^
|
||||
bottomSheetBackgroundColor.hashCode ^
|
||||
bottomSheetPhotosTextStyle.hashCode ^
|
||||
bottomSheetCloseIconColor.hashCode;
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties
|
||||
..add(ColorProperty('backgroundColor', backgroundColor))
|
||||
..add(ColorProperty('shareIconColor', shareIconColor))
|
||||
..add(DiagnosticsProperty('titleTextStyle', titleTextStyle))
|
||||
..add(ColorProperty('gridIconButtonColor', gridIconButtonColor))
|
||||
..add(ColorProperty('bottomSheetBarrierColor', bottomSheetBarrierColor))
|
||||
..add(ColorProperty(
|
||||
'bottomSheetBackgroundColor', bottomSheetBackgroundColor))
|
||||
..add(DiagnosticsProperty(
|
||||
'bottomSheetPhotosTextStyle', bottomSheetPhotosTextStyle))
|
||||
..add(ColorProperty(
|
||||
'bottomSheetCloseIconColor', bottomSheetCloseIconColor));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ export 'src/connection_status_builder.dart';
|
||||
export 'src/date_divider.dart';
|
||||
export 'src/deleted_message.dart';
|
||||
export 'src/full_screen_media.dart';
|
||||
export 'src/image_footer.dart';
|
||||
export 'src/image_header.dart';
|
||||
export 'src/gallery_footer.dart';
|
||||
export 'src/gallery_header.dart';
|
||||
export 'src/info_tile.dart';
|
||||
export 'src/mention_tile.dart';
|
||||
export 'src/message_action.dart';
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
import 'package:flutter/material.dart' hide TextTheme;
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockStreamChatClient extends Mock implements StreamChatClient {}
|
||||
|
||||
void main() {
|
||||
test('GalleryFooterThemeData copyWith, ==, hashCode basics', () {
|
||||
expect(const GalleryFooterThemeData(),
|
||||
const GalleryFooterThemeData().copyWith());
|
||||
expect(const GalleryFooterThemeData().hashCode,
|
||||
const GalleryFooterThemeData().copyWith().hashCode);
|
||||
});
|
||||
|
||||
test(
|
||||
'Light GalleryFooterThemeData lerps completely to dark GalleryFooterThemeData',
|
||||
() {
|
||||
expect(
|
||||
const GalleryFooterThemeData().lerp(_galleryFooterThemeDataControl,
|
||||
_galleryFooterThemeDataControlDark, 1),
|
||||
_galleryFooterThemeDataControlDark);
|
||||
});
|
||||
|
||||
test(
|
||||
'Light GalleryFooterThemeData lerps halfway to dark GalleryFooterThemeData',
|
||||
() {
|
||||
expect(
|
||||
const GalleryFooterThemeData().lerp(_galleryFooterThemeDataControl,
|
||||
_galleryFooterThemeDataControlDark, 0.5),
|
||||
_galleryFooterThemeDataControlMidLerp);
|
||||
});
|
||||
|
||||
test(
|
||||
'Dark GalleryFooterThemeData lerps completely to light GalleryFooterThemeData',
|
||||
() {
|
||||
expect(
|
||||
const GalleryFooterThemeData().lerp(_galleryFooterThemeDataControlDark,
|
||||
_galleryFooterThemeDataControl, 1),
|
||||
_galleryFooterThemeDataControl);
|
||||
});
|
||||
|
||||
test('Merging dark and light themes results in a dark theme', () {
|
||||
expect(
|
||||
_galleryFooterThemeDataControl
|
||||
.merge(_galleryFooterThemeDataControlDark),
|
||||
_galleryFooterThemeDataControlDark);
|
||||
});
|
||||
|
||||
test('Merging dark and light themes results in a dark theme', () {
|
||||
expect(
|
||||
_galleryFooterThemeDataControlDark
|
||||
.merge(_galleryFooterThemeDataControl),
|
||||
_galleryFooterThemeDataControl);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'Passing no GalleryFooterThemeData returns default light theme values',
|
||||
(WidgetTester tester) async {
|
||||
late BuildContext _context;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
builder: (context, child) => StreamChat(
|
||||
client: MockStreamChatClient(),
|
||||
child: child,
|
||||
),
|
||||
home: Builder(
|
||||
builder: (context) {
|
||||
_context = context;
|
||||
return Scaffold(
|
||||
appBar: GalleryFooter(
|
||||
message: Message(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final imageFooterTheme = GalleryFooterTheme.of(_context);
|
||||
expect(imageFooterTheme.backgroundColor,
|
||||
_galleryFooterThemeDataControl.backgroundColor);
|
||||
expect(imageFooterTheme.shareIconColor,
|
||||
_galleryFooterThemeDataControl.shareIconColor);
|
||||
expect(imageFooterTheme.titleTextStyle,
|
||||
_galleryFooterThemeDataControl.titleTextStyle);
|
||||
expect(imageFooterTheme.gridIconButtonColor,
|
||||
_galleryFooterThemeDataControl.gridIconButtonColor);
|
||||
expect(imageFooterTheme.bottomSheetBarrierColor,
|
||||
_galleryFooterThemeDataControl.bottomSheetBarrierColor);
|
||||
expect(imageFooterTheme.bottomSheetBackgroundColor,
|
||||
_galleryFooterThemeDataControl.bottomSheetBackgroundColor);
|
||||
expect(imageFooterTheme.bottomSheetCloseIconColor,
|
||||
_galleryFooterThemeDataControl.bottomSheetCloseIconColor);
|
||||
expect(imageFooterTheme.bottomSheetPhotosTextStyle,
|
||||
_galleryFooterThemeDataControl.bottomSheetPhotosTextStyle);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'Passing no GalleryFooterThemeData returns default dark theme values',
|
||||
(WidgetTester tester) async {
|
||||
late BuildContext _context;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
builder: (context, child) => StreamChat(
|
||||
client: MockStreamChatClient(),
|
||||
streamChatThemeData: StreamChatThemeData.dark(),
|
||||
child: child,
|
||||
),
|
||||
home: Builder(
|
||||
builder: (context) {
|
||||
_context = context;
|
||||
return Scaffold(
|
||||
appBar: GalleryFooter(
|
||||
message: Message(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final imageFooterTheme = GalleryFooterTheme.of(_context);
|
||||
expect(imageFooterTheme.backgroundColor,
|
||||
_galleryFooterThemeDataControlDark.backgroundColor);
|
||||
expect(imageFooterTheme.shareIconColor,
|
||||
_galleryFooterThemeDataControlDark.shareIconColor);
|
||||
expect(imageFooterTheme.titleTextStyle,
|
||||
_galleryFooterThemeDataControlDark.titleTextStyle);
|
||||
expect(imageFooterTheme.gridIconButtonColor,
|
||||
_galleryFooterThemeDataControlDark.gridIconButtonColor);
|
||||
expect(imageFooterTheme.bottomSheetBarrierColor,
|
||||
_galleryFooterThemeDataControlDark.bottomSheetBarrierColor);
|
||||
expect(imageFooterTheme.bottomSheetBackgroundColor,
|
||||
_galleryFooterThemeDataControlDark.bottomSheetBackgroundColor);
|
||||
expect(imageFooterTheme.bottomSheetCloseIconColor,
|
||||
_galleryFooterThemeDataControlDark.bottomSheetCloseIconColor);
|
||||
expect(imageFooterTheme.bottomSheetPhotosTextStyle,
|
||||
_galleryFooterThemeDataControlDark.bottomSheetPhotosTextStyle);
|
||||
});
|
||||
}
|
||||
|
||||
// Light theme control
|
||||
final _galleryFooterThemeDataControl = GalleryFooterThemeData(
|
||||
backgroundColor: ColorTheme.light().barsBg,
|
||||
shareIconColor: ColorTheme.light().textHighEmphasis,
|
||||
titleTextStyle: TextTheme.light().headlineBold,
|
||||
gridIconButtonColor: ColorTheme.light().textHighEmphasis,
|
||||
bottomSheetBackgroundColor: ColorTheme.light().barsBg,
|
||||
bottomSheetBarrierColor: ColorTheme.light().overlay,
|
||||
bottomSheetCloseIconColor: ColorTheme.light().textHighEmphasis,
|
||||
bottomSheetPhotosTextStyle: TextTheme.light().headlineBold,
|
||||
);
|
||||
|
||||
// Mid-lerp theme control
|
||||
const _galleryFooterThemeDataControlMidLerp = GalleryFooterThemeData(
|
||||
backgroundColor: Color(0xff87898b),
|
||||
shareIconColor: Color(0xff7f7f7f),
|
||||
titleTextStyle: TextStyle(
|
||||
color: Color(0xff7f7f7f),
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
gridIconButtonColor: Color(0xff7f7f7f),
|
||||
bottomSheetBarrierColor: Color(0x4c000000),
|
||||
bottomSheetBackgroundColor: Color(0xff87898b),
|
||||
bottomSheetPhotosTextStyle: TextStyle(
|
||||
color: Color(0xff7f7f7f),
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
bottomSheetCloseIconColor: Color(0xff7f7f7f),
|
||||
);
|
||||
|
||||
// Dark theme control
|
||||
final _galleryFooterThemeDataControlDark = GalleryFooterThemeData(
|
||||
backgroundColor: ColorTheme.dark().barsBg,
|
||||
shareIconColor: ColorTheme.dark().textHighEmphasis,
|
||||
titleTextStyle: TextTheme.dark().headlineBold,
|
||||
gridIconButtonColor: ColorTheme.dark().textHighEmphasis,
|
||||
bottomSheetBackgroundColor: ColorTheme.dark().barsBg,
|
||||
bottomSheetBarrierColor: ColorTheme.dark().overlay,
|
||||
bottomSheetCloseIconColor: ColorTheme.dark().textHighEmphasis,
|
||||
bottomSheetPhotosTextStyle: TextTheme.dark().headlineBold,
|
||||
);
|
||||
@@ -0,0 +1,190 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockStreamChatClient extends Mock implements StreamChatClient {}
|
||||
|
||||
void main() {
|
||||
test('GalleryHeaderThemeData copyWith, ==, hashCode basics', () {
|
||||
expect(const GalleryHeaderThemeData(),
|
||||
const GalleryHeaderThemeData().copyWith());
|
||||
expect(const GalleryHeaderThemeData().hashCode,
|
||||
const GalleryHeaderThemeData().copyWith().hashCode);
|
||||
});
|
||||
|
||||
test(
|
||||
'Light GalleryHeaderThemeData lerps completely to dark GalleryHeaderThemeData',
|
||||
() {
|
||||
expect(
|
||||
const GalleryHeaderThemeData().lerp(_galleryHeaderThemeDataControl,
|
||||
_galleryHeaderThemeDataDarkControl, 1),
|
||||
_galleryHeaderThemeDataDarkControl);
|
||||
});
|
||||
|
||||
test(
|
||||
'Light GalleryHeaderThemeData lerps halfway to dark GalleryHeaderThemeData',
|
||||
() {
|
||||
expect(
|
||||
const GalleryHeaderThemeData().lerp(_galleryHeaderThemeDataControl,
|
||||
_galleryHeaderThemeDataDarkControl, 0.5),
|
||||
_galleryHeaderThemeDataHalfLerpControl);
|
||||
});
|
||||
|
||||
test(
|
||||
'Dark GalleryHeaderThemeData lerps completely to light GalleryHeaderThemeData',
|
||||
() {
|
||||
expect(
|
||||
const GalleryHeaderThemeData().lerp(_galleryHeaderThemeDataDarkControl,
|
||||
_galleryHeaderThemeDataControl, 1),
|
||||
_galleryHeaderThemeDataControl);
|
||||
});
|
||||
|
||||
test('Merging dark and light themes results in a dark theme', () {
|
||||
expect(
|
||||
_galleryHeaderThemeDataControl
|
||||
.merge(_galleryHeaderThemeDataDarkControl),
|
||||
_galleryHeaderThemeDataDarkControl);
|
||||
});
|
||||
|
||||
test('Merging dark and light themes results in a dark theme', () {
|
||||
expect(
|
||||
_galleryHeaderThemeDataDarkControl
|
||||
.merge(_galleryHeaderThemeDataControl),
|
||||
_galleryHeaderThemeDataControl);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'Passing no GalleryHeaderThemeData returns default light theme values',
|
||||
(WidgetTester tester) async {
|
||||
late BuildContext _context;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
builder: (context, child) => StreamChat(
|
||||
client: MockStreamChatClient(),
|
||||
child: child,
|
||||
),
|
||||
home: Builder(
|
||||
builder: (context) {
|
||||
_context = context;
|
||||
return Scaffold(
|
||||
appBar: GalleryHeader(
|
||||
message: Message(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final imageHeaderTheme = GalleryHeaderTheme.of(_context);
|
||||
expect(imageHeaderTheme.closeButtonColor,
|
||||
_galleryHeaderThemeDataControl.closeButtonColor);
|
||||
expect(imageHeaderTheme.backgroundColor,
|
||||
_galleryHeaderThemeDataControl.backgroundColor);
|
||||
expect(imageHeaderTheme.iconMenuPointColor,
|
||||
_galleryHeaderThemeDataControl.iconMenuPointColor);
|
||||
expect(imageHeaderTheme.titleTextStyle,
|
||||
_galleryHeaderThemeDataControl.titleTextStyle);
|
||||
expect(imageHeaderTheme.subtitleTextStyle,
|
||||
_galleryHeaderThemeDataControl.subtitleTextStyle);
|
||||
expect(imageHeaderTheme.bottomSheetBarrierColor,
|
||||
_galleryHeaderThemeDataControl.bottomSheetBarrierColor);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'Passing no GalleryHeaderThemeData returns default dark theme values',
|
||||
(WidgetTester tester) async {
|
||||
late BuildContext _context;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
builder: (context, child) => StreamChat(
|
||||
client: MockStreamChatClient(),
|
||||
streamChatThemeData: StreamChatThemeData.dark(),
|
||||
child: child,
|
||||
),
|
||||
home: Builder(
|
||||
builder: (context) {
|
||||
_context = context;
|
||||
return Scaffold(
|
||||
appBar: GalleryHeader(
|
||||
message: Message(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final imageHeaderTheme = GalleryHeaderTheme.of(_context);
|
||||
expect(imageHeaderTheme.closeButtonColor,
|
||||
_galleryHeaderThemeDataDarkControl.closeButtonColor);
|
||||
expect(imageHeaderTheme.backgroundColor,
|
||||
_galleryHeaderThemeDataDarkControl.backgroundColor);
|
||||
expect(imageHeaderTheme.iconMenuPointColor,
|
||||
_galleryHeaderThemeDataDarkControl.iconMenuPointColor);
|
||||
expect(imageHeaderTheme.titleTextStyle,
|
||||
_galleryHeaderThemeDataDarkControl.titleTextStyle);
|
||||
expect(imageHeaderTheme.subtitleTextStyle,
|
||||
_galleryHeaderThemeDataDarkControl.subtitleTextStyle);
|
||||
expect(imageHeaderTheme.bottomSheetBarrierColor,
|
||||
_galleryHeaderThemeDataDarkControl.bottomSheetBarrierColor);
|
||||
});
|
||||
}
|
||||
|
||||
// Light theme test control.
|
||||
final _galleryHeaderThemeDataControl = GalleryHeaderThemeData(
|
||||
closeButtonColor: const Color(0xff000000),
|
||||
backgroundColor: const Color(0xffffffff),
|
||||
iconMenuPointColor: const Color(0xff000000),
|
||||
titleTextStyle: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
subtitleTextStyle: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.black,
|
||||
).copyWith(
|
||||
color: const Color(0xff7A7A7A),
|
||||
),
|
||||
bottomSheetBarrierColor: const Color.fromRGBO(0, 0, 0, 0.2),
|
||||
);
|
||||
|
||||
// Light theme test control.
|
||||
final _galleryHeaderThemeDataHalfLerpControl = GalleryHeaderThemeData(
|
||||
closeButtonColor: const Color(0xff7f7f7f),
|
||||
backgroundColor: const Color(0xff87898b),
|
||||
iconMenuPointColor: const Color(0xff7f7f7f),
|
||||
titleTextStyle: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xff7f7f7f),
|
||||
),
|
||||
subtitleTextStyle: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Color(0xff7a7a7a),
|
||||
).copyWith(
|
||||
color: const Color(0xff7A7A7A),
|
||||
),
|
||||
bottomSheetBarrierColor: const Color(0x4c000000),
|
||||
);
|
||||
|
||||
// Dark theme test control.
|
||||
final _galleryHeaderThemeDataDarkControl = GalleryHeaderThemeData(
|
||||
closeButtonColor: const Color(0xffffffff),
|
||||
backgroundColor: const Color(0xff101418),
|
||||
iconMenuPointColor: const Color(0xffffffff),
|
||||
titleTextStyle: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
subtitleTextStyle: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.white,
|
||||
).copyWith(
|
||||
color: const Color(0xff7A7A7A),
|
||||
),
|
||||
bottomSheetBarrierColor: const Color.fromRGBO(0, 0, 0, 0.4),
|
||||
);
|
||||
@@ -37,7 +37,7 @@ void main() {
|
||||
child: WillPopScope(
|
||||
onWillPop: () async => false,
|
||||
child: Scaffold(
|
||||
body: ImageFooter(
|
||||
body: GalleryFooter(
|
||||
message: Message(),
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user