Merge pull request #938 from S-ecki/develop
feat(ui): Add Channel Header customization
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
## Upcoming
|
## Upcoming
|
||||||
|
|
||||||
|
✅ Added
|
||||||
|
|
||||||
|
- `centerTitle` and `elevation` properties to `ChannelHeader`, `ThreadHeader` and `ChannelListHeader`.
|
||||||
|
|
||||||
🐞 Fixed
|
🐞 Fixed
|
||||||
|
|
||||||
- [[#1067]](https://github.com/GetStream/stream-chat-flutter/issues/1067): Fix name text overflow in reaction card.
|
- [[#1067]](https://github.com/GetStream/stream-chat-flutter/issues/1067): Fix name text overflow in reaction card.
|
||||||
|
|||||||
@@ -61,9 +61,11 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
this.showConnectionStateTile = false,
|
this.showConnectionStateTile = false,
|
||||||
this.title,
|
this.title,
|
||||||
this.subtitle,
|
this.subtitle,
|
||||||
|
this.centerTitle,
|
||||||
this.leading,
|
this.leading,
|
||||||
this.actions,
|
this.actions,
|
||||||
this.backgroundColor,
|
this.backgroundColor,
|
||||||
|
this.elevation = 1,
|
||||||
}) : preferredSize = const Size.fromHeight(kToolbarHeight),
|
}) : preferredSize = const Size.fromHeight(kToolbarHeight),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
@@ -92,6 +94,9 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
/// Subtitle widget
|
/// Subtitle widget
|
||||||
final Widget? subtitle;
|
final Widget? subtitle;
|
||||||
|
|
||||||
|
/// Whether the title should be centered
|
||||||
|
final bool? centerTitle;
|
||||||
|
|
||||||
/// Leading widget
|
/// Leading widget
|
||||||
final Widget? leading;
|
final Widget? leading;
|
||||||
|
|
||||||
@@ -102,8 +107,16 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
/// The background color for this [ChannelHeader].
|
/// The background color for this [ChannelHeader].
|
||||||
final Color? backgroundColor;
|
final Color? backgroundColor;
|
||||||
|
|
||||||
|
/// The elevation for this [ChannelHeader].
|
||||||
|
final double elevation;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final effectiveCenterTitle = getEffectiveCenterTitle(
|
||||||
|
Theme.of(context),
|
||||||
|
actions: actions,
|
||||||
|
centerTitle: centerTitle,
|
||||||
|
);
|
||||||
final channel = StreamChannel.of(context).channel;
|
final channel = StreamChannel.of(context).channel;
|
||||||
final channelHeaderTheme = ChannelHeaderTheme.of(context);
|
final channelHeaderTheme = ChannelHeaderTheme.of(context);
|
||||||
|
|
||||||
@@ -144,7 +157,7 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
systemOverlayStyle: theme.brightness == Brightness.dark
|
systemOverlayStyle: theme.brightness == Brightness.dark
|
||||||
? SystemUiOverlayStyle.light
|
? SystemUiOverlayStyle.light
|
||||||
: SystemUiOverlayStyle.dark,
|
: SystemUiOverlayStyle.dark,
|
||||||
elevation: 1,
|
elevation: elevation,
|
||||||
leading: leadingWidget,
|
leading: leadingWidget,
|
||||||
backgroundColor: backgroundColor ?? channelHeaderTheme.color,
|
backgroundColor: backgroundColor ?? channelHeaderTheme.color,
|
||||||
actions: actions ??
|
actions: actions ??
|
||||||
@@ -162,14 +175,16 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
centerTitle: true,
|
centerTitle: centerTitle,
|
||||||
title: InkWell(
|
title: InkWell(
|
||||||
onTap: onTitleTap,
|
onTap: onTitleTap,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: preferredSize.height,
|
height: preferredSize.height,
|
||||||
width: preferredSize.width,
|
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: effectiveCenterTitle
|
||||||
|
? CrossAxisAlignment.center
|
||||||
|
: CrossAxisAlignment.stretch,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
title ??
|
title ??
|
||||||
ChannelName(
|
ChannelName(
|
||||||
|
|||||||
@@ -56,9 +56,11 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
this.showConnectionStateTile = false,
|
this.showConnectionStateTile = false,
|
||||||
this.preNavigationCallback,
|
this.preNavigationCallback,
|
||||||
this.subtitle,
|
this.subtitle,
|
||||||
|
this.centerTitle,
|
||||||
this.leading,
|
this.leading,
|
||||||
this.actions,
|
this.actions,
|
||||||
this.backgroundColor,
|
this.backgroundColor,
|
||||||
|
this.elevation = 1,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// Pass this if you don't have a [StreamChatClient] in your widget tree.
|
/// Pass this if you don't have a [StreamChatClient] in your widget tree.
|
||||||
@@ -83,6 +85,9 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
/// Subtitle widget
|
/// Subtitle widget
|
||||||
final Widget? subtitle;
|
final Widget? subtitle;
|
||||||
|
|
||||||
|
/// Whether the title should be centered
|
||||||
|
final bool? centerTitle;
|
||||||
|
|
||||||
/// Leading widget
|
/// Leading widget
|
||||||
/// By default it shows the logged in user avatar
|
/// By default it shows the logged in user avatar
|
||||||
final Widget? leading;
|
final Widget? leading;
|
||||||
@@ -94,6 +99,9 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
/// The background color for this [ChannelListHeader].
|
/// The background color for this [ChannelListHeader].
|
||||||
final Color? backgroundColor;
|
final Color? backgroundColor;
|
||||||
|
|
||||||
|
/// The elevation for this [ChannelListHeader].
|
||||||
|
final double elevation;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final _client = client ?? StreamChat.of(context).client;
|
final _client = client ?? StreamChat.of(context).client;
|
||||||
@@ -128,10 +136,10 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
systemOverlayStyle: theme.brightness == Brightness.dark
|
systemOverlayStyle: theme.brightness == Brightness.dark
|
||||||
? SystemUiOverlayStyle.light
|
? SystemUiOverlayStyle.light
|
||||||
: SystemUiOverlayStyle.dark,
|
: SystemUiOverlayStyle.dark,
|
||||||
elevation: 1,
|
elevation: elevation,
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
backgroundColor ?? channelListHeaderThemeData.color,
|
backgroundColor ?? channelListHeaderThemeData.color,
|
||||||
centerTitle: true,
|
centerTitle: centerTitle,
|
||||||
leading: leading ??
|
leading: leading ??
|
||||||
Center(
|
Center(
|
||||||
child: user != null
|
child: user != null
|
||||||
|
|||||||
@@ -528,6 +528,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
return ((index + 2) * 2) - 1;
|
return ((index + 2) * 2) - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Item Count -> 8 (1 parent, 2 header+footer, 2 top+bottom, 3 messages)
|
// Item Count -> 8 (1 parent, 2 header+footer, 2 top+bottom, 3 messages)
|
||||||
|
|||||||
@@ -65,11 +65,13 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
this.onBackPressed,
|
this.onBackPressed,
|
||||||
this.title,
|
this.title,
|
||||||
this.subtitle,
|
this.subtitle,
|
||||||
|
this.centerTitle,
|
||||||
this.leading,
|
this.leading,
|
||||||
this.actions,
|
this.actions,
|
||||||
this.onTitleTap,
|
this.onTitleTap,
|
||||||
this.showTypingIndicator = true,
|
this.showTypingIndicator = true,
|
||||||
this.backgroundColor,
|
this.backgroundColor,
|
||||||
|
this.elevation = 1,
|
||||||
}) : preferredSize = const Size.fromHeight(kToolbarHeight),
|
}) : preferredSize = const Size.fromHeight(kToolbarHeight),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
@@ -92,6 +94,9 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
/// Subtitle widget
|
/// Subtitle widget
|
||||||
final Widget? subtitle;
|
final Widget? subtitle;
|
||||||
|
|
||||||
|
/// Whether the title should be centered
|
||||||
|
final bool? centerTitle;
|
||||||
|
|
||||||
/// Leading widget
|
/// Leading widget
|
||||||
final Widget? leading;
|
final Widget? leading;
|
||||||
|
|
||||||
@@ -105,8 +110,17 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
/// The background color of this [ThreadHeader].
|
/// The background color of this [ThreadHeader].
|
||||||
final Color? backgroundColor;
|
final Color? backgroundColor;
|
||||||
|
|
||||||
|
/// The elevation for this [ThreadHeader].
|
||||||
|
final double elevation;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final effectiveCenterTitle = getEffectiveCenterTitle(
|
||||||
|
Theme.of(context),
|
||||||
|
actions: actions,
|
||||||
|
centerTitle: centerTitle,
|
||||||
|
);
|
||||||
|
|
||||||
final channelHeaderTheme = ChannelHeaderTheme.of(context);
|
final channelHeaderTheme = ChannelHeaderTheme.of(context);
|
||||||
|
|
||||||
final defaultSubtitle = subtitle ??
|
final defaultSubtitle = subtitle ??
|
||||||
@@ -134,7 +148,7 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
systemOverlayStyle: theme.brightness == Brightness.dark
|
systemOverlayStyle: theme.brightness == Brightness.dark
|
||||||
? SystemUiOverlayStyle.light
|
? SystemUiOverlayStyle.light
|
||||||
: SystemUiOverlayStyle.dark,
|
: SystemUiOverlayStyle.dark,
|
||||||
elevation: 1,
|
elevation: elevation,
|
||||||
leading: leading ??
|
leading: leading ??
|
||||||
(showBackButton
|
(showBackButton
|
||||||
? StreamBackButton(
|
? StreamBackButton(
|
||||||
@@ -144,7 +158,7 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
)
|
)
|
||||||
: const SizedBox()),
|
: const SizedBox()),
|
||||||
backgroundColor: backgroundColor ?? channelHeaderTheme.color,
|
backgroundColor: backgroundColor ?? channelHeaderTheme.color,
|
||||||
centerTitle: true,
|
centerTitle: centerTitle,
|
||||||
actions: actions,
|
actions: actions,
|
||||||
title: InkWell(
|
title: InkWell(
|
||||||
onTap: onTitleTap,
|
onTap: onTitleTap,
|
||||||
@@ -153,6 +167,9 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
width: 250,
|
width: 250,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: effectiveCenterTitle
|
||||||
|
? CrossAxisAlignment.center
|
||||||
|
: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
title ??
|
title ??
|
||||||
Text(
|
Text(
|
||||||
|
|||||||
@@ -47,6 +47,12 @@ class TypingIndicator extends StatelessWidget {
|
|||||||
.where((element) => element.value.parentId == parentId)
|
.where((element) => element.value.parentId == parentId)
|
||||||
.map((e) => e.key)),
|
.map((e) => e.key)),
|
||||||
builder: (context, data) => AnimatedSwitcher(
|
builder: (context, data) => AnimatedSwitcher(
|
||||||
|
layoutBuilder: (currentChild, previousChildren) => Stack(
|
||||||
|
children: <Widget>[
|
||||||
|
...previousChildren,
|
||||||
|
if (currentChild != null) currentChild,
|
||||||
|
],
|
||||||
|
),
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
child: data.isNotEmpty
|
child: data.isNotEmpty
|
||||||
? Padding(
|
? Padding(
|
||||||
|
|||||||
@@ -17,6 +17,28 @@ Future<void> launchURL(BuildContext context, String url) async {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get centerTitle considering a default and platform specific behaviour
|
||||||
|
bool getEffectiveCenterTitle(
|
||||||
|
ThemeData theme, {
|
||||||
|
bool? centerTitle,
|
||||||
|
List<Widget>? actions,
|
||||||
|
}) {
|
||||||
|
if (centerTitle != null) return centerTitle;
|
||||||
|
if (theme.appBarTheme.centerTitle != null) {
|
||||||
|
return theme.appBarTheme.centerTitle!;
|
||||||
|
}
|
||||||
|
switch (theme.platform) {
|
||||||
|
case TargetPlatform.android:
|
||||||
|
case TargetPlatform.fuchsia:
|
||||||
|
case TargetPlatform.linux:
|
||||||
|
case TargetPlatform.windows:
|
||||||
|
return false;
|
||||||
|
case TargetPlatform.iOS:
|
||||||
|
case TargetPlatform.macOS:
|
||||||
|
return actions == null || actions.length < 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Shows confirmation dialog
|
/// Shows confirmation dialog
|
||||||
Future<bool?> showConfirmationDialog(
|
Future<bool?> showConfirmationDialog(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
|
|||||||
@@ -74,6 +74,8 @@ GlobalStreamChatLocalizations? getStreamChatTranslation(Locale locale) {
|
|||||||
return const StreamChatLocalizationsKo();
|
return const StreamChatLocalizationsKo();
|
||||||
case 'pt':
|
case 'pt':
|
||||||
return const StreamChatLocalizationsPt();
|
return const StreamChatLocalizationsPt();
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user