refactor(ui): add stream_channel_info_bottom_sheet.dart
Signed-off-by: xsahil03x <[email protected]>
This commit is contained in:
@@ -62,10 +62,12 @@ abstract class PagedValueNotifier<Key, Value>
|
||||
|
||||
/// Refresh the data presented by this [PagedValueNotifier].
|
||||
///
|
||||
/// Resets the [value] to the initial value in case [resetValue] is true.
|
||||
///
|
||||
/// Note: This API is intended for UI-driven refresh signals,
|
||||
/// such as swipe-to-refresh.
|
||||
Future<void> refresh() {
|
||||
value = _initialValue;
|
||||
Future<void> refresh({bool resetValue = true}) {
|
||||
if (resetValue) value = _initialValue;
|
||||
return doInitialLoad();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,365 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat/stream_chat.dart';
|
||||
import 'package:stream_chat_flutter/src/channel_info.dart';
|
||||
import 'package:stream_chat_flutter/src/extension.dart';
|
||||
import 'package:stream_chat_flutter/src/option_list_tile.dart';
|
||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||
import 'package:stream_chat_flutter/src/theme/themes.dart';
|
||||
import 'package:stream_chat_flutter/src/user_avatar.dart';
|
||||
import 'package:stream_chat_flutter/src/v4/stream_channel_name.dart';
|
||||
|
||||
/// A [BottomSheet] that shows information about a [Channel].
|
||||
class StreamChannelInfoBottomSheet extends StatelessWidget {
|
||||
/// Creates a new instance [StreamChannelInfoBottomSheet] widget.
|
||||
StreamChannelInfoBottomSheet({
|
||||
Key? key,
|
||||
required this.channel,
|
||||
this.onMemberTap,
|
||||
this.onViewInfoTap,
|
||||
this.onLeaveChannelTap,
|
||||
this.onDeleteConversationTap,
|
||||
this.onCancelTap,
|
||||
}) : assert(
|
||||
channel.state != null,
|
||||
'Channel ${channel.id} is not initialized',
|
||||
),
|
||||
super(key: key);
|
||||
|
||||
/// The [Channel] to show information about.
|
||||
final Channel channel;
|
||||
|
||||
/// A callback that is called when a member is tapped.
|
||||
final void Function(Member)? onMemberTap;
|
||||
|
||||
/// A callback that is called when the "View Info" button is tapped.
|
||||
final VoidCallback? onViewInfoTap;
|
||||
|
||||
/// A callback that is called when the "Leave Channel" button is tapped.
|
||||
///
|
||||
/// Only shown when the channel is a group channel.
|
||||
final VoidCallback? onLeaveChannelTap;
|
||||
|
||||
/// A callback that is called when the "Delete Conversation" button is tapped.
|
||||
///
|
||||
/// Only shown when you are the `owner` of the channel.
|
||||
final VoidCallback? onDeleteConversationTap;
|
||||
|
||||
/// A callback that is called when the "Cancel" button is tapped.
|
||||
final VoidCallback? onCancelTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeData = StreamChatTheme.of(context);
|
||||
final colorTheme = themeData.colorTheme;
|
||||
final channelPreviewTheme = ChannelPreviewTheme.of(context);
|
||||
|
||||
final currentUser = channel.client.state.currentUser;
|
||||
final isOneToOneChannel = channel.isDistinct && channel.memberCount == 2;
|
||||
|
||||
final members = channel.state?.members ?? [];
|
||||
|
||||
final isOwner = members.any(
|
||||
(it) => it.user?.id == currentUser?.id && it.role == 'owner',
|
||||
);
|
||||
|
||||
// remove current user in case it's 1-1 conversation
|
||||
if (isOneToOneChannel) {
|
||||
members.removeWhere((it) => it.user?.id == currentUser?.id);
|
||||
}
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 24),
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: StreamChannelName(
|
||||
channel: channel,
|
||||
textStyle: themeData.textTheme.headlineBold,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Center(
|
||||
// TODO: Refactor ChannelInfo
|
||||
child: ChannelInfo(
|
||||
showTypingIndicator: false,
|
||||
channel: channel,
|
||||
textStyle: channelPreviewTheme.subtitleStyle,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 17),
|
||||
Container(
|
||||
height: 94,
|
||||
alignment: Alignment.center,
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
itemCount: members.length,
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
separatorBuilder: (context, index) => const SizedBox(width: 16),
|
||||
itemBuilder: (context, index) {
|
||||
final member = members[index];
|
||||
final user = member.user!;
|
||||
return Column(
|
||||
children: [
|
||||
UserAvatar(
|
||||
user: user,
|
||||
constraints: const BoxConstraints(
|
||||
maxHeight: 64,
|
||||
maxWidth: 64,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
onlineIndicatorConstraints: BoxConstraints.tight(
|
||||
const Size(12, 12),
|
||||
),
|
||||
onTap: onMemberTap != null
|
||||
? (_) => onMemberTap!(member)
|
||||
: null,
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
user.name,
|
||||
style: themeData.textTheme.footnoteBold,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
OptionListTile(
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: StreamSvgIcon.user(
|
||||
color: colorTheme.textLowEmphasis,
|
||||
),
|
||||
),
|
||||
title: context.translations.viewInfoLabel,
|
||||
onTap: onViewInfoTap,
|
||||
),
|
||||
if (!isOneToOneChannel)
|
||||
OptionListTile(
|
||||
title: context.translations.leaveGroupLabel,
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: StreamSvgIcon.userRemove(
|
||||
color: colorTheme.textLowEmphasis,
|
||||
),
|
||||
),
|
||||
onTap: onLeaveChannelTap,
|
||||
),
|
||||
if (isOwner)
|
||||
OptionListTile(
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: StreamSvgIcon.delete(
|
||||
color: colorTheme.accentError,
|
||||
),
|
||||
),
|
||||
title: context.translations.deleteConversationLabel,
|
||||
titleColor: colorTheme.accentError,
|
||||
onTap: onDeleteConversationTap,
|
||||
),
|
||||
OptionListTile(
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: StreamSvgIcon.closeSmall(
|
||||
color: colorTheme.textLowEmphasis,
|
||||
),
|
||||
),
|
||||
title: context.translations.cancelLabel,
|
||||
onTap: onCancelTap ?? Navigator.of(context).pop,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const _kDefaultChannelInfoBottomSheetShape = RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(32),
|
||||
topRight: Radius.circular(32),
|
||||
),
|
||||
);
|
||||
|
||||
/// Shows a modal material design bottom sheet.
|
||||
///
|
||||
/// A modal bottom sheet is an alternative to a menu or a dialog and prevents
|
||||
/// the user from interacting with the rest of the app.
|
||||
///
|
||||
/// A closely related widget is a persistent bottom sheet, which shows
|
||||
/// information that supplements the primary content of the app without
|
||||
/// preventing the use from interacting with the app. Persistent bottom sheets
|
||||
/// can be created and displayed with the [showBottomSheet] function or the
|
||||
/// [ScaffoldState.showBottomSheet] method.
|
||||
///
|
||||
/// The `context` argument is used to look up the [Navigator] and [Theme] for
|
||||
/// the bottom sheet. It is only used when the method is called. Its
|
||||
/// corresponding widget can be safely removed from the tree before the bottom
|
||||
/// sheet is closed.
|
||||
///
|
||||
/// The `isScrollControlled` parameter specifies whether this is a route for
|
||||
/// a bottom sheet that will utilize [DraggableScrollableSheet]. If you wish
|
||||
/// to have a bottom sheet that has a scrollable child such as a [ListView] or
|
||||
/// a [GridView] and have the bottom sheet be draggable, you should set this
|
||||
/// parameter to true.
|
||||
///
|
||||
/// The `useRootNavigator` parameter ensures that the root navigator is used to
|
||||
/// display the [BottomSheet] when set to `true`. This is useful in the case
|
||||
/// that a modal [BottomSheet] needs to be displayed above all other content
|
||||
/// but the caller is inside another [Navigator].
|
||||
///
|
||||
/// The [isDismissible] parameter specifies whether the bottom sheet will be
|
||||
/// dismissed when user taps on the scrim.
|
||||
///
|
||||
/// The [enableDrag] parameter specifies whether the bottom sheet can be
|
||||
/// dragged up and down and dismissed by swiping downwards.
|
||||
///
|
||||
/// The optional [backgroundColor], [elevation], [shape], [clipBehavior],
|
||||
/// [constraints] and [transitionAnimationController]
|
||||
/// parameters can be passed in to customize the appearance and behavior of
|
||||
/// modal bottom sheets (see the documentation for these on [BottomSheet]
|
||||
/// for more details).
|
||||
///
|
||||
/// The [transitionAnimationController] controls the bottom sheet's entrance and
|
||||
/// exit animations if provided.
|
||||
///
|
||||
/// The optional `routeSettings` parameter sets the [RouteSettings] of the modal bottom sheet
|
||||
/// sheet. This is particularly useful in the case that a user wants to observe
|
||||
/// [PopupRoute]s within a [NavigatorObserver].
|
||||
///
|
||||
/// Returns a `Future` that resolves to the value (if any) that was passed to
|
||||
/// [Navigator.pop] when the modal bottom sheet was closed.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [BottomSheet], which becomes the parent of the widget returned by the
|
||||
/// function passed as the `builder` argument to [showModalBottomSheet].
|
||||
/// * [showBottomSheet] and [ScaffoldState.showBottomSheet], for showing
|
||||
/// non-modal bottom sheets.
|
||||
/// * [DraggableScrollableSheet], which allows you to create a bottom sheet
|
||||
/// that grows and then becomes scrollable once it reaches its maximum size.
|
||||
/// * <https://material.io/design/components/sheets-bottom.html#modal-bottom-sheet>
|
||||
Future<T?> showChannelInfoModalBottomSheet<T>({
|
||||
required BuildContext context,
|
||||
required Channel channel,
|
||||
Color? backgroundColor,
|
||||
double? elevation,
|
||||
BoxConstraints? constraints,
|
||||
Color? barrierColor,
|
||||
bool isScrollControlled = true,
|
||||
bool useRootNavigator = false,
|
||||
bool isDismissible = true,
|
||||
bool enableDrag = true,
|
||||
RouteSettings? routeSettings,
|
||||
AnimationController? transitionAnimationController,
|
||||
Clip? clipBehavior = Clip.hardEdge,
|
||||
ShapeBorder? shape = _kDefaultChannelInfoBottomSheetShape,
|
||||
void Function(Member)? onMemberTap,
|
||||
VoidCallback? onViewInfoTap,
|
||||
VoidCallback? onLeaveChannelTap,
|
||||
VoidCallback? onDeleteConversationTap,
|
||||
VoidCallback? onCancelTap,
|
||||
}) =>
|
||||
showModalBottomSheet<T>(
|
||||
context: context,
|
||||
backgroundColor: backgroundColor,
|
||||
elevation: elevation,
|
||||
shape: shape,
|
||||
clipBehavior: clipBehavior,
|
||||
constraints: constraints,
|
||||
barrierColor: barrierColor,
|
||||
isScrollControlled: isScrollControlled,
|
||||
useRootNavigator: useRootNavigator,
|
||||
isDismissible: isDismissible,
|
||||
enableDrag: enableDrag,
|
||||
routeSettings: routeSettings,
|
||||
transitionAnimationController: transitionAnimationController,
|
||||
builder: (BuildContext context) => StreamChannelInfoBottomSheet(
|
||||
channel: channel,
|
||||
onMemberTap: onMemberTap,
|
||||
onViewInfoTap: onViewInfoTap,
|
||||
onLeaveChannelTap: onLeaveChannelTap,
|
||||
onDeleteConversationTap: onDeleteConversationTap,
|
||||
onCancelTap: onCancelTap,
|
||||
),
|
||||
);
|
||||
|
||||
/// Shows a material design bottom sheet in the nearest [Scaffold] ancestor. If
|
||||
/// you wish to show a persistent bottom sheet, use [Scaffold.bottomSheet].
|
||||
///
|
||||
/// Returns a controller that can be used to close and otherwise manipulate the
|
||||
/// bottom sheet.
|
||||
///
|
||||
/// The optional [backgroundColor], [elevation], [shape], [clipBehavior],
|
||||
/// [constraints] and [transitionAnimationController]
|
||||
/// parameters can be passed in to customize the appearance and behavior of
|
||||
/// persistent bottom sheets (see the documentation for these on [BottomSheet]
|
||||
/// for more details).
|
||||
///
|
||||
/// To rebuild the bottom sheet (e.g. if it is stateful), call
|
||||
/// [PersistentBottomSheetController.setState] on the controller returned by
|
||||
/// this method.
|
||||
///
|
||||
/// The new bottom sheet becomes a [LocalHistoryEntry] for the enclosing
|
||||
/// [ModalRoute] and a back button is added to the app bar of the [Scaffold]
|
||||
/// that closes the bottom sheet.
|
||||
///
|
||||
/// To create a persistent bottom sheet that is not a [LocalHistoryEntry] and
|
||||
/// does not add a back button to the enclosing Scaffold's app bar, use the
|
||||
/// [Scaffold.bottomSheet] constructor parameter.
|
||||
///
|
||||
/// A closely related widget is a modal bottom sheet, which is an alternative
|
||||
/// to a menu or a dialog and prevents the user from interacting with the rest
|
||||
/// of the app. Modal bottom sheets can be created and displayed with the
|
||||
/// [showModalBottomSheet] function.
|
||||
///
|
||||
/// The `context` argument is used to look up the [Scaffold] for the bottom
|
||||
/// sheet. It is only used when the method is called. Its corresponding widget
|
||||
/// can be safely removed from the tree before the bottom sheet is closed.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [BottomSheet], which becomes the parent of the widget returned by the
|
||||
/// `builder`.
|
||||
/// * [showModalBottomSheet], which can be used to display a modal bottom
|
||||
/// sheet.
|
||||
/// * [Scaffold.of], for information about how to obtain the [BuildContext].
|
||||
/// * <https://material.io/design/components/sheets-bottom.html#standard-bottom-sheet>
|
||||
PersistentBottomSheetController<T> showChannelInfoBottomSheet<T>({
|
||||
required BuildContext context,
|
||||
required Channel channel,
|
||||
Color? backgroundColor,
|
||||
double? elevation,
|
||||
BoxConstraints? constraints,
|
||||
AnimationController? transitionAnimationController,
|
||||
Clip? clipBehavior = Clip.hardEdge,
|
||||
ShapeBorder? shape = _kDefaultChannelInfoBottomSheetShape,
|
||||
void Function(Member)? onMemberTap,
|
||||
VoidCallback? onViewInfoTap,
|
||||
VoidCallback? onLeaveChannelTap,
|
||||
VoidCallback? onDeleteConversationTap,
|
||||
VoidCallback? onCancelTap,
|
||||
}) =>
|
||||
showBottomSheet<T>(
|
||||
context: context,
|
||||
backgroundColor: backgroundColor,
|
||||
elevation: elevation,
|
||||
shape: shape,
|
||||
clipBehavior: clipBehavior,
|
||||
constraints: constraints,
|
||||
transitionAnimationController: transitionAnimationController,
|
||||
builder: (BuildContext context) => StreamChannelInfoBottomSheet(
|
||||
channel: channel,
|
||||
onMemberTap: onMemberTap,
|
||||
onViewInfoTap: onViewInfoTap,
|
||||
onLeaveChannelTap: onLeaveChannelTap,
|
||||
onDeleteConversationTap: onDeleteConversationTap,
|
||||
onCancelTap: onCancelTap,
|
||||
),
|
||||
);
|
||||
@@ -47,7 +47,10 @@ export 'src/user_item.dart';
|
||||
export 'src/user_list_view.dart';
|
||||
export 'src/user_mention_tile.dart';
|
||||
export 'src/utils.dart';
|
||||
|
||||
// v4
|
||||
export 'src/v4/channel_list_view/stream_channel_list_controller.dart';
|
||||
export 'src/v4/channel_list_view/stream_channel_list_event_handler.dart';
|
||||
export 'src/v4/channel_list_view/stream_channel_list_view.dart';
|
||||
export 'src/v4/stream_channel_info_bottom_sheet.dart';
|
||||
export 'src/visible_footnote.dart';
|
||||
|
||||
Reference in New Issue
Block a user