lint changes

This commit is contained in:
Deven Joshi
2021-05-04 17:58:29 +05:30
parent edba3d2ad5
commit 9acf8d1037
13 changed files with 533 additions and 518 deletions
@@ -1,10 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.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/src/unread_indicator.dart'; import 'package:stream_chat_flutter/src/unread_indicator.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import '../stream_chat_flutter.dart'; /// Back button implementation
class StreamBackButton extends StatelessWidget { class StreamBackButton extends StatelessWidget {
/// Constructor for creating back button
const StreamBackButton({ const StreamBackButton({
Key? key, Key? key,
this.onPressed, this.onPressed,
@@ -12,15 +14,17 @@ class StreamBackButton extends StatelessWidget {
this.cid, this.cid,
}) : super(key: key); }) : super(key: key);
/// Callback for when button is pressed
final VoidCallback? onPressed; final VoidCallback? onPressed;
/// Show unread count
final bool showUnreads; final bool showUnreads;
/// Channel cid used to retrieve unread count /// Channel cid used to retrieve unread count
final String? cid; final String? cid;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => Stack(
return Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
RawMaterialButton( RawMaterialButton(
@@ -55,4 +59,3 @@ class StreamBackButton extends StatelessWidget {
], ],
); );
} }
}
@@ -1,13 +1,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/channel_info.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import '../stream_chat_flutter.dart'; /// Bottom Sheet with options
import 'channel_info.dart';
import 'option_list_tile.dart';
class ChannelBottomSheet extends StatefulWidget { class ChannelBottomSheet extends StatefulWidget {
final VoidCallback? onViewInfoTap; /// Constructor for creating bottom sheet
const ChannelBottomSheet({Key? key, this.onViewInfoTap}) : super(key: key);
const ChannelBottomSheet({this.onViewInfoTap}); /// Callback when 'View Info' is tapped
final VoidCallback? onViewInfoTap;
@override @override
_ChannelBottomSheetState createState() => _ChannelBottomSheetState(); _ChannelBottomSheetState createState() => _ChannelBottomSheetState();
@@ -29,7 +30,7 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
return Material( return Material(
color: StreamChatTheme.of(context).colorTheme.white, color: StreamChatTheme.of(context).colorTheme.white,
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
topLeft: Radius.circular(16), topLeft: Radius.circular(16),
topRight: Radius.circular(16), topRight: Radius.circular(16),
@@ -120,7 +121,7 @@ class _ChannelBottomSheetState extends State<ChannelBottomSheet> {
), ),
borderRadius: BorderRadius.circular(32), borderRadius: BorderRadius.circular(32),
onlineIndicatorConstraints: onlineIndicatorConstraints:
BoxConstraints.tight(Size(12, 12)), BoxConstraints.tight(const Size(12, 12)),
), ),
const SizedBox( const SizedBox(
height: 6, height: 6,
@@ -4,13 +4,9 @@ import 'package:stream_chat_flutter/src/channel_info.dart';
import 'package:stream_chat_flutter/src/channel_name.dart'; import 'package:stream_chat_flutter/src/channel_name.dart';
import 'package:stream_chat_flutter/src/info_tile.dart'; import 'package:stream_chat_flutter/src/info_tile.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/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';
import './channel_name.dart';
import '../stream_chat_flutter.dart';
import 'channel_image.dart';
import 'connection_status_builder.dart';
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/channel_header.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/channel_header.png)
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/channel_header_paint.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/channel_header_paint.png)
/// ///
@@ -43,16 +39,36 @@ import 'connection_status_builder.dart';
/// Usually you would use this widget as an [AppBar] inside a [Scaffold]. /// Usually you would use this widget as an [AppBar] inside a [Scaffold].
/// However you can also use it as a normal widget. /// However you can also use it as a normal widget.
/// ///
/// Make sure to have a [StreamChannel] ancestor in order to provide the information about the channel. /// Make sure to have a [StreamChannel] ancestor in order to provide the
/// Every part of the widget uses a [StreamBuilder] to render the channel information as soon as it updates. /// information about the channel.
/// Every part of the widget uses a [StreamBuilder] to render the channel
/// information as soon as it updates.
/// ///
/// By default the widget shows a backButton that calls [Navigator.pop]. /// By default the widget shows a backButton that calls [Navigator.pop].
/// You can disable this button using the [showBackButton] property of just override the behaviour /// You can disable this button using the [showBackButton] property of just
/// override the behaviour
/// with [onBackPressed]. /// with [onBackPressed].
/// ///
/// The widget components render the ui based on the first ancestor of type [StreamChatTheme] and on its [ChannelTheme.channelHeaderTheme] property. /// The widget components render the ui based on the first ancestor of type
/// [StreamChatTheme] and on its [ChannelTheme.channelHeaderTheme] property.
/// Modify it to change the widget appearance. /// Modify it to change the widget appearance.
class ChannelHeader extends StatelessWidget implements PreferredSizeWidget { class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
/// Creates a channel header
const ChannelHeader({
Key? key,
this.showBackButton = true,
this.onBackPressed,
this.onTitleTap,
this.showTypingIndicator = true,
this.onImageTap,
this.showConnectionStateTile = false,
this.title,
this.subtitle,
this.leading,
this.actions,
}) : 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;
@@ -69,6 +85,7 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
/// If true the typing indicator will be rendered if a user is typing /// If true the typing indicator will be rendered if a user is typing
final bool showTypingIndicator; final bool showTypingIndicator;
/// Show connection tile on header
final bool showConnectionStateTile; final bool showConnectionStateTile;
/// Title widget /// Title widget
@@ -84,22 +101,6 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
/// By default it shows the [ChannelImage] /// By default it shows the [ChannelImage]
final List<Widget>? actions; final List<Widget>? actions;
/// Creates a channel header
ChannelHeader({
Key? key,
this.showBackButton = true,
this.onBackPressed,
this.onTitleTap,
this.showTypingIndicator = true,
this.onImageTap,
this.showConnectionStateTile = false,
this.title,
this.subtitle,
this.leading,
this.actions,
}) : preferredSize = Size.fromHeight(kToolbarHeight),
super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final channel = StreamChannel.of(context).channel; final channel = StreamChannel.of(context).channel;
@@ -110,7 +111,7 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
onPressed: onBackPressed, onPressed: onBackPressed,
showUnreads: true, showUnreads: true,
) )
: SizedBox()); : const SizedBox());
return ConnectionStatusBuilder( return ConnectionStatusBuilder(
statusBuilder: (context, status) { statusBuilder: (context, status) {
@@ -131,6 +132,7 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
} }
return InfoTile( return InfoTile(
// ignore: avoid_bool_literals_in_conditional_expressions
showMessage: showConnectionStateTile ? showStatus : false, showMessage: showConnectionStateTile ? showStatus : false,
message: statusString, message: statusString,
child: AppBar( child: AppBar(
@@ -165,7 +167,7 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
centerTitle: true, centerTitle: true,
title: InkWell( title: InkWell(
onTap: onTitleTap, onTap: onTitleTap,
child: Container( child: SizedBox(
height: preferredSize.height, height: preferredSize.height,
width: preferredSize.width, width: preferredSize.width,
child: Column( child: Column(
@@ -178,7 +180,7 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
.channelHeaderTheme .channelHeaderTheme
.title, .title,
), ),
SizedBox(height: 2), const SizedBox(height: 2),
subtitle ?? subtitle ??
ChannelInfo( ChannelInfo(
showTypingIndicator: showTypingIndicator, showTypingIndicator: showTypingIndicator,
@@ -36,12 +36,14 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// } /// }
/// ``` /// ```
/// ///
/// The widget uses a [StreamBuilder] to render the channel information image as soon as it updates. /// The widget uses a [StreamBuilder] to render the channel information
/// image as soon as it updates.
/// ///
/// By default the widget radius size is 40x40 pixels. /// By default the widget radius size is 40x40 pixels.
/// Set the property [constraints] to set a custom dimension. /// Set the property [constraints] to set a custom dimension.
/// ///
/// The widget renders the ui based on the first ancestor of type [StreamChatTheme]. /// The widget renders the ui based on the first ancestor of type
/// [StreamChatTheme].
/// Modify it to change the widget appearance. /// Modify it to change the widget appearance.
class ChannelImage extends StatelessWidget { class ChannelImage extends StatelessWidget {
/// Instantiate a new ChannelImage /// Instantiate a new ChannelImage
@@ -56,6 +58,7 @@ class ChannelImage extends StatelessWidget {
this.selectionThickness = 4, this.selectionThickness = 4,
}) : super(key: key); }) : super(key: key);
/// [BorderRadius] to display the widget
final BorderRadius? borderRadius; final BorderRadius? borderRadius;
/// The channel to show the image of /// The channel to show the image of
@@ -67,10 +70,13 @@ class ChannelImage extends StatelessWidget {
/// The function called when the image is tapped /// The function called when the image is tapped
final VoidCallback? onTap; final VoidCallback? onTap;
/// If image is selected
final bool selected; final bool selected;
/// Selection color for image
final Color? selectionColor; final Color? selectionColor;
/// Thickness of selection image
final double selectionThickness; final double selectionThickness;
@override @override
@@ -91,8 +97,7 @@ class ChannelImage extends StatelessWidget {
stream: streamChat.client.state.usersStream.map( stream: streamChat.client.state.usersStream.map(
(users) => users[otherMember?.userId] ?? otherMember!.user!), (users) => users[otherMember?.userId] ?? otherMember!.user!),
initialData: otherMember!.user, initialData: otherMember!.user,
builder: (context, snapshot) { builder: (context, snapshot) => UserAvatar(
return UserAvatar(
borderRadius: borderRadius ?? borderRadius: borderRadius ??
StreamChatTheme.of(context) StreamChatTheme.of(context)
.channelPreviewTheme .channelPreviewTheme
@@ -109,14 +114,14 @@ class ChannelImage extends StatelessWidget {
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) =>
member.user?.id != streamChat.user?.id && member.user?.id != streamChat.user?.id &&
member.user?.extraData['image'] != null) member.user?.extraData['image'] != null)
.take(4) .take(4)
// ignore: cast_nullable_to_non_nullable
.map((e) => e.user?.extraData['image'] as String) .map((e) => e.user?.extraData['image'] as String)
.toList(); .toList();
return GroupImage( return GroupImage(
@@ -161,8 +166,7 @@ class ChannelImage extends StatelessWidget {
if (image != null) if (image != null)
CachedNetworkImage( CachedNetworkImage(
imageUrl: image, imageUrl: image,
errorWidget: (_, __, ___) { errorWidget: (_, __, ___) => Center(
return Center(
child: Text( child: Text(
snapshot.data?.containsKey('name') ?? false snapshot.data?.containsKey('name') ?? false
? snapshot.data!['name'][0] ? snapshot.data!['name'][0]
@@ -172,8 +176,7 @@ class ChannelImage extends StatelessWidget {
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
); ),
},
fit: BoxFit.cover, fit: BoxFit.cover,
) )
else else
@@ -6,6 +6,14 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'connection_status_builder.dart'; import 'connection_status_builder.dart';
class ChannelInfo extends StatelessWidget { class ChannelInfo extends StatelessWidget {
const ChannelInfo({
Key? key,
required this.channel,
this.textStyle,
this.showTypingIndicator = true,
}) : super(key: key);
/// The channel about which the info is to be displayed
final Channel channel; final Channel channel;
/// The style of the text displayed /// The style of the text displayed
@@ -14,13 +22,6 @@ class ChannelInfo extends StatelessWidget {
/// If true the typing indicator will be rendered if a user is typing /// If true the typing indicator will be rendered if a user is typing
final bool showTypingIndicator; final bool showTypingIndicator;
const ChannelInfo({
Key? key,
required this.channel,
this.textStyle,
this.showTypingIndicator = true,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final client = StreamChat.of(context).client; final client = StreamChat.of(context).client;
@@ -93,10 +94,10 @@ class ChannelInfo extends StatelessWidget {
Widget _buildConnectingTitleState(BuildContext context) => Row( Widget _buildConnectingTitleState(BuildContext context) => Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Container( const SizedBox(
height: 16, height: 16,
width: 16, width: 16,
child: const Center( child: Center(
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
), ),
), ),
@@ -6,10 +6,7 @@ import 'package:stream_chat_flutter/src/stream_neumorphic_button.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';
import 'connection_status_builder.dart'; /// Widget builder for title
import 'info_tile.dart';
import 'stream_chat.dart';
typedef TitleBuilder = Widget Function( typedef TitleBuilder = Widget Function(
BuildContext context, BuildContext context,
ConnectionStatus status, ConnectionStatus status,
@@ -42,10 +39,13 @@ typedef TitleBuilder = Widget Function(
/// Usually you would use this widget as an [AppBar] inside a [Scaffold]. /// Usually you would use this widget as an [AppBar] inside a [Scaffold].
/// However you can also use it as a normal widget. /// However you can also use it as a normal widget.
/// ///
/// The widget by default uses the inherited [StreamChatClient] to fetch information about the status. /// The widget by default uses the inherited [StreamChatClient]
/// However you can also pass your own [StreamChatClient] if you don't have it in the widget tree. /// to fetch information about the status.
/// However you can also pass your own [StreamChatClient]
/// if you don't have it in the widget tree.
/// ///
/// The widget components render the ui based on the first ancestor of type [StreamChatTheme] and on its [ChannelListHeaderTheme] property. /// The widget components render the ui based on the first ancestor of type
/// [StreamChatTheme] and on its [ChannelListHeaderTheme] property.
/// Modify it to change the widget appearance. /// Modify it to change the widget appearance.
class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
/// Instantiates a ChannelListHeader /// Instantiates a ChannelListHeader
@@ -75,8 +75,10 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
/// Callback to call when pressing the new chat button. /// Callback to call when pressing the new chat button.
final VoidCallback? onNewChatButtonTap; final VoidCallback? onNewChatButtonTap;
/// Show connection state tile
final bool showConnectionStateTile; final bool showConnectionStateTile;
/// Callback before navigation is performed
final VoidCallback? preNavigationCallback; final VoidCallback? preNavigationCallback;
/// Subtitle widget /// Subtitle widget
@@ -113,6 +115,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
} }
return InfoTile( return InfoTile(
// ignore: avoid_bool_literals_in_conditional_expressions
showMessage: showConnectionStateTile ? showStatus : false, showMessage: showConnectionStateTile ? showStatus : false,
message: statusString, message: statusString,
child: AppBar( child: AppBar(
@@ -143,7 +146,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
.avatarTheme .avatarTheme
?.constraints, ?.constraints,
) )
: Offstage(), : const Offstage(),
), ),
actions: actions ?? actions: actions ??
[ [
@@ -151,7 +154,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
child: IconButton( child: IconButton(
icon: ConnectionStatusBuilder( icon: ConnectionStatusBuilder(
statusBuilder: (context, status) { statusBuilder: (context, status) {
var color; Color? color;
switch (status) { switch (status) {
case ConnectionStatus.connected: case ConnectionStatus.connected:
color = StreamChatTheme.of(context) color = StreamChatTheme.of(context)
@@ -193,11 +196,11 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
case ConnectionStatus.disconnected: case ConnectionStatus.disconnected:
return _buildDisconnectedTitleState(context, _client); return _buildDisconnectedTitleState(context, _client);
default: default:
return Offstage(); return const Offstage();
} }
}, },
), ),
subtitle ?? Offstage(), subtitle ?? const Offstage(),
], ],
), ),
), ),
@@ -213,18 +216,17 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
), ),
); );
Widget _buildConnectingTitleState(BuildContext context) { Widget _buildConnectingTitleState(BuildContext context) => Row(
return Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Container( const SizedBox(
height: 16, height: 16,
width: 16, width: 16,
child: Center( child: Center(
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
), ),
), ),
SizedBox(width: 10), const SizedBox(width: 10),
Text( Text(
'Searching for Network', 'Searching for Network',
style: StreamChatTheme.of(context) style: StreamChatTheme.of(context)
@@ -237,13 +239,11 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
), ),
], ],
); );
}
Widget _buildDisconnectedTitleState( Widget _buildDisconnectedTitleState(
BuildContext context, BuildContext context,
StreamChatClient client, StreamChatClient client,
) { ) => Row(
return Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text( Text(
@@ -275,8 +275,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget {
), ),
], ],
); );
}
@override @override
Size get preferredSize => Size.fromHeight(kToolbarHeight); Size get preferredSize => const Size.fromHeight(kToolbarHeight);
} }
@@ -3,13 +3,12 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:shimmer/shimmer.dart'; import 'package:shimmer/shimmer.dart';
import 'package:stream_chat_flutter/src/channel_bottom_sheet.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/src/utils.dart'; import 'package:stream_chat_flutter/src/utils.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';
import '../stream_chat_flutter.dart';
import 'channel_bottom_sheet.dart';
import 'channel_preview.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?);
@@ -17,6 +16,7 @@ typedef ChannelTapCallback = void Function(Channel, Widget?);
/// Builder used to create a custom [ChannelPreview] from a [Channel] /// Builder used to create a custom [ChannelPreview] from a [Channel]
typedef ChannelPreviewBuilder = Widget Function(BuildContext, Channel); typedef ChannelPreviewBuilder = Widget Function(BuildContext, Channel);
/// Callback for when 'View Info' is tapped
typedef ViewInfoCallback = void Function(Channel); typedef ViewInfoCallback = void Function(Channel);
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/channel_list_view.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/channel_list_view.png)
@@ -47,10 +47,12 @@ typedef ViewInfoCallback = void Function(Channel);
/// ``` /// ```
/// ///
/// ///
/// Make sure to have a [StreamChat] ancestor in order to provide the information about the channels. /// Make sure to have a [StreamChat] ancestor in order to provide the
/// information about the channels.
/// The widget uses a [ListView.custom] to render the list of channels. /// The widget uses a [ListView.custom] to render the list of channels.
/// ///
/// The widget components render the ui based on the first ancestor of type [StreamChatTheme]. /// The widget components render the ui based on the first ancestor of
/// type [StreamChatTheme].
/// Modify it to change the widget appearance. /// Modify it to change the widget appearance.
class ChannelListView extends StatefulWidget { class ChannelListView extends StatefulWidget {
/// Instantiate a new ChannelListView /// Instantiate a new ChannelListView
@@ -94,8 +96,10 @@ class ChannelListView extends StatefulWidget {
final Map<String, dynamic>? options; final Map<String, dynamic>? options;
/// The sorting used for the channels matching the filters. /// The sorting used for the channels matching the filters.
/// Sorting is based on field and direction, multiple sorting options can be provided. /// Sorting is based on field and direction, multiple sorting options
/// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count. /// can be provided.
/// You can sort based on last_updated, last_message_at, updated_at,
/// created_at or member_count.
/// Direction can be ascending or descending. /// Direction can be ascending or descending.
final List<SortOption<ChannelModel>>? sort; final List<SortOption<ChannelModel>>? sort;
@@ -137,8 +141,10 @@ class ChannelListView extends StatefulWidget {
/// The amount of space by which to inset the children. /// The amount of space by which to inset the children.
final EdgeInsetsGeometry? padding; final EdgeInsetsGeometry? padding;
/// List of selected channels which are displayed differently
final List<Channel> selectedChannels; final List<Channel> selectedChannels;
/// Callback for when 'View Info' is tapped
final ViewInfoCallback? onViewInfoTap; final ViewInfoCallback? onViewInfoTap;
/// The builder that will be used in case of error /// The builder that will be used in case of error
@@ -203,15 +209,14 @@ class _ChannelListViewState extends State<ChannelListView> {
crossAxisCount: widget.crossAxisCount, crossAxisCount: widget.crossAxisCount,
), ),
itemCount: channels.length, itemCount: channels.length,
physics: AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
itemBuilder: (context, index) { itemBuilder: (context, index) =>
return _gridItemBuilder(context, index, channels); _gridItemBuilder(context, index, channels),
},
); );
} else { } else {
child = ListView.separated( child = ListView.separated(
padding: widget.padding, padding: widget.padding,
physics: AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
itemCount: itemCount:
channels.isNotEmpty ? channels.length + 1 : channels.length, channels.isNotEmpty ? channels.length + 1 : channels.length,
separatorBuilder: (_, index) { separatorBuilder: (_, index) {
@@ -220,9 +225,8 @@ class _ChannelListViewState extends State<ChannelListView> {
} }
return _separatorBuilder(context, index); return _separatorBuilder(context, index);
}, },
itemBuilder: (context, index) { itemBuilder: (context, index) =>
return _listItemBuilder(context, index, channels); _listItemBuilder(context, index, channels),
},
); );
} }
} }
@@ -233,11 +237,9 @@ class _ChannelListViewState extends State<ChannelListView> {
); );
} }
Widget _buildEmptyWidget(BuildContext context) { Widget _buildEmptyWidget(BuildContext context) => LayoutBuilder(
return LayoutBuilder( builder: (context, viewportConstraints) => SingleChildScrollView(
builder: (context, viewportConstraints) { physics: const AlwaysScrollableScrollPhysics(),
return SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: Stack( child: Stack(
children: [ children: [
ConstrainedBox( ConstrainedBox(
@@ -307,15 +309,12 @@ class _ChannelListViewState extends State<ChannelListView> {
), ),
], ],
), ),
),
); );
},
);
}
Widget _buildLoadingWidget(BuildContext context) { Widget _buildLoadingWidget(BuildContext context) => ListView(
return ListView(
padding: widget.padding, padding: widget.padding,
physics: AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
children: List.generate( children: List.generate(
25, 25,
(i) { (i) {
@@ -331,7 +330,6 @@ class _ChannelListViewState extends State<ChannelListView> {
}, },
), ),
); );
}
Shimmer _buildLoadingItem(BuildContext context) { Shimmer _buildLoadingItem(BuildContext context) {
if (widget.crossAxisCount > 1) { if (widget.crossAxisCount > 1) {
@@ -340,24 +338,24 @@ class _ChannelListViewState extends State<ChannelListView> {
highlightColor: StreamChatTheme.of(context).colorTheme.whiteSmoke, highlightColor: StreamChatTheme.of(context).colorTheme.whiteSmoke,
child: Column( child: Column(
children: [ children: [
SizedBox(height: 4), const SizedBox(height: 4),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
for (int i = 0; i < widget.crossAxisCount; i++) for (int i = 0; i < widget.crossAxisCount; i++)
Container( Container(
decoration: BoxDecoration( decoration: const BoxDecoration(
color: Colors.white, color: Colors.white,
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
constraints: BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
height: 70, height: 70,
width: 70, width: 70,
), ),
), ),
], ],
), ),
SizedBox( const SizedBox(
height: 16, height: 16,
), ),
], ],
@@ -373,7 +371,7 @@ class _ChannelListViewState extends State<ChannelListView> {
color: StreamChatTheme.of(context).colorTheme.white, color: StreamChatTheme.of(context).colorTheme.white,
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
constraints: BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
height: 40, height: 40,
width: 40, width: 40,
), ),
@@ -389,7 +387,7 @@ class _ChannelListViewState extends State<ChannelListView> {
color: StreamChatTheme.of(context).colorTheme.white, color: StreamChatTheme.of(context).colorTheme.white,
borderRadius: BorderRadius.circular(11), borderRadius: BorderRadius.circular(11),
), ),
constraints: BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
height: 16, height: 16,
width: 82, width: 82,
), ),
@@ -406,7 +404,7 @@ class _ChannelListViewState extends State<ChannelListView> {
color: StreamChatTheme.of(context).colorTheme.white, color: StreamChatTheme.of(context).colorTheme.white,
borderRadius: BorderRadius.circular(11), borderRadius: BorderRadius.circular(11),
), ),
constraints: BoxConstraints.expand( constraints: const BoxConstraints.expand(
height: 16, height: 16,
), ),
), ),
@@ -418,7 +416,7 @@ class _ChannelListViewState extends State<ChannelListView> {
color: StreamChatTheme.of(context).colorTheme.white, color: StreamChatTheme.of(context).colorTheme.white,
borderRadius: BorderRadius.circular(11), borderRadius: BorderRadius.circular(11),
), ),
constraints: BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
height: 16, height: 16,
width: 42, width: 42,
), ),
@@ -430,17 +428,16 @@ class _ChannelListViewState extends State<ChannelListView> {
} }
} }
Widget _buildErrorWidget(BuildContext context, Object error) { Widget _buildErrorWidget(BuildContext context, Object error) => Center(
return Center(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Text.rich( Text.rich(
TextSpan( const TextSpan(
children: [ children: [
WidgetSpan( WidgetSpan(
child: Padding( child: Padding(
padding: const EdgeInsets.only( padding: EdgeInsets.only(
right: 2, right: 2,
), ),
child: Icon(Icons.error_outline), child: Icon(Icons.error_outline),
@@ -453,12 +450,11 @@ class _ChannelListViewState extends State<ChannelListView> {
), ),
TextButton( TextButton(
onPressed: () => _channelListController.loadData!(), onPressed: () => _channelListController.loadData!(),
child: Text('Retry'), child: const Text('Retry'),
), ),
], ],
), ),
); );
}
Widget _listItemBuilder(BuildContext context, int i, List<Channel> channels) { Widget _listItemBuilder(BuildContext context, int i, List<Channel> channels) {
final channelsProvider = ChannelsBloc.of(context); final channelsProvider = ChannelsBloc.of(context);
@@ -471,11 +467,10 @@ class _ChannelListViewState extends State<ChannelListView> {
key: ValueKey<String>('CHANNEL-${channel.id}'), key: ValueKey<String>('CHANNEL-${channel.id}'),
channel: channel, channel: channel,
child: Builder( child: Builder(
builder: (context) { builder: (context) => Slidable(
return Slidable(
controller: _slideController, controller: _slideController,
enabled: widget.swipeToAction, enabled: widget.swipeToAction,
actionPane: SlidableBehindActionPane(), actionPane: const SlidableBehindActionPane(),
actionExtentRatio: 0.12, actionExtentRatio: 0.12,
secondaryActions: <Widget>[ secondaryActions: <Widget>[
IconSlideAction( IconSlideAction(
@@ -484,23 +479,21 @@ class _ChannelListViewState extends State<ChannelListView> {
onTap: () { onTap: () {
showModalBottomSheet( showModalBottomSheet(
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
shape: RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
topLeft: Radius.circular(32), topLeft: Radius.circular(32),
topRight: Radius.circular(32), topRight: Radius.circular(32),
), ),
), ),
context: context, context: context,
builder: (context) { builder: (context) => StreamChannel(
return StreamChannel(
channel: channel, channel: channel,
child: ChannelBottomSheet( child: ChannelBottomSheet(
onViewInfoTap: () { onViewInfoTap: () {
widget.onViewInfoTap?.call(channel); widget.onViewInfoTap?.call(channel);
}, },
), ),
); ),
},
); );
}, },
), ),
@@ -525,8 +518,7 @@ class _ChannelListViewState extends State<ChannelListView> {
'Are you sure you want to delete this conversation?', 'Are you sure you want to delete this conversation?',
cancelText: 'CANCEL', cancelText: 'CANCEL',
icon: StreamSvgIcon.delete( icon: StreamSvgIcon.delete(
color: color: StreamChatTheme.of(context).colorTheme.accentRed,
StreamChatTheme.of(context).colorTheme.accentRed,
), ),
); );
if (res == true) { if (res == true) {
@@ -545,8 +537,7 @@ class _ChannelListViewState extends State<ChannelListView> {
onTap: (channel) => onTap(channel, widget.channelWidget), onTap: (channel) => onTap(channel, widget.channelWidget),
), ),
), ),
); ),
},
), ),
); );
} else { } else {
@@ -566,12 +557,10 @@ class _ChannelListViewState extends State<ChannelListView> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) { builder: (context) => StreamChannel(
return StreamChannel(
channel: client, channel: client,
child: widget.channelWidget!, child: widget.channelWidget!,
); ),
},
), ),
); );
}; };
@@ -593,18 +582,18 @@ class _ChannelListViewState extends State<ChannelListView> {
channel: channel, channel: channel,
borderRadius: BorderRadius.circular(32), borderRadius: BorderRadius.circular(32),
selected: selected, selected: selected,
constraints: BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
width: 64, width: 64,
height: 64, height: 64,
), ),
onTap: () => _getChannelTap(context), onTap: () => _getChannelTap(context),
), ),
SizedBox(height: 7), const SizedBox(height: 7),
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 8), padding: const EdgeInsets.symmetric(horizontal: 8),
child: StreamChannel( child: StreamChannel(
channel: channel, channel: channel,
child: ChannelName( child: const ChannelName(
textStyle: TextStyle( textStyle: TextStyle(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@@ -620,8 +609,8 @@ class _ChannelListViewState extends State<ChannelListView> {
Widget _buildQueryProgressIndicator( Widget _buildQueryProgressIndicator(
context, context,
ChannelsBlocState channelsProvider, ChannelsBlocState channelsProvider,
) { ) =>
return StreamBuilder<bool>( StreamBuilder<bool>(
stream: channelsProvider.queryChannelsLoading, stream: channelsProvider.queryChannelsLoading,
initialData: false, initialData: false,
builder: (context, snapshot) { builder: (context, snapshot) {
@@ -631,8 +620,8 @@ class _ChannelListViewState extends State<ChannelListView> {
.colorTheme .colorTheme
.accentRed .accentRed
.withOpacity(.2), .withOpacity(.2),
child: Padding( child: const Padding(
padding: const EdgeInsets.symmetric(vertical: 16), padding: EdgeInsets.symmetric(vertical: 16),
child: Center( child: Center(
child: Text('Error loading channels'), child: Text('Error loading channels'),
), ),
@@ -640,15 +629,14 @@ class _ChannelListViewState extends State<ChannelListView> {
); );
} }
return snapshot.data! return snapshot.data!
? Center( ? const Center(
child: Padding( child: Padding(
padding: const EdgeInsets.all(16), padding: EdgeInsets.all(16),
child: const CircularProgressIndicator(), child: CircularProgressIndicator(),
), ),
) )
: Offstage(); : const Offstage();
}); });
}
Widget _separatorBuilder(context, i) { Widget _separatorBuilder(context, i) {
final effect = StreamChatTheme.of(context).colorTheme.borderBottom; final effect = StreamChatTheme.of(context).colorTheme.borderBottom;
@@ -1,12 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.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';
import '../stream_chat_flutter.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 image as soon as it updates. /// The widget uses a [StreamBuilder] to render the channel information
/// image as soon as it updates.
class ChannelName extends StatelessWidget { class ChannelName extends StatelessWidget {
/// Instantiate a new ChannelName /// Instantiate a new ChannelName
const ChannelName({ const ChannelName({
@@ -25,9 +26,8 @@ class ChannelName extends StatelessWidget {
return StreamBuilder<Map<String, dynamic>>( return StreamBuilder<Map<String, dynamic>>(
stream: channel.extraDataStream, stream: channel.extraDataStream,
initialData: channel.extraData, initialData: channel.extraData,
builder: (context, snapshot) { builder: (context, snapshot) =>
return _buildName(snapshot.data!, channel.state?.members, client); _buildName(snapshot.data!, channel.state?.members, client),
},
); );
} }
@@ -35,8 +35,8 @@ class ChannelName extends StatelessWidget {
Map<String, dynamic> extraData, Map<String, dynamic> extraData,
List<Member>? members, List<Member>? members,
StreamChatState client, StreamChatState client,
) { ) =>
return LayoutBuilder( LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
String? title; String? title;
if (extraData['name'] == null) { if (extraData['name'] == null) {
@@ -50,7 +50,8 @@ class ChannelName extends StatelessWidget {
var currentChars = 0; var currentChars = 0;
final currentMembers = <Member>[]; final currentMembers = <Member>[];
otherMembers!.forEach((element) { otherMembers!.forEach((element) {
final newLength = currentChars + (element.user?.name.length ?? 0); final newLength =
currentChars + (element.user?.name.length ?? 0);
if (newLength < maxChars) { if (newLength < maxChars) {
currentChars = newLength; currentChars = newLength;
currentMembers.add(element); currentMembers.add(element);
@@ -60,7 +61,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(', ')} ${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}'; '${currentMembers.map((e) => e.user?.name).join(', ')} '
'${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}';
} else { } else {
title = 'No title'; title = 'No title';
} }
@@ -76,4 +78,3 @@ class ChannelName extends StatelessWidget {
}, },
); );
} }
}
@@ -3,23 +3,39 @@ import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:jiffy/jiffy.dart'; import 'package:jiffy/jiffy.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/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';
import '../stream_chat_flutter.dart';
import 'channel_name.dart';
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/channel_preview.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/channel_preview.png)
/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/channel_preview_paint.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/channel_preview_paint.png)
/// ///
/// It shows the current [Channel] preview. /// It shows the current [Channel] preview.
/// ///
/// The widget uses a [StreamBuilder] to render the channel information image as soon as it updates. /// The widget uses a [StreamBuilder] to render the channel information
/// image as soon as it updates.
/// ///
/// Usually you don't use this widget as it's the default channel preview used by [ChannelListView]. /// Usually you don't use this widget as it's the default channel preview
/// used by [ChannelListView].
/// ///
/// The widget renders the ui based on the first ancestor of type [StreamChatTheme]. /// The widget renders the ui based on the first ancestor of type
/// [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]
const ChannelPreview({
required this.channel,
Key? key,
this.onTap,
this.onLongPress,
this.onImageTap,
this.title,
this.subtitle,
this.leading,
this.sendingIndicator,
this.trailing,
}) : super(key: key);
/// Function called when tapping this widget /// Function called when tapping this widget
final void Function(Channel)? onTap; final void Function(Channel)? onTap;
@@ -38,36 +54,25 @@ class ChannelPreview extends StatelessWidget {
/// Widget rendering the subtitle /// Widget rendering the subtitle
final Widget? subtitle; final Widget? subtitle;
/// Widget rendering the leading element, by default it shows the [ChannelImage] /// Widget rendering the leading element, by default
/// it shows the [ChannelImage]
final Widget? leading; final Widget? leading;
/// Widget rendering the trailing element, by default it shows the last message date /// Widget rendering the trailing element,
/// by default it shows the last message date
final Widget? trailing; final Widget? trailing;
/// Widget rendering the sending indicator, by default it uses the [SendingIndicator] widget /// Widget rendering the sending indicator,
/// by default it uses the [SendingIndicator] widget
final Widget? sendingIndicator; final Widget? sendingIndicator;
const ChannelPreview({
required this.channel,
Key? key,
this.onTap,
this.onLongPress,
this.onImageTap,
this.title,
this.subtitle,
this.leading,
this.sendingIndicator,
this.trailing,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final channelPreviewTheme = StreamChatTheme.of(context).channelPreviewTheme; final channelPreviewTheme = StreamChatTheme.of(context).channelPreviewTheme;
return StreamBuilder<bool>( return StreamBuilder<bool>(
stream: channel.isMutedStream, stream: channel.isMutedStream,
initialData: channel.isMuted, initialData: channel.isMuted,
builder: (context, snapshot) { builder: (context, snapshot) => Opacity(
return Opacity(
opacity: snapshot.data! ? 0.5 : 1, opacity: snapshot.data! ? 0.5 : 1,
child: ListTile( child: ListTile(
visualDensity: VisualDensity.compact, visualDensity: VisualDensity.compact,
@@ -105,7 +110,7 @@ class ChannelPreview extends StatelessWidget {
snapshot.data!.isEmpty || snapshot.data!.isEmpty ||
!snapshot.data!.any((Member e) => !snapshot.data!.any((Member e) =>
e.user!.id == channel.client.state.user?.id)) { e.user!.id == channel.client.state.user?.id)) {
return SizedBox(); return const SizedBox();
} }
return UnreadIndicator( return UnreadIndicator(
cid: channel.cid, cid: channel.cid,
@@ -143,24 +148,22 @@ class ChannelPreview extends StatelessWidget {
), ),
); );
} }
return SizedBox(); return const SizedBox();
}, },
), ),
trailing ?? _buildDate(context), trailing ?? _buildDate(context),
], ],
), ),
), ),
); ));
});
} }
Widget _buildDate(BuildContext context) { Widget _buildDate(BuildContext context) => StreamBuilder<DateTime?>(
return StreamBuilder<DateTime?>(
stream: channel.lastMessageAtStream, stream: channel.lastMessageAtStream,
initialData: channel.lastMessageAt, initialData: channel.lastMessageAt,
builder: (context, snapshot) { builder: (context, snapshot) {
if (!snapshot.hasData) { if (!snapshot.hasData) {
return SizedBox(); return const SizedBox();
} }
final lastMessageAt = snapshot.data!.toLocal(); final lastMessageAt = snapshot.data!.toLocal();
@@ -173,7 +176,9 @@ class ChannelPreview extends StatelessWidget {
startOfDay.millisecondsSinceEpoch) { startOfDay.millisecondsSinceEpoch) {
stringDate = Jiffy(lastMessageAt.toLocal()).jm; stringDate = Jiffy(lastMessageAt.toLocal()).jm;
} else if (lastMessageAt.millisecondsSinceEpoch >= } else if (lastMessageAt.millisecondsSinceEpoch >=
startOfDay.subtract(Duration(days: 1)).millisecondsSinceEpoch) { startOfDay
.subtract(const Duration(days: 1))
.millisecondsSinceEpoch) {
stringDate = 'Yesterday'; stringDate = 'Yesterday';
} else if (startOfDay.difference(lastMessageAt).inDays < 7) { } else if (startOfDay.difference(lastMessageAt).inDays < 7) {
stringDate = Jiffy(lastMessageAt.toLocal()).EEEE; stringDate = Jiffy(lastMessageAt.toLocal()).EEEE;
@@ -183,11 +188,11 @@ class ChannelPreview extends StatelessWidget {
return Text( return Text(
stringDate, stringDate,
style: StreamChatTheme.of(context).channelPreviewTheme.lastMessageAt, style:
StreamChatTheme.of(context).channelPreviewTheme.lastMessageAt,
); );
}, },
); );
}
Widget _buildSubtitle(BuildContext context) { Widget _buildSubtitle(BuildContext context) {
if (channel.isMuted) { if (channel.isMuted) {
@@ -211,15 +216,15 @@ class ChannelPreview extends StatelessWidget {
); );
} }
Widget _buildLastMessage(BuildContext context) { Widget _buildLastMessage(BuildContext context) =>
return StreamBuilder<List<Message>?>( StreamBuilder<List<Message>?>(
stream: channel.state!.messagesStream, stream: channel.state!.messagesStream,
initialData: channel.state!.messages, initialData: channel.state!.messages,
builder: (context, snapshot) { builder: (context, snapshot) {
final lastMessage = snapshot.data final lastMessage = snapshot.data
?.lastWhereOrNull((m) => m.shadowed != true && !m.isDeleted); ?.lastWhereOrNull((m) => m.shadowed != true && !m.isDeleted);
if (lastMessage == null) { if (lastMessage == null) {
return SizedBox(); return const SizedBox();
} }
var text = lastMessage.text; var text = lastMessage.text;
@@ -246,7 +251,10 @@ class ChannelPreview extends StatelessWidget {
text, text,
lastMessage.mentionedUsers, lastMessage.mentionedUsers,
lastMessage.attachments, lastMessage.attachments,
StreamChatTheme.of(context).channelPreviewTheme.subtitle?.copyWith( StreamChatTheme.of(context)
.channelPreviewTheme
.subtitle
?.copyWith(
color: StreamChatTheme.of(context) color: StreamChatTheme.of(context)
.channelPreviewTheme .channelPreviewTheme
.subtitle .subtitle
@@ -254,7 +262,10 @@ class ChannelPreview extends StatelessWidget {
fontStyle: (lastMessage.isSystem || lastMessage.isDeleted) fontStyle: (lastMessage.isSystem || lastMessage.isDeleted)
? FontStyle.italic ? FontStyle.italic
: FontStyle.normal), : FontStyle.normal),
StreamChatTheme.of(context).channelPreviewTheme.subtitle?.copyWith( StreamChatTheme.of(context)
.channelPreviewTheme
.subtitle
?.copyWith(
color: StreamChatTheme.of(context) color: StreamChatTheme.of(context)
.channelPreviewTheme .channelPreviewTheme
.subtitle .subtitle
@@ -270,7 +281,6 @@ class ChannelPreview extends StatelessWidget {
); );
}, },
); );
}
TextSpan _getDisplayText( TextSpan _getDisplayText(
String text, String text,
@@ -1,13 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.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';
import 'stream_chat.dart';
/// Widget that builds itself based on the latest snapshot of interaction with /// Widget that builds itself based on the latest snapshot of interaction with
/// a [Stream] of type [ConnectionStatus]. /// a [Stream] of type [ConnectionStatus].
/// ///
/// The widget will use the closest [StreamChatClient.wsConnectionStatusStream] in case no /// The widget will use the closest [StreamChatClient.wsConnectionStatusStream]
/// stream is provided. /// in case no stream is provided.
class ConnectionStatusBuilder extends StatelessWidget { class ConnectionStatusBuilder extends StatelessWidget {
/// Creates a new ConnectionStatusBuilder /// Creates a new ConnectionStatusBuilder
const ConnectionStatusBuilder({ const ConnectionStatusBuilder({
@@ -47,11 +46,11 @@ class ConnectionStatusBuilder extends StatelessWidget {
if (errorBuilder != null) { if (errorBuilder != null) {
return errorBuilder!(context, snapshot.error); return errorBuilder!(context, snapshot.error);
} }
return Offstage(); return const Offstage();
} }
if (!snapshot.hasData) { if (!snapshot.hasData) {
if (loadingBuilder != null) return loadingBuilder!(context); if (loadingBuilder != null) return loadingBuilder!(context);
return Offstage(); return const Offstage();
} }
return statusBuilder(context, snapshot.data!); return statusBuilder(context, snapshot.data!);
}, },
@@ -4,15 +4,20 @@ 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 {
final DateTime dateTime;
final bool uppercase;
/// Constructor for creating a [DateDivider]
const DateDivider({ const DateDivider({
Key? key, Key? key,
required this.dateTime, required this.dateTime,
this.uppercase = false, this.uppercase = false,
}) : super(key: key); }) : super(key: key);
/// [DateTime] to display
final DateTime dateTime;
/// If text is uppercase
final bool uppercase;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final createdAt = Jiffy(dateTime); final createdAt = Jiffy(dateTime);
@@ -22,10 +27,10 @@ class DateDivider extends StatelessWidget {
if (Jiffy(createdAt).isSame(now, Units.DAY)) { if (Jiffy(createdAt).isSame(now, Units.DAY)) {
dayInfo = 'Today'; dayInfo = 'Today';
} else if (Jiffy(createdAt) } else if (Jiffy(createdAt)
.isSame(now.subtract(Duration(days: 1)), Units.DAY)) { .isSame(now.subtract(const Duration(days: 1)), Units.DAY)) {
dayInfo = 'Yesterday'; dayInfo = 'Yesterday';
} else if (Jiffy(createdAt).isAfter( } else if (Jiffy(createdAt).isAfter(
now.subtract(Duration(days: 7)), now.subtract(const Duration(days: 7)),
Units.DAY, Units.DAY,
)) { )) {
dayInfo = createdAt.EEEE; dayInfo = createdAt.EEEE;
@@ -3,7 +3,10 @@ import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
/// Widget to display deleted message
class DeletedMessage extends StatelessWidget { class DeletedMessage extends StatelessWidget {
/// Constructor to create [DeletedMessage]
const DeletedMessage({ const DeletedMessage({
Key? key, Key? key,
required this.messageTheme, required this.messageTheme,
@@ -29,8 +32,7 @@ class DeletedMessage extends StatelessWidget {
final bool reverse; final bool reverse;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => Transform(
return Transform(
transform: Matrix4.rotationY(reverse ? pi : 0), transform: Matrix4.rotationY(reverse ? pi : 0),
alignment: Alignment.center, alignment: Alignment.center,
child: Material( child: Material(
@@ -71,4 +73,3 @@ class DeletedMessage extends StatelessWidget {
), ),
); );
} }
}
@@ -9,9 +9,7 @@ final _emojis = Emoji.all();
/// String extension /// String extension
extension StringExtension on String { extension StringExtension on String {
/// Returns the capitalized string /// Returns the capitalized string
String capitalize() { String capitalize() => '${this[0].toUpperCase()}${substring(1)}';
return '${this[0].toUpperCase()}${substring(1)}';
}
/// Returns whether the string contains only emoji's or not. /// Returns whether the string contains only emoji's or not.
/// ///
@@ -46,7 +44,9 @@ extension PlatformFileX on PlatformFile {
); );
} }
///
extension InputDecorationX on InputDecoration { extension InputDecorationX on InputDecoration {
///
InputDecoration merge(InputDecoration? other) { InputDecoration merge(InputDecoration? other) {
if (other == null) return this; if (other == null) return this;
return copyWith( return copyWith(
@@ -98,7 +98,9 @@ extension InputDecorationX on InputDecoration {
} }
} }
/// Gets text scale factor through context
extension BuildContextX on BuildContext { extension BuildContextX on BuildContext {
// ignore: public_member_api_docs
double get textScaleFactor => double get textScaleFactor =>
MediaQuery.maybeOf(this)?.textScaleFactor ?? 1.0; MediaQuery.maybeOf(this)?.textScaleFactor ?? 1.0;
} }