From 8370da4fe36e6e23f4adc60db9b0d4597ccedf9e Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 17 Aug 2025 19:44:43 +0800 Subject: [PATCH] :sparkles: Realm page redesgiened --- lib/screens/realm/realm_detail.dart | 680 +++++++++++++++++----------- lib/widgets/post/post_list.dart | 17 +- lib/widgets/post/post_list.g.dart | 40 +- lib/widgets/post/post_shared.dart | 44 +- 4 files changed, 477 insertions(+), 304 deletions(-) diff --git a/lib/screens/realm/realm_detail.dart b/lib/screens/realm/realm_detail.dart index 68b550d8..35cb1c42 100644 --- a/lib/screens/realm/realm_detail.dart +++ b/lib/screens/realm/realm_detail.dart @@ -4,6 +4,8 @@ import 'package:island/screens/chat/chat.dart'; import 'package:flutter/material.dart'; import 'package:island/models/chat.dart'; import 'package:island/services/color.dart'; +import 'package:island/services/responsive.dart'; +import 'package:island/widgets/post/post_list.dart'; import 'package:palette_generator/palette_generator.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gap/gap.dart'; @@ -78,155 +80,285 @@ class RealmDetailScreen extends HookConsumerWidget { offset: Offset(1.0, 1.0), ); + final realmIdentity = ref.watch(realmIdentityProvider(slug)); + final realmChatRooms = ref.watch(realmChatRoomsProvider(slug)); + + Widget realmDescriptionWidget(SnRealm realm) => Card( + margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4), + child: Theme( + data: Theme.of(context).copyWith(dividerColor: Colors.transparent), + child: ExpansionTile( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8)), + ), + collapsedShape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8)), + ), + title: const Text('description').tr(), + initiallyExpanded: + realmIdentity.hasValue && realmIdentity.value == null, + tilePadding: EdgeInsets.only(left: 24, right: 20), + expandedCrossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + realm.description, + style: const TextStyle(fontSize: 16), + ).padding(horizontal: 20, bottom: 16, top: 8), + ], + ), + ), + ); + + Widget realmActionWidget(SnRealm realm) => Card( + margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4), + child: FilledButton.tonalIcon( + onPressed: () async { + try { + final apiClient = ref.read(apiClientProvider); + await apiClient.post('/sphere/realms/$slug/members/me'); + ref.invalidate(realmIdentityProvider(slug)); + ref.invalidate(realmsJoinedProvider); + showSnackBar('realmJoinSuccess'.tr()); + } catch (err) { + showErrorAlert(err); + } + }, + icon: const Icon(Symbols.add), + label: const Text('realmJoin').tr(), + ).padding(all: 16), + ); + + Widget realmChatRoomListWidget(SnRealm realm) => Card( + margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + 'chatTabGroup', + ).tr().bold().padding(horizontal: 24, top: 12, bottom: 4), + realmChatRooms.when( + loading: () => Center(child: CircularProgressIndicator()), + error: (error, _) => Center(child: Text('Error: $error')), + data: (rooms) { + if (rooms.isEmpty) { + return const SliverToBoxAdapter(child: SizedBox.shrink()); + } + return Column( + children: [ + for (final room in rooms) + ChatRoomListTile( + room: room, + onTap: () { + context.pushNamed( + 'chatRoom', + pathParameters: {'id': room.id}, + ); + }, + ), + ], + ); + }, + ), + ], + ), + ); + return AppScaffold( isNoBackground: false, + appBar: + isWideScreen(context) + ? realmState.when( + data: + (realm) => AppBar( + foregroundColor: appbarColor.value, + leading: PageBackButton( + color: appbarColor.value, + shadows: [iconShadow], + ), + flexibleSpace: Stack( + children: [ + Positioned.fill( + child: + realm!.background?.id != null + ? CloudImageWidget( + fileId: realm.background!.id, + ) + : Container( + color: + Theme.of( + context, + ).appBarTheme.backgroundColor, + ), + ), + FlexibleSpaceBar( + title: Text( + realm.name, + style: TextStyle( + color: + appbarColor.value ?? + Theme.of( + context, + ).appBarTheme.foregroundColor, + shadows: [iconShadow], + ), + ), + background: Container(), + ), + ], + ), + actions: [ + IconButton( + icon: Icon(Icons.people, shadows: [iconShadow]), + onPressed: () { + showModalBottomSheet( + isScrollControlled: true, + context: context, + builder: + (context) => + _RealmMemberListSheet(realmSlug: slug), + ); + }, + ), + _RealmActionMenu( + realmSlug: slug, + iconShadow: iconShadow, + ), + const Gap(8), + ], + ), + error: (_, _) => AppBar(leading: PageBackButton()), + loading: () => AppBar(leading: PageBackButton()), + ) + : null, body: realmState.when( loading: () => const Center(child: CircularProgressIndicator()), error: (error, _) => Center(child: Text('Error: $error')), data: - (realm) => CustomScrollView( - slivers: [ - SliverAppBar( - expandedHeight: 180, - pinned: true, - foregroundColor: appbarColor.value, - leading: PageBackButton( - color: appbarColor.value, - shadows: [iconShadow], - ), - flexibleSpace: FlexibleSpaceBar( - background: - realm!.background?.id != null - ? CloudImageWidget(fileId: realm.background!.id) - : Container( - color: - Theme.of(context).appBarTheme.backgroundColor, - ), - title: Text( - realm.name, - style: TextStyle( - color: - appbarColor.value ?? - Theme.of(context).appBarTheme.foregroundColor, - shadows: [iconShadow], - ), - ), - ), - actions: [ - IconButton( - icon: Icon(Icons.people, shadows: [iconShadow]), - onPressed: () { - showModalBottomSheet( - isScrollControlled: true, - context: context, - builder: - (context) => - _RealmMemberListSheet(realmSlug: slug), - ); - }, - ), - _RealmActionMenu(realmSlug: slug, iconShadow: iconShadow), - const Gap(8), - ], - ), - SliverToBoxAdapter( - child: ref - .watch(realmIdentityProvider(slug)) - .when( - loading: () => const SizedBox.shrink(), - error: (_, _) => const SizedBox.shrink(), - data: - (identity) => Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ExpansionTile( - title: const Text('description').tr(), - initiallyExpanded: identity == null, - tilePadding: EdgeInsets.symmetric( - horizontal: 20, - ), - expandedCrossAxisAlignment: - CrossAxisAlignment.stretch, - children: [ - Text( - realm.description, - style: const TextStyle(fontSize: 16), - ).padding( - horizontal: 20, - bottom: 16, - top: 8, + (realm) => + isWideScreen(context) + ? Row( + children: [ + Flexible( + flex: 3, + child: CustomScrollView( + slivers: [SliverPostList(realm: slug)], + ), + ), + Flexible( + flex: 2, + child: Column( + children: [ + realmIdentity.when( + loading: () => const SizedBox.shrink(), + error: (_, _) => const SizedBox.shrink(), + data: + (identity) => Column( + crossAxisAlignment: + CrossAxisAlignment.stretch, + children: [ + realmDescriptionWidget(realm!), + if (identity == null && + realm.isCommunity) + realmActionWidget(realm) + else + const SizedBox.shrink(), + ], ), - ], + ), + realmChatRoomListWidget(realm!), + ], + ), + ), + ], + ).padding(horizontal: 8, top: 8) + : CustomScrollView( + slivers: [ + SliverAppBar( + expandedHeight: 180, + pinned: true, + foregroundColor: appbarColor.value, + leading: PageBackButton( + color: appbarColor.value, + shadows: [iconShadow], + ), + flexibleSpace: Stack( + children: [ + Positioned.fill( + child: + realm!.background?.id != null + ? CloudImageWidget( + fileId: realm.background!.id, + ) + : Container( + color: + Theme.of( + context, + ).appBarTheme.backgroundColor, + ), + ), + FlexibleSpaceBar( + title: Text( + realm.name, + style: TextStyle( + color: + appbarColor.value ?? + Theme.of( + context, + ).appBarTheme.foregroundColor, + shadows: [iconShadow], + ), ), - if (identity == null && realm.isCommunity) - FilledButton.tonalIcon( - onPressed: () async { - try { - final apiClient = ref.read( - apiClientProvider, - ); - await apiClient.post( - '/sphere/realms/$slug/members/me', - ); - ref.invalidate( - realmIdentityProvider(slug), - ); - ref.invalidate(realmsJoinedProvider); - showSnackBar('realmJoinSuccess'.tr()); - } catch (err) { - showErrorAlert(err); - } - }, - icon: const Icon(Symbols.add), - label: const Text('realmJoin').tr(), - ).padding(horizontal: 16, vertical: 16) - else - const SizedBox.shrink(), - ], - ), - ), - ), - const SliverToBoxAdapter(child: Divider(height: 1)), - Consumer( - builder: (context, ref, _) { - final chatRooms = ref.watch(realmChatRoomsProvider(slug)); - return chatRooms.when( - loading: - () => const SliverToBoxAdapter( - child: Center(child: CircularProgressIndicator()), + background: + Container(), // Empty container since background is handled by Stack + ), + ], ), - error: - (error, _) => SliverToBoxAdapter( - child: Center(child: Text('Error: $error')), - ), - data: (rooms) { - if (rooms.isEmpty) { - return const SliverToBoxAdapter( - child: SizedBox.shrink(), - ); - } - return SliverList( - delegate: SliverChildBuilderDelegate(( - context, - index, - ) { - return ChatRoomListTile( - room: rooms[index], - onTap: () { - context.pushNamed( - 'chatRoom', - pathParameters: {'id': rooms[index].id}, + actions: [ + IconButton( + icon: Icon(Icons.people, shadows: [iconShadow]), + onPressed: () { + showModalBottomSheet( + isScrollControlled: true, + context: context, + builder: + (context) => _RealmMemberListSheet( + realmSlug: slug, + ), ); }, - ); - }, childCount: rooms.length), - ); - }, - ); - }, - ), - ], - ), + ), + _RealmActionMenu( + realmSlug: slug, + iconShadow: iconShadow, + ), + const Gap(8), + ], + ), + SliverGap(4), + SliverToBoxAdapter( + child: realmIdentity.when( + loading: () => const SizedBox.shrink(), + error: (_, _) => const SizedBox.shrink(), + data: + (identity) => Column( + crossAxisAlignment: + CrossAxisAlignment.stretch, + children: [ + realmDescriptionWidget(realm), + if (identity == null && realm.isCommunity) + realmActionWidget(realm) + else + const SizedBox.shrink(), + ], + ), + ), + ), + SliverToBoxAdapter( + child: realmChatRoomListWidget(realm), + ), + SliverPostList(realm: slug), + ], + ), ), ); } @@ -508,146 +640,152 @@ class _RealmMemberListSheet extends HookConsumerWidget { } } + Widget _buildMemberListHeader() { + return Padding( + padding: EdgeInsets.only(top: 16, left: 20, right: 16, bottom: 12), + child: Row( + children: [ + Text( + 'members'.plural(memberState.total), + style: Theme.of(context).textTheme.headlineSmall?.copyWith( + fontWeight: FontWeight.w600, + letterSpacing: -0.5, + ), + ), + const Spacer(), + IconButton( + icon: const Icon(Symbols.person_add), + onPressed: invitePerson, + style: IconButton.styleFrom(minimumSize: const Size(36, 36)), + ), + IconButton( + icon: const Icon(Symbols.refresh), + onPressed: () { + // Refresh both providers + memberNotifier.reset(); + memberNotifier.loadMore(); + ref.invalidate(memberListProvider); + }, + ), + IconButton( + icon: const Icon(Symbols.close), + onPressed: () => Navigator.pop(context), + style: IconButton.styleFrom(minimumSize: const Size(36, 36)), + ), + ], + ), + ); + } + + Widget _buildMemberListContent() { + return Expanded( + child: PagingHelperView( + provider: memberListProvider, + futureRefreshable: memberListProvider.future, + notifierRefreshable: memberListProvider.notifier, + contentBuilder: (data, widgetCount, endItemView) { + return ListView.builder( + itemCount: widgetCount, + itemBuilder: (context, index) { + if (index == data.items.length) { + return endItemView; + } + + final member = data.items[index]; + return ListTile( + contentPadding: EdgeInsets.only(left: 16, right: 12), + leading: ProfilePictureWidget( + fileId: member.account!.profile.picture?.id, + ), + title: Row( + spacing: 6, + children: [ + Flexible(child: Text(member.account!.nick)), + if (member.joinedAt == null) + const Icon(Symbols.pending_actions, size: 20), + ], + ), + subtitle: Row( + children: [ + Text( + member.role >= 100 + ? 'permissionOwner' + : member.role >= 50 + ? 'permissionModerator' + : 'permissionMember', + ).tr(), + Text('·').bold().padding(horizontal: 6), + Expanded(child: Text("@${member.account!.name}")), + ], + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if ((realmIdentity.value?.role ?? 0) >= 50) + IconButton( + icon: const Icon(Symbols.edit), + onPressed: () { + showModalBottomSheet( + isScrollControlled: true, + context: context, + builder: + (context) => _RealmMemberRoleSheet( + realmSlug: realmSlug, + member: member, + ), + ).then((value) { + if (value != null) { + // Refresh both providers + memberNotifier.reset(); + memberNotifier.loadMore(); + ref.invalidate(memberListProvider); + } + }); + }, + ), + if ((realmIdentity.value?.role ?? 0) >= 50) + IconButton( + icon: const Icon(Symbols.delete), + onPressed: () { + showConfirmAlert( + 'removeRealmMemberHint'.tr(), + 'removeRealmMember'.tr(), + ).then((confirm) async { + if (confirm != true) return; + try { + final apiClient = ref.watch(apiClientProvider); + await apiClient.delete( + '/sphere/realms/$realmSlug/members/${member.accountId}', + ); + // Refresh both providers + memberNotifier.reset(); + memberNotifier.loadMore(); + ref.invalidate(memberListProvider); + } catch (err) { + showErrorAlert(err); + } + }); + }, + ), + ], + ), + ); + }, + ); + }, + ), + ); + } + return Container( constraints: BoxConstraints( maxHeight: MediaQuery.of(context).size.height * 0.8, ), child: Column( children: [ - Padding( - padding: EdgeInsets.only(top: 16, left: 20, right: 16, bottom: 12), - child: Row( - children: [ - Text( - 'members'.plural(memberState.total), - style: Theme.of(context).textTheme.headlineSmall?.copyWith( - fontWeight: FontWeight.w600, - letterSpacing: -0.5, - ), - ), - const Spacer(), - IconButton( - icon: const Icon(Symbols.person_add), - onPressed: invitePerson, - style: IconButton.styleFrom(minimumSize: const Size(36, 36)), - ), - IconButton( - icon: const Icon(Symbols.refresh), - onPressed: () { - // Refresh both providers - memberNotifier.reset(); - memberNotifier.loadMore(); - ref.invalidate(memberListProvider); - }, - ), - IconButton( - icon: const Icon(Symbols.close), - onPressed: () => Navigator.pop(context), - style: IconButton.styleFrom(minimumSize: const Size(36, 36)), - ), - ], - ), - ), + _buildMemberListHeader(), const Divider(height: 1), - Expanded( - child: PagingHelperView( - provider: memberListProvider, - futureRefreshable: memberListProvider.future, - notifierRefreshable: memberListProvider.notifier, - contentBuilder: (data, widgetCount, endItemView) { - return ListView.builder( - itemCount: widgetCount, - itemBuilder: (context, index) { - if (index == data.items.length) { - return endItemView; - } - - final member = data.items[index]; - return ListTile( - contentPadding: EdgeInsets.only(left: 16, right: 12), - leading: ProfilePictureWidget( - fileId: member.account!.profile.picture?.id, - ), - title: Row( - spacing: 6, - children: [ - Flexible(child: Text(member.account!.nick)), - if (member.joinedAt == null) - const Icon(Symbols.pending_actions, size: 20), - ], - ), - subtitle: Row( - children: [ - Text( - member.role >= 100 - ? 'permissionOwner' - : member.role >= 50 - ? 'permissionModerator' - : 'permissionMember', - ).tr(), - Text('·').bold().padding(horizontal: 6), - Expanded(child: Text("@${member.account!.name}")), - ], - ), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - if ((realmIdentity.value?.role ?? 0) >= 50) - IconButton( - icon: const Icon(Symbols.edit), - onPressed: () { - showModalBottomSheet( - isScrollControlled: true, - context: context, - builder: - (context) => _RealmMemberRoleSheet( - realmSlug: realmSlug, - member: member, - ), - ).then((value) { - if (value != null) { - // Refresh both providers - memberNotifier.reset(); - memberNotifier.loadMore(); - ref.invalidate(memberListProvider); - } - }); - }, - ), - if ((realmIdentity.value?.role ?? 0) >= 50) - IconButton( - icon: const Icon(Symbols.delete), - onPressed: () { - showConfirmAlert( - 'removeRealmMemberHint'.tr(), - 'removeRealmMember'.tr(), - ).then((confirm) async { - if (confirm != true) return; - try { - final apiClient = ref.watch( - apiClientProvider, - ); - await apiClient.delete( - '/sphere/realms/$realmSlug/members/${member.accountId}', - ); - // Refresh both providers - memberNotifier.reset(); - memberNotifier.loadMore(); - ref.invalidate(memberListProvider); - } catch (err) { - showErrorAlert(err); - } - }); - }, - ), - ], - ), - ); - }, - ); - }, - ), - ), + _buildMemberListContent(), ], ), ); diff --git a/lib/widgets/post/post_list.dart b/lib/widgets/post/post_list.dart index a6326864..14ce8eb4 100644 --- a/lib/widgets/post/post_list.dart +++ b/lib/widgets/post/post_list.dart @@ -15,8 +15,9 @@ class PostListNotifier extends _$PostListNotifier static const int _pageSize = 20; @override - Future> build( - String? pubName, { + Future> build({ + String? pubName, + String? realm, int? type, List? categories, List? tags, @@ -33,6 +34,7 @@ class PostListNotifier extends _$PostListNotifier 'offset': offset, 'take': _pageSize, if (pubName != null) 'pub': pubName, + if (realm != null) 'realm': realm, if (type != null) 'type': type, if (tags != null) 'tags': tags, if (categories != null) 'categories': categories, @@ -68,6 +70,7 @@ enum PostItemType { class SliverPostList extends HookConsumerWidget { final String? pubName; + final String? realm; final int? type; final List? categories; final List? tags; @@ -81,6 +84,7 @@ class SliverPostList extends HookConsumerWidget { const SliverPostList({ super.key, this.pubName, + this.realm, this.type, this.categories, this.tags, @@ -96,21 +100,24 @@ class SliverPostList extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { return PagingHelperSliverView( provider: postListNotifierProvider( - pubName, + pubName: pubName, + realm: realm, type: type, categories: categories, tags: tags, ), futureRefreshable: postListNotifierProvider( - pubName, + pubName: pubName, + realm: realm, type: type, categories: categories, tags: tags, ).future, notifierRefreshable: postListNotifierProvider( - pubName, + pubName: pubName, + realm: realm, type: type, categories: categories, tags: tags, diff --git a/lib/widgets/post/post_list.g.dart b/lib/widgets/post/post_list.g.dart index 73ca52ca..c8944439 100644 --- a/lib/widgets/post/post_list.g.dart +++ b/lib/widgets/post/post_list.g.dart @@ -6,7 +6,7 @@ part of 'post_list.dart'; // RiverpodGenerator // ************************************************************************** -String _$postListNotifierHash() => r'2ca4f3cfbbcd04f3cc32e7f7bd511a5811042829'; +String _$postListNotifierHash() => r'9784b282b3ee14b7109e263c5841a082cf0be78e'; /// Copied from Dart SDK class _SystemHash { @@ -32,12 +32,14 @@ class _SystemHash { abstract class _$PostListNotifier extends BuildlessAutoDisposeAsyncNotifier> { late final String? pubName; + late final String? realm; late final int? type; late final List? categories; late final List? tags; - FutureOr> build( - String? pubName, { + FutureOr> build({ + String? pubName, + String? realm, int? type, List? categories, List? tags, @@ -55,14 +57,16 @@ class PostListNotifierFamily const PostListNotifierFamily(); /// See also [PostListNotifier]. - PostListNotifierProvider call( - String? pubName, { + PostListNotifierProvider call({ + String? pubName, + String? realm, int? type, List? categories, List? tags, }) { return PostListNotifierProvider( - pubName, + pubName: pubName, + realm: realm, type: type, categories: categories, tags: tags, @@ -74,7 +78,8 @@ class PostListNotifierFamily covariant PostListNotifierProvider provider, ) { return call( - provider.pubName, + pubName: provider.pubName, + realm: provider.realm, type: provider.type, categories: provider.categories, tags: provider.tags, @@ -104,8 +109,9 @@ class PostListNotifierProvider CursorPagingData > { /// See also [PostListNotifier]. - PostListNotifierProvider( - String? pubName, { + PostListNotifierProvider({ + String? pubName, + String? realm, int? type, List? categories, List? tags, @@ -113,6 +119,7 @@ class PostListNotifierProvider () => PostListNotifier() ..pubName = pubName + ..realm = realm ..type = type ..categories = categories ..tags = tags, @@ -126,6 +133,7 @@ class PostListNotifierProvider allTransitiveDependencies: PostListNotifierFamily._allTransitiveDependencies, pubName: pubName, + realm: realm, type: type, categories: categories, tags: tags, @@ -139,12 +147,14 @@ class PostListNotifierProvider required super.debugGetCreateSourceHash, required super.from, required this.pubName, + required this.realm, required this.type, required this.categories, required this.tags, }) : super.internal(); final String? pubName; + final String? realm; final int? type; final List? categories; final List? tags; @@ -154,7 +164,8 @@ class PostListNotifierProvider covariant PostListNotifier notifier, ) { return notifier.build( - pubName, + pubName: pubName, + realm: realm, type: type, categories: categories, tags: tags, @@ -169,6 +180,7 @@ class PostListNotifierProvider () => create() ..pubName = pubName + ..realm = realm ..type = type ..categories = categories ..tags = tags, @@ -178,6 +190,7 @@ class PostListNotifierProvider allTransitiveDependencies: null, debugGetCreateSourceHash: null, pubName: pubName, + realm: realm, type: type, categories: categories, tags: tags, @@ -198,6 +211,7 @@ class PostListNotifierProvider bool operator ==(Object other) { return other is PostListNotifierProvider && other.pubName == pubName && + other.realm == realm && other.type == type && other.categories == categories && other.tags == tags; @@ -207,6 +221,7 @@ class PostListNotifierProvider int get hashCode { var hash = _SystemHash.combine(0, runtimeType.hashCode); hash = _SystemHash.combine(hash, pubName.hashCode); + hash = _SystemHash.combine(hash, realm.hashCode); hash = _SystemHash.combine(hash, type.hashCode); hash = _SystemHash.combine(hash, categories.hashCode); hash = _SystemHash.combine(hash, tags.hashCode); @@ -222,6 +237,9 @@ mixin PostListNotifierRef /// The parameter `pubName` of this provider. String? get pubName; + /// The parameter `realm` of this provider. + String? get realm; + /// The parameter `type` of this provider. int? get type; @@ -244,6 +262,8 @@ class _PostListNotifierProviderElement @override String? get pubName => (origin as PostListNotifierProvider).pubName; @override + String? get realm => (origin as PostListNotifierProvider).realm; + @override int? get type => (origin as PostListNotifierProvider).type; @override List? get categories => diff --git a/lib/widgets/post/post_shared.dart b/lib/widgets/post/post_shared.dart index c884651c..8124b91c 100644 --- a/lib/widgets/post/post_shared.dart +++ b/lib/widgets/post/post_shared.dart @@ -582,25 +582,33 @@ class PostHeader extends StatelessWidget { else ...([ const Icon(Symbols.arrow_right, size: 14), - InkWell( - child: Row( - mainAxisSize: MainAxisSize.min, - spacing: 5, - children: [ - Text(item.realm!.name), - ProfilePictureWidget( - file: item.realm!.picture, - fallbackIcon: Symbols.group, - radius: 9, - ), - ], + Flexible( + child: InkWell( + child: Row( + mainAxisSize: MainAxisSize.min, + spacing: 5, + children: [ + Flexible( + child: Text( + item.realm!.name, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ProfilePictureWidget( + file: item.realm!.picture, + fallbackIcon: Symbols.group, + radius: 9, + ), + ], + ), + onTap: () { + GoRouter.of(context).pushNamed( + 'realmDetail', + pathParameters: {'slug': item.realm!.slug}, + ); + }, ), - onTap: () { - GoRouter.of(context).pushNamed( - 'realmDetail', - pathParameters: {'slug': item.realm!.slug}, - ); - }, ), ]), ],