refactor(ui): improve channel list and controller
Signed-off-by: xsahil03x <[email protected]>
This commit is contained in:
@@ -1833,6 +1833,14 @@ class ChannelClientState {
|
|||||||
(watchers, users) => watchers!.map((e) => users[e.id] ?? e).toList(),
|
(watchers, users) => watchers!.map((e) => users[e.id] ?? e).toList(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Channel member for the current user.
|
||||||
|
Member? get currentUserMember => members.firstWhereOrNull(
|
||||||
|
(m) => m.user?.id == _channel.client.state.currentUser?.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// User role for the current user.
|
||||||
|
String? get currentUserRole => currentUserMember?.role;
|
||||||
|
|
||||||
/// Channel read list.
|
/// Channel read list.
|
||||||
List<Read> get read => _channelState.read;
|
List<Read> get read => _channelState.read;
|
||||||
|
|
||||||
|
|||||||
+29
@@ -176,6 +176,32 @@ class StreamChannelListController extends PagedValueNotifier<int, Channel> {
|
|||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Leaves the [channel] and updates the list.
|
||||||
|
Future<void> leaveChannel(Channel channel) async {
|
||||||
|
final user = client.state.currentUser;
|
||||||
|
assert(user != null, 'You must be logged in to leave a channel.');
|
||||||
|
await channel.removeMembers([user!.id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deletes the [channel] and updates the list.
|
||||||
|
Future<void> deleteChannel(Channel channel) async {
|
||||||
|
await channel.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mutes the [channel] and updates the list.
|
||||||
|
Future<void> muteChannel(Channel channel) async {
|
||||||
|
await channel.mute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Un-mutes the [channel] and updates the list.
|
||||||
|
Future<void> unmuteChannel(Channel channel) async {
|
||||||
|
await channel.unmute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Event listener, which can be set in order to listen
|
||||||
|
/// [client] web-socket events.
|
||||||
|
bool Function(Event event)? eventListener;
|
||||||
|
|
||||||
StreamSubscription<Event>? _channelEventSubscription;
|
StreamSubscription<Event>? _channelEventSubscription;
|
||||||
|
|
||||||
// Subscribes to the channel list events.
|
// Subscribes to the channel list events.
|
||||||
@@ -185,6 +211,9 @@ class StreamChannelListController extends PagedValueNotifier<int, Channel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_channelEventSubscription = client.on().listen((event) {
|
_channelEventSubscription = client.on().listen((event) {
|
||||||
|
// Returns early if the event is already handled by the listener.
|
||||||
|
if (eventListener?.call(event) ?? false) return;
|
||||||
|
|
||||||
final eventType = event.type;
|
final eventType = event.type;
|
||||||
if (eventType == EventType.channelDeleted) {
|
if (eventType == EventType.channelDeleted) {
|
||||||
_eventHandler.onChannelDeleted(event, this);
|
_eventHandler.onChannelDeleted(event, this);
|
||||||
|
|||||||
+68
-16
@@ -31,10 +31,14 @@ class StreamChannelListTile extends StatelessWidget {
|
|||||||
this.leading,
|
this.leading,
|
||||||
this.title,
|
this.title,
|
||||||
this.subtitle,
|
this.subtitle,
|
||||||
|
this.trailing,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.onLongPress,
|
this.onLongPress,
|
||||||
|
this.tileColor,
|
||||||
this.visualDensity = VisualDensity.compact,
|
this.visualDensity = VisualDensity.compact,
|
||||||
this.contentPadding = const EdgeInsets.symmetric(horizontal: 8),
|
this.contentPadding = const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
this.unreadIndicatorBuilder,
|
||||||
|
this.sendingIndicatorBuilder,
|
||||||
}) : assert(
|
}) : assert(
|
||||||
channel.state != null,
|
channel.state != null,
|
||||||
'Channel ${channel.id} is not initialized',
|
'Channel ${channel.id} is not initialized',
|
||||||
@@ -53,12 +57,23 @@ class StreamChannelListTile extends StatelessWidget {
|
|||||||
/// Additional content displayed below the title.
|
/// Additional content displayed below the title.
|
||||||
final Widget? subtitle;
|
final Widget? subtitle;
|
||||||
|
|
||||||
|
/// A widget to display at the end of tile.
|
||||||
|
final Widget? trailing;
|
||||||
|
|
||||||
/// Called when the user taps this list tile.
|
/// Called when the user taps this list tile.
|
||||||
final GestureTapCallback? onTap;
|
final GestureTapCallback? onTap;
|
||||||
|
|
||||||
/// Called when the user long-presses on this list tile.
|
/// Called when the user long-presses on this list tile.
|
||||||
final GestureLongPressCallback? onLongPress;
|
final GestureLongPressCallback? onLongPress;
|
||||||
|
|
||||||
|
/// {@template flutter.material.ListTile.tileColor}
|
||||||
|
/// Defines the background color of `ListTile` when [selected] is false.
|
||||||
|
///
|
||||||
|
/// When the value is null, the `tileColor` is set to [ListTileTheme.tileColor]
|
||||||
|
/// if it's not null and to [Colors.transparent] if it's null.
|
||||||
|
/// {@endtemplate}
|
||||||
|
final Color? tileColor;
|
||||||
|
|
||||||
/// Defines how compact the list tile's layout will be.
|
/// Defines how compact the list tile's layout will be.
|
||||||
///
|
///
|
||||||
/// {@macro flutter.material.themedata.visualDensity}
|
/// {@macro flutter.material.themedata.visualDensity}
|
||||||
@@ -77,6 +92,40 @@ class StreamChannelListTile extends StatelessWidget {
|
|||||||
/// If null, `EdgeInsets.symmetric(horizontal: 16.0)` is used.
|
/// If null, `EdgeInsets.symmetric(horizontal: 16.0)` is used.
|
||||||
final EdgeInsetsGeometry contentPadding;
|
final EdgeInsetsGeometry contentPadding;
|
||||||
|
|
||||||
|
/// The widget builder for the unread indicator.
|
||||||
|
final WidgetBuilder? unreadIndicatorBuilder;
|
||||||
|
|
||||||
|
/// The widget builder for the sending indicator.
|
||||||
|
///
|
||||||
|
/// `Message` is the last message in the channel, Use it to determine the
|
||||||
|
/// status using [Message.status].
|
||||||
|
final Widget Function(BuildContext, Message)? sendingIndicatorBuilder;
|
||||||
|
|
||||||
|
/// Creates a copy of this tile but with the given fields replaced with
|
||||||
|
/// the new values.
|
||||||
|
StreamChannelListTile copyWith({
|
||||||
|
Key? key,
|
||||||
|
Channel? channel,
|
||||||
|
Widget? leading,
|
||||||
|
Widget? title,
|
||||||
|
Widget? subtitle,
|
||||||
|
VoidCallback? onTap,
|
||||||
|
VoidCallback? onLongPress,
|
||||||
|
VisualDensity? visualDensity,
|
||||||
|
EdgeInsetsGeometry? contentPadding,
|
||||||
|
}) =>
|
||||||
|
StreamChannelListTile(
|
||||||
|
key: key ?? this.key,
|
||||||
|
channel: channel ?? this.channel,
|
||||||
|
leading: leading ?? this.leading,
|
||||||
|
title: title ?? this.title,
|
||||||
|
subtitle: subtitle ?? this.subtitle,
|
||||||
|
onTap: onTap ?? this.onTap,
|
||||||
|
onLongPress: onLongPress ?? this.onLongPress,
|
||||||
|
visualDensity: visualDensity ?? this.visualDensity,
|
||||||
|
contentPadding: contentPadding ?? this.contentPadding,
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final channelState = channel.state!;
|
final channelState = channel.state!;
|
||||||
@@ -101,6 +150,12 @@ class StreamChannelListTile extends StatelessWidget {
|
|||||||
textStyle: channelPreviewTheme.subtitleStyle,
|
textStyle: channelPreviewTheme.subtitleStyle,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final trailing = this.trailing ??
|
||||||
|
ChannelLastMessageDate(
|
||||||
|
channel: channel,
|
||||||
|
textStyle: channelPreviewTheme.lastMessageAtStyle,
|
||||||
|
);
|
||||||
|
|
||||||
return BetterStreamBuilder<bool>(
|
return BetterStreamBuilder<bool>(
|
||||||
stream: channel.isMutedStream,
|
stream: channel.isMutedStream,
|
||||||
initialData: channel.isMuted,
|
initialData: channel.isMuted,
|
||||||
@@ -113,6 +168,7 @@ class StreamChannelListTile extends StatelessWidget {
|
|||||||
visualDensity: visualDensity,
|
visualDensity: visualDensity,
|
||||||
contentPadding: contentPadding,
|
contentPadding: contentPadding,
|
||||||
leading: leading,
|
leading: leading,
|
||||||
|
tileColor: tileColor,
|
||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: title),
|
Expanded(child: title),
|
||||||
@@ -125,7 +181,8 @@ class StreamChannelListTile extends StatelessWidget {
|
|||||||
!members.any((it) => it.user!.id == currentUser.id)) {
|
!members.any((it) => it.user!.id == currentUser.id)) {
|
||||||
return const Offstage();
|
return const Offstage();
|
||||||
}
|
}
|
||||||
return UnreadIndicator(cid: channel.cid);
|
return unreadIndicatorBuilder?.call(context) ??
|
||||||
|
UnreadIndicator(cid: channel.cid);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -154,24 +211,19 @@ class StreamChannelListTile extends StatelessWidget {
|
|||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(right: 4),
|
padding: const EdgeInsets.only(right: 4),
|
||||||
child: SendingIndicator(
|
child:
|
||||||
message: lastMessage,
|
sendingIndicatorBuilder?.call(context, lastMessage) ??
|
||||||
size: channelPreviewTheme.indicatorIconSize,
|
SendingIndicator(
|
||||||
isMessageRead: channelState.read
|
message: lastMessage,
|
||||||
.where((it) => it.user.id != currentUser.id)
|
size: channelPreviewTheme.indicatorIconSize,
|
||||||
.where(
|
isMessageRead: channelState
|
||||||
(it) => it.lastRead.isAfter(lastMessage.createdAt),
|
.currentUserRead!.lastRead
|
||||||
)
|
.isAfter(lastMessage.createdAt),
|
||||||
.isNotEmpty,
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ChannelLastMessageDate(
|
trailing,
|
||||||
channel: channel,
|
|
||||||
textStyle: channelPreviewTheme.lastMessageAtStyle,
|
|
||||||
),
|
|
||||||
// trailing ?? _buildDate(context),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
+73
-63
@@ -19,6 +19,7 @@ Widget defaultSeparatorBuilder(BuildContext context, int index) =>
|
|||||||
typedef StreamChannelListViewItemBuilder = Widget Function(
|
typedef StreamChannelListViewItemBuilder = Widget Function(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
Channel channel,
|
Channel channel,
|
||||||
|
StreamChannelListTile defaultWidget,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// A [ListView] that shows a list of [Channel]s,
|
/// A [ListView] that shows a list of [Channel]s,
|
||||||
@@ -51,6 +52,9 @@ class StreamChannelListView extends StatefulWidget {
|
|||||||
required this.controller,
|
required this.controller,
|
||||||
this.itemBuilder,
|
this.itemBuilder,
|
||||||
this.separatorBuilder = defaultSeparatorBuilder,
|
this.separatorBuilder = defaultSeparatorBuilder,
|
||||||
|
this.emptyBuilder,
|
||||||
|
this.loadingBuilder,
|
||||||
|
this.errorBuilder,
|
||||||
this.onChannelTap,
|
this.onChannelTap,
|
||||||
this.onChannelLongPress,
|
this.onChannelLongPress,
|
||||||
this.padding,
|
this.padding,
|
||||||
@@ -71,13 +75,29 @@ class StreamChannelListView extends StatefulWidget {
|
|||||||
|
|
||||||
/// A builder that is called to build items in the [ListView].
|
/// A builder that is called to build items in the [ListView].
|
||||||
///
|
///
|
||||||
/// The `index` parameter is the index of the list tile in the list and the
|
/// The `channel` parameter is the [Channel] at this position in the list
|
||||||
/// `channel` parameter is the [Channel] at that position.
|
/// and the `defaultWidget` is the default widget used
|
||||||
|
/// i.e: [StreamChannelListTile].
|
||||||
final StreamChannelListViewItemBuilder? itemBuilder;
|
final StreamChannelListViewItemBuilder? itemBuilder;
|
||||||
|
|
||||||
/// A builder that is called to build the list separator.
|
/// A builder that is called to build the list separator.
|
||||||
final IndexedWidgetBuilder separatorBuilder;
|
final IndexedWidgetBuilder separatorBuilder;
|
||||||
|
|
||||||
|
/// A builder that is called to build the empty state of the list.
|
||||||
|
///
|
||||||
|
/// If not provider, [StreamChannelListEmptyWidget] will be used.
|
||||||
|
final WidgetBuilder? emptyBuilder;
|
||||||
|
|
||||||
|
/// A builder that is called to build the loading state of the list.
|
||||||
|
///
|
||||||
|
/// If not provided, [StreamChannelListLoadingTile] will be used.
|
||||||
|
final WidgetBuilder? loadingBuilder;
|
||||||
|
|
||||||
|
/// A builder that is called to build the error state of the list.
|
||||||
|
///
|
||||||
|
/// If not provided, [StreamChannelListErrorWidget] will be used.
|
||||||
|
final Widget Function(BuildContext, StreamChatError)? errorBuilder;
|
||||||
|
|
||||||
/// Called when the user taps this list tile.
|
/// Called when the user taps this list tile.
|
||||||
final void Function(Channel)? onChannelTap;
|
final void Function(Channel)? onChannelTap;
|
||||||
|
|
||||||
@@ -250,12 +270,13 @@ class _StreamChannelListViewState extends State<StreamChannelListView> {
|
|||||||
builder: (context, value, _) => value.when(
|
builder: (context, value, _) => value.when(
|
||||||
(channels, nextPageKey, error) {
|
(channels, nextPageKey, error) {
|
||||||
if (channels.isEmpty) {
|
if (channels.isEmpty) {
|
||||||
return const Center(
|
return widget.emptyBuilder?.call(context) ??
|
||||||
child: Padding(
|
const Center(
|
||||||
padding: EdgeInsets.all(8),
|
child: Padding(
|
||||||
child: StreamChannelListEmpty(),
|
padding: EdgeInsets.all(8),
|
||||||
),
|
child: StreamChannelListEmptyWidget(),
|
||||||
);
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ListView.separated(
|
return ListView.separated(
|
||||||
@@ -290,47 +311,61 @@ class _StreamChannelListViewState extends State<StreamChannelListView> {
|
|||||||
|
|
||||||
if (index == channels.length) {
|
if (index == channels.length) {
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
return ChannelListLoadMoreError(
|
return StreamChannelListLoadMoreError(
|
||||||
onTap: _controller.retry,
|
onTap: _controller.retry,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return const Center(
|
return const Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.all(16),
|
padding: EdgeInsets.all(16),
|
||||||
child: ChannelListLoadMoreIndicator(),
|
child: StreamChannelListLoadMoreIndicator(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final channel = channels[index];
|
final channel = channels[index];
|
||||||
final itemBuilder = widget.itemBuilder;
|
|
||||||
if (itemBuilder != null) return itemBuilder(context, channel);
|
|
||||||
|
|
||||||
final onTap = widget.onChannelTap;
|
final onTap = widget.onChannelTap;
|
||||||
final onLongPress = widget.onChannelLongPress;
|
final onLongPress = widget.onChannelLongPress;
|
||||||
|
|
||||||
return StreamChannelListTile(
|
final streamChannelListTile = StreamChannelListTile(
|
||||||
channel: channel,
|
channel: channel,
|
||||||
onTap: onTap == null ? null : () => onTap(channel),
|
onTap: onTap == null ? null : () => onTap(channel),
|
||||||
onLongPress:
|
onLongPress:
|
||||||
onLongPress == null ? null : () => onLongPress(channel),
|
onLongPress == null ? null : () => onLongPress(channel),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final itemBuilder = widget.itemBuilder;
|
||||||
|
|
||||||
|
if (itemBuilder != null) {
|
||||||
|
return itemBuilder(
|
||||||
|
context,
|
||||||
|
channel,
|
||||||
|
streamChannelListTile,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return streamChannelListTile;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
loading: () => ListView.separated(
|
loading: () =>
|
||||||
padding: widget.padding,
|
widget.loadingBuilder?.call(context) ??
|
||||||
physics: widget.physics,
|
ListView.separated(
|
||||||
reverse: widget.reverse,
|
padding: widget.padding,
|
||||||
itemCount: 25,
|
physics: widget.physics,
|
||||||
separatorBuilder: widget.separatorBuilder,
|
reverse: widget.reverse,
|
||||||
itemBuilder: (_, __) => const StreamChannelListLoadingTile(),
|
itemCount: 25,
|
||||||
),
|
separatorBuilder: widget.separatorBuilder,
|
||||||
error: (error) => Center(
|
itemBuilder: (_, __) => const StreamChannelListLoadingTile(),
|
||||||
child: StreamChannelListError(
|
),
|
||||||
onPressed: _controller.refresh,
|
error: (error) =>
|
||||||
),
|
widget.errorBuilder?.call(context, error) ??
|
||||||
),
|
Center(
|
||||||
|
child: StreamChannelListErrorWidget(
|
||||||
|
onPressed: _controller.refresh,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -338,9 +373,9 @@ class _StreamChannelListViewState extends State<StreamChannelListView> {
|
|||||||
/// A [StreamChannelListTile] that can be used in a [ListView] to show a
|
/// A [StreamChannelListTile] that can be used in a [ListView] to show a
|
||||||
/// loading tile while waiting for the [StreamChannelListController] to load
|
/// loading tile while waiting for the [StreamChannelListController] to load
|
||||||
/// more channels.
|
/// more channels.
|
||||||
class ChannelListLoadMoreIndicator extends StatelessWidget {
|
class StreamChannelListLoadMoreIndicator extends StatelessWidget {
|
||||||
/// Creates a new instance of [ChannelListLoadMoreIndicator].
|
/// Creates a new instance of [StreamChannelListLoadMoreIndicator].
|
||||||
const ChannelListLoadMoreIndicator({Key? key}) : super(key: key);
|
const StreamChannelListLoadMoreIndicator({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => const SizedBox(
|
Widget build(BuildContext context) => const SizedBox(
|
||||||
@@ -352,9 +387,9 @@ class ChannelListLoadMoreIndicator extends StatelessWidget {
|
|||||||
|
|
||||||
/// A [StreamChannelListTile] that is used to display the error indicator when
|
/// A [StreamChannelListTile] that is used to display the error indicator when
|
||||||
/// loading more channels fails.
|
/// loading more channels fails.
|
||||||
class ChannelListLoadMoreError extends StatelessWidget {
|
class StreamChannelListLoadMoreError extends StatelessWidget {
|
||||||
/// Creates a new instance of [ChannelListLoadMoreError].
|
/// Creates a new instance of [StreamChannelListLoadMoreError].
|
||||||
const ChannelListLoadMoreError({
|
const StreamChannelListLoadMoreError({
|
||||||
Key? key,
|
Key? key,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
@@ -407,9 +442,9 @@ class StreamChannelListSeparator extends StatelessWidget {
|
|||||||
|
|
||||||
/// A widget that is used to display an error screen
|
/// A widget that is used to display an error screen
|
||||||
/// when [StreamChannelListController] fails to load initial channels.
|
/// when [StreamChannelListController] fails to load initial channels.
|
||||||
class StreamChannelListError extends StatelessWidget {
|
class StreamChannelListErrorWidget extends StatelessWidget {
|
||||||
/// Creates a new instance of [StreamChannelListError] widget.
|
/// Creates a new instance of [StreamChannelListErrorWidget] widget.
|
||||||
const StreamChannelListError({
|
const StreamChannelListErrorWidget({
|
||||||
Key? key,
|
Key? key,
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
@@ -445,15 +480,9 @@ class StreamChannelListError extends StatelessWidget {
|
|||||||
|
|
||||||
/// A widget that is used to display an empty state when
|
/// A widget that is used to display an empty state when
|
||||||
/// [StreamChannelListController] loads zero channels.
|
/// [StreamChannelListController] loads zero channels.
|
||||||
class StreamChannelListEmpty extends StatelessWidget {
|
class StreamChannelListEmptyWidget extends StatelessWidget {
|
||||||
/// Creates a new instance of [StreamChannelListEmpty] widget.
|
/// Creates a new instance of [StreamChannelListEmptyWidget] widget.
|
||||||
const StreamChannelListEmpty({
|
const StreamChannelListEmptyWidget({Key? key}) : super(key: key);
|
||||||
Key? key,
|
|
||||||
this.onPressed,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
/// The callback to invoke when the user taps on the start a chat button.
|
|
||||||
final VoidCallback? onPressed;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -461,7 +490,6 @@ class StreamChannelListEmpty extends StatelessWidget {
|
|||||||
return Column(
|
return Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const Spacer(),
|
|
||||||
StreamSvgIcon.message(
|
StreamSvgIcon.message(
|
||||||
size: 148,
|
size: 148,
|
||||||
color: chatThemeData.colorTheme.disabled,
|
color: chatThemeData.colorTheme.disabled,
|
||||||
@@ -471,24 +499,6 @@ class StreamChannelListEmpty extends StatelessWidget {
|
|||||||
context.translations.letsStartChattingLabel,
|
context.translations.letsStartChattingLabel,
|
||||||
style: chatThemeData.textTheme.headline,
|
style: chatThemeData.textTheme.headline,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
|
||||||
Text(
|
|
||||||
context.translations.sendingFirstMessageLabel,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: chatThemeData.textTheme.body.copyWith(
|
|
||||||
color: chatThemeData.colorTheme.textLowEmphasis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
TextButton(
|
|
||||||
onPressed: onPressed,
|
|
||||||
child: Text(
|
|
||||||
context.translations.startAChatLabel,
|
|
||||||
style: chatThemeData.textTheme.bodyBold.copyWith(
|
|
||||||
color: chatThemeData.colorTheme.accentPrimary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user