Compare commits

..

2 Commits

Author SHA1 Message Date
a8e0ade0c8 Realm Popularity 2025-02-20 23:44:28 +08:00
3338e699c4 💄 Memorable realm view style 2025-02-20 22:09:05 +08:00
9 changed files with 247 additions and 186 deletions

View File

@ -17,6 +17,7 @@ const kAppDrawerPreferCollapse = 'app_drawer_prefer_collapse';
const kAppNotifyWithHaptic = 'app_notify_with_haptic'; const kAppNotifyWithHaptic = 'app_notify_with_haptic';
const kAppExpandPostLink = 'app_expand_post_link'; const kAppExpandPostLink = 'app_expand_post_link';
const kAppExpandChatLink = 'app_expand_chat_link'; const kAppExpandChatLink = 'app_expand_chat_link';
const kAppRealmCompactView = 'app_realm_compact_view';
const Map<String, FilterQuality> kImageQualityLevel = { const Map<String, FilterQuality> kImageQualityLevel = {
'settingsImageQualityLowest': FilterQuality.none, 'settingsImageQualityLowest': FilterQuality.none,
@ -72,6 +73,13 @@ class ConfigProvider extends ChangeNotifier {
return prefs.getString(kNetworkServerStoreKey) ?? kNetworkServerDefault; return prefs.getString(kNetworkServerStoreKey) ?? kNetworkServerDefault;
} }
bool get realmCompactView {
return prefs.getBool(kAppRealmCompactView) ?? false;
}
set realmCompactView(bool value) {
prefs.setBool(kAppRealmCompactView, value);
}
set serverUrl(String url) { set serverUrl(String url) {
prefs.setString(kNetworkServerStoreKey, url); prefs.setString(kNetworkServerStoreKey, url);
_home.saveWidgetData("nex_server_url", url); _home.saveWidgetData("nex_server_url", url);

View File

@ -1,6 +1,3 @@
import 'dart:convert';
import 'dart:developer';
import 'package:dismissible_page/dismissible_page.dart'; import 'package:dismissible_page/dismissible_page.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';

View File

@ -104,7 +104,7 @@ class _ChannelDetailScreenState extends State<ChannelDetailScreen> {
try { try {
final sn = context.read<SnNetworkProvider>(); final sn = context.read<SnNetworkProvider>();
await sn.client.delete( await sn.client.delete(
'/cgi/im/channels/${_channel!.realm?.alias ?? 'global'}/${_channel!.id}/members/me', '/cgi/im/channels/${_channel!.realm?.alias ?? 'global'}/${_channel!.alias}/members/me',
); );
if (!mounted) return; if (!mounted) return;
Navigator.pop(context, false); Navigator.pop(context, false);

View File

@ -4,17 +4,16 @@ import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:surface/providers/config.dart';
import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/sn_network.dart';
import 'package:surface/providers/userinfo.dart'; import 'package:surface/providers/userinfo.dart';
import 'package:surface/types/realm.dart'; import 'package:surface/types/realm.dart';
import 'package:surface/widgets/account/account_image.dart';
import 'package:surface/widgets/app_bar_leading.dart'; import 'package:surface/widgets/app_bar_leading.dart';
import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/dialog.dart';
import 'package:surface/widgets/loading_indicator.dart'; import 'package:surface/widgets/loading_indicator.dart';
import 'package:surface/widgets/navigation/app_scaffold.dart'; import 'package:surface/widgets/navigation/app_scaffold.dart';
import 'package:surface/widgets/realm/realm_item.dart';
import 'package:surface/widgets/unauthorized_hint.dart'; import 'package:surface/widgets/unauthorized_hint.dart';
import 'package:surface/widgets/universal_image.dart';
class RealmScreen extends StatefulWidget { class RealmScreen extends StatefulWidget {
const RealmScreen({super.key}); const RealmScreen({super.key});
@ -75,12 +74,12 @@ class _RealmScreenState extends State<RealmScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_isCompactView = context.read<ConfigProvider>().realmCompactView;
_fetchRealms(); _fetchRealms();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final sn = context.read<SnNetworkProvider>();
final ua = context.read<UserProvider>(); final ua = context.read<UserProvider>();
if (!ua.isAuthorized) { if (!ua.isAuthorized) {
@ -110,6 +109,7 @@ class _RealmScreenState extends State<RealmScreen> {
icon: !_isCompactView ? const Icon(Symbols.view_list) : const Icon(Symbols.view_module), icon: !_isCompactView ? const Icon(Symbols.view_list) : const Icon(Symbols.view_module),
onPressed: () { onPressed: () {
setState(() => _isCompactView = !_isCompactView); setState(() => _isCompactView = !_isCompactView);
context.read<ConfigProvider>().realmCompactView = _isCompactView;
}, },
), ),
const Gap(8), const Gap(8),
@ -134,21 +134,12 @@ class _RealmScreenState extends State<RealmScreen> {
itemCount: _realms?.length ?? 0, itemCount: _realms?.length ?? 0,
itemBuilder: (context, idx) { itemBuilder: (context, idx) {
final realm = _realms![idx]; final realm = _realms![idx];
if (_isCompactView) {
return ListTile( return RealmItemWidget(
contentPadding: const EdgeInsets.symmetric(horizontal: 16), showPopularity: false,
leading: AccountImage( item: realm,
content: realm.avatar, isListView: _isCompactView,
fallbackWidget: const Icon(Symbols.group, size: 20), actionListView: [
),
title: Text(realm.name),
subtitle: Text(
realm.description,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
trailing: PopupMenuButton(
itemBuilder: (BuildContext context) => [
PopupMenuItem( PopupMenuItem(
child: Row( child: Row(
children: [ children: [
@ -181,82 +172,8 @@ class _RealmScreenState extends State<RealmScreen> {
}, },
), ),
], ],
), onUpdate: _fetchRealms,
onTap: () {
GoRouter.of(context).pushNamed(
'realmDetail',
pathParameters: {'alias': realm.alias},
).then((value) {
if (value == true) {
_fetchRealms();
}
});
},
); );
}
return Container(
constraints: BoxConstraints(maxWidth: 640),
child: Card(
margin: const EdgeInsets.all(12),
child: InkWell(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AspectRatio(
aspectRatio: 16 / 7,
child: Stack(
clipBehavior: Clip.none,
fit: StackFit.expand,
children: [
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Container(
color: Theme.of(context).colorScheme.surfaceContainer,
child: (realm.banner?.isEmpty ?? true)
? const SizedBox.shrink()
: AutoResizeUniversalImage(
sn.getAttachmentUrl(realm.banner!),
fit: BoxFit.cover,
),
),
),
Positioned(
bottom: -30,
left: 18,
child: AccountImage(
content: realm.avatar,
radius: 24,
fallbackWidget: const Icon(Symbols.group, size: 24),
),
),
],
),
),
const Gap(20 + 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(realm.name).textStyle(Theme.of(context).textTheme.titleMedium!),
Text(realm.description).textStyle(Theme.of(context).textTheme.bodySmall!),
],
).padding(horizontal: 24, bottom: 14),
],
),
onTap: () {
GoRouter.of(context).pushNamed(
'realmDetail',
pathParameters: {'alias': realm.alias},
).then((value) {
if (value == true) {
_fetchRealms();
}
});
},
),
),
).center();
}, },
), ),
), ),

View File

@ -4,6 +4,7 @@ import 'package:gap/gap.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/config.dart';
import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/sn_network.dart';
import 'package:surface/providers/userinfo.dart'; import 'package:surface/providers/userinfo.dart';
import 'package:surface/types/chat.dart'; import 'package:surface/types/chat.dart';
@ -12,6 +13,7 @@ import 'package:surface/widgets/account/account_image.dart';
import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/dialog.dart';
import 'package:surface/widgets/loading_indicator.dart'; import 'package:surface/widgets/loading_indicator.dart';
import 'package:surface/widgets/navigation/app_scaffold.dart'; import 'package:surface/widgets/navigation/app_scaffold.dart';
import 'package:surface/widgets/realm/realm_item.dart';
import 'package:surface/widgets/universal_image.dart'; import 'package:surface/widgets/universal_image.dart';
class RealmDiscoveryScreen extends StatefulWidget { class RealmDiscoveryScreen extends StatefulWidget {
@ -24,6 +26,7 @@ class RealmDiscoveryScreen extends StatefulWidget {
class _RealmDiscoveryScreenState extends State<RealmDiscoveryScreen> { class _RealmDiscoveryScreenState extends State<RealmDiscoveryScreen> {
List<SnRealm>? _realms; List<SnRealm>? _realms;
bool _isBusy = false; bool _isBusy = false;
bool _isCompactView = false;
Future<void> _fetchRealms() async { Future<void> _fetchRealms() async {
try { try {
@ -44,16 +47,25 @@ class _RealmDiscoveryScreenState extends State<RealmDiscoveryScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_isCompactView = context.read<ConfigProvider>().realmCompactView;
_fetchRealms(); _fetchRealms();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final sn = context.read<SnNetworkProvider>();
return AppScaffold( return AppScaffold(
appBar: AppBar( appBar: AppBar(
title: Text('screenRealmDiscovery').tr(), title: Text('screenRealmDiscovery').tr(),
actions: [
IconButton(
icon: _isCompactView ? const Icon(Symbols.view_list) : const Icon(Symbols.view_module),
onPressed: () {
setState(() => _isCompactView = !_isCompactView);
context.read<ConfigProvider>().realmCompactView = _isCompactView;
},
),
const Gap(8),
],
), ),
body: Column( body: Column(
children: [ children: [
@ -66,64 +78,16 @@ class _RealmDiscoveryScreenState extends State<RealmDiscoveryScreen> {
itemCount: _realms?.length ?? 0, itemCount: _realms?.length ?? 0,
itemBuilder: (context, idx) { itemBuilder: (context, idx) {
final realm = _realms![idx]; final realm = _realms![idx];
return Container( return RealmItemWidget(
constraints: BoxConstraints(maxWidth: 640), item: realm,
child: Card( isListView: _isCompactView,
margin: const EdgeInsets.all(12),
child: InkWell(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AspectRatio(
aspectRatio: 16 / 7,
child: Stack(
clipBehavior: Clip.none,
fit: StackFit.expand,
children: [
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Container(
color: Theme.of(context).colorScheme.surfaceContainer,
child: (realm.banner?.isEmpty ?? true)
? const SizedBox.shrink()
: AutoResizeUniversalImage(
sn.getAttachmentUrl(realm.banner!),
fit: BoxFit.cover,
),
),
),
Positioned(
bottom: -30,
left: 18,
child: AccountImage(
content: realm.avatar,
radius: 24,
fallbackWidget: const Icon(Symbols.group, size: 24),
),
),
],
),
),
const Gap(20 + 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(realm.name).textStyle(Theme.of(context).textTheme.titleMedium!),
Text(realm.description).textStyle(Theme.of(context).textTheme.bodySmall!),
],
).padding(horizontal: 24, bottom: 14),
],
),
onTap: () { onTap: () {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
builder: (context) => _RealmJoinPopup(realm: realm), builder: (context) => _RealmJoinPopup(realm: realm),
); );
}, },
), );
),
).center();
}, },
), ),
), ),

View File

@ -43,6 +43,7 @@ class SnRealm with _$SnRealm {
@HiveField(10) required int accountId, @HiveField(10) required int accountId,
@HiveField(11) required bool isPublic, @HiveField(11) required bool isPublic,
@HiveField(12) required bool isCommunity, @HiveField(12) required bool isCommunity,
@Default(0) int popularity,
}) = _SnRealm; }) = _SnRealm;
factory SnRealm.fromJson(Map<String, dynamic> json) => factory SnRealm.fromJson(Map<String, dynamic> json) =>

View File

@ -394,6 +394,7 @@ mixin _$SnRealm {
bool get isPublic => throw _privateConstructorUsedError; bool get isPublic => throw _privateConstructorUsedError;
@HiveField(12) @HiveField(12)
bool get isCommunity => throw _privateConstructorUsedError; bool get isCommunity => throw _privateConstructorUsedError;
int get popularity => throw _privateConstructorUsedError;
/// Serializes this SnRealm to a JSON map. /// Serializes this SnRealm to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError; Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@ -423,7 +424,8 @@ abstract class $SnRealmCopyWith<$Res> {
@HiveField(9) Map<String, dynamic>? accessPolicy, @HiveField(9) Map<String, dynamic>? accessPolicy,
@HiveField(10) int accountId, @HiveField(10) int accountId,
@HiveField(11) bool isPublic, @HiveField(11) bool isPublic,
@HiveField(12) bool isCommunity}); @HiveField(12) bool isCommunity,
int popularity});
} }
/// @nodoc /// @nodoc
@ -455,6 +457,7 @@ class _$SnRealmCopyWithImpl<$Res, $Val extends SnRealm>
Object? accountId = null, Object? accountId = null,
Object? isPublic = null, Object? isPublic = null,
Object? isCommunity = null, Object? isCommunity = null,
Object? popularity = null,
}) { }) {
return _then(_value.copyWith( return _then(_value.copyWith(
id: null == id id: null == id
@ -513,6 +516,10 @@ class _$SnRealmCopyWithImpl<$Res, $Val extends SnRealm>
? _value.isCommunity ? _value.isCommunity
: isCommunity // ignore: cast_nullable_to_non_nullable : isCommunity // ignore: cast_nullable_to_non_nullable
as bool, as bool,
popularity: null == popularity
? _value.popularity
: popularity // ignore: cast_nullable_to_non_nullable
as int,
) as $Val); ) as $Val);
} }
} }
@ -538,7 +545,8 @@ abstract class _$$SnRealmImplCopyWith<$Res> implements $SnRealmCopyWith<$Res> {
@HiveField(9) Map<String, dynamic>? accessPolicy, @HiveField(9) Map<String, dynamic>? accessPolicy,
@HiveField(10) int accountId, @HiveField(10) int accountId,
@HiveField(11) bool isPublic, @HiveField(11) bool isPublic,
@HiveField(12) bool isCommunity}); @HiveField(12) bool isCommunity,
int popularity});
} }
/// @nodoc /// @nodoc
@ -568,6 +576,7 @@ class __$$SnRealmImplCopyWithImpl<$Res>
Object? accountId = null, Object? accountId = null,
Object? isPublic = null, Object? isPublic = null,
Object? isCommunity = null, Object? isCommunity = null,
Object? popularity = null,
}) { }) {
return _then(_$SnRealmImpl( return _then(_$SnRealmImpl(
id: null == id id: null == id
@ -626,6 +635,10 @@ class __$$SnRealmImplCopyWithImpl<$Res>
? _value.isCommunity ? _value.isCommunity
: isCommunity // ignore: cast_nullable_to_non_nullable : isCommunity // ignore: cast_nullable_to_non_nullable
as bool, as bool,
popularity: null == popularity
? _value.popularity
: popularity // ignore: cast_nullable_to_non_nullable
as int,
)); ));
} }
} }
@ -648,7 +661,8 @@ class _$SnRealmImpl extends _SnRealm {
@HiveField(9) required final Map<String, dynamic>? accessPolicy, @HiveField(9) required final Map<String, dynamic>? accessPolicy,
@HiveField(10) required this.accountId, @HiveField(10) required this.accountId,
@HiveField(11) required this.isPublic, @HiveField(11) required this.isPublic,
@HiveField(12) required this.isCommunity}) @HiveField(12) required this.isCommunity,
this.popularity = 0})
: _members = members, : _members = members,
_accessPolicy = accessPolicy, _accessPolicy = accessPolicy,
super._(); super._();
@ -713,10 +727,13 @@ class _$SnRealmImpl extends _SnRealm {
@override @override
@HiveField(12) @HiveField(12)
final bool isCommunity; final bool isCommunity;
@override
@JsonKey()
final int popularity;
@override @override
String toString() { String toString() {
return 'SnRealm(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, alias: $alias, name: $name, description: $description, members: $members, avatar: $avatar, banner: $banner, accessPolicy: $accessPolicy, accountId: $accountId, isPublic: $isPublic, isCommunity: $isCommunity)'; return 'SnRealm(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, alias: $alias, name: $name, description: $description, members: $members, avatar: $avatar, banner: $banner, accessPolicy: $accessPolicy, accountId: $accountId, isPublic: $isPublic, isCommunity: $isCommunity, popularity: $popularity)';
} }
@override @override
@ -745,7 +762,9 @@ class _$SnRealmImpl extends _SnRealm {
(identical(other.isPublic, isPublic) || (identical(other.isPublic, isPublic) ||
other.isPublic == isPublic) && other.isPublic == isPublic) &&
(identical(other.isCommunity, isCommunity) || (identical(other.isCommunity, isCommunity) ||
other.isCommunity == isCommunity)); other.isCommunity == isCommunity) &&
(identical(other.popularity, popularity) ||
other.popularity == popularity));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@ -765,7 +784,8 @@ class _$SnRealmImpl extends _SnRealm {
const DeepCollectionEquality().hash(_accessPolicy), const DeepCollectionEquality().hash(_accessPolicy),
accountId, accountId,
isPublic, isPublic,
isCommunity); isCommunity,
popularity);
/// Create a copy of SnRealm /// Create a copy of SnRealm
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@ -798,7 +818,8 @@ abstract class _SnRealm extends SnRealm {
@HiveField(9) required final Map<String, dynamic>? accessPolicy, @HiveField(9) required final Map<String, dynamic>? accessPolicy,
@HiveField(10) required final int accountId, @HiveField(10) required final int accountId,
@HiveField(11) required final bool isPublic, @HiveField(11) required final bool isPublic,
@HiveField(12) required final bool isCommunity}) = _$SnRealmImpl; @HiveField(12) required final bool isCommunity,
final int popularity}) = _$SnRealmImpl;
const _SnRealm._() : super._(); const _SnRealm._() : super._();
factory _SnRealm.fromJson(Map<String, dynamic> json) = _$SnRealmImpl.fromJson; factory _SnRealm.fromJson(Map<String, dynamic> json) = _$SnRealmImpl.fromJson;
@ -844,6 +865,8 @@ abstract class _SnRealm extends SnRealm {
@override @override
@HiveField(12) @HiveField(12)
bool get isCommunity; bool get isCommunity;
@override
int get popularity;
/// Create a copy of SnRealm /// Create a copy of SnRealm
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.

View File

@ -128,6 +128,7 @@ _$SnRealmImpl _$$SnRealmImplFromJson(Map<String, dynamic> json) =>
accountId: (json['account_id'] as num).toInt(), accountId: (json['account_id'] as num).toInt(),
isPublic: json['is_public'] as bool, isPublic: json['is_public'] as bool,
isCommunity: json['is_community'] as bool, isCommunity: json['is_community'] as bool,
popularity: (json['popularity'] as num?)?.toInt() ?? 0,
); );
Map<String, dynamic> _$$SnRealmImplToJson(_$SnRealmImpl instance) => Map<String, dynamic> _$$SnRealmImplToJson(_$SnRealmImpl instance) =>
@ -146,4 +147,5 @@ Map<String, dynamic> _$$SnRealmImplToJson(_$SnRealmImpl instance) =>
'account_id': instance.accountId, 'account_id': instance.accountId,
'is_public': instance.isPublic, 'is_public': instance.isPublic,
'is_community': instance.isCommunity, 'is_community': instance.isCommunity,
'popularity': instance.popularity,
}; };

View File

@ -0,0 +1,149 @@
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/sn_network.dart';
import 'package:surface/types/realm.dart';
import 'package:surface/widgets/account/account_image.dart';
import 'package:surface/widgets/universal_image.dart';
class RealmItemWidget extends StatelessWidget {
final SnRealm item;
final bool isListView;
final List<PopupMenuItem>? actionListView;
final Function? onUpdate;
final Function? onTap;
final bool showPopularity;
const RealmItemWidget({
super.key,
required this.item,
required this.isListView,
this.actionListView,
this.onUpdate,
this.onTap,
this.showPopularity = true,
});
@override
Widget build(BuildContext context) {
if (isListView) {
return ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
leading: AccountImage(
content: item.avatar,
fallbackWidget: const Icon(Symbols.group, size: 20),
),
title: Text(item.name),
subtitle: Row(
children: [
if (showPopularity) const Icon(Symbols.local_fire_department, size: 18).padding(right: 1),
if (showPopularity) Text(item.popularity.toString()),
if (showPopularity) const Gap(6),
Expanded(
child: Text(
item.description,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
trailing:
actionListView != null ? PopupMenuButton(itemBuilder: (BuildContext context) => actionListView!) : null,
onTap: () {
if (onTap != null) {
onTap!();
return;
}
GoRouter.of(context).pushNamed(
'realmDetail',
pathParameters: {'alias': item.alias},
).then((value) {
if (value == true) {
onUpdate?.call();
}
});
},
);
}
final sn = context.read<SnNetworkProvider>();
return Container(
constraints: BoxConstraints(maxWidth: 640),
child: Card(
margin: const EdgeInsets.all(12),
child: InkWell(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AspectRatio(
aspectRatio: 16 / 7,
child: Stack(
clipBehavior: Clip.none,
fit: StackFit.expand,
children: [
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Container(
color: Theme.of(context).colorScheme.surfaceContainer,
child: (item.banner?.isEmpty ?? true)
? const SizedBox.shrink()
: AutoResizeUniversalImage(
sn.getAttachmentUrl(item.banner!),
fit: BoxFit.cover,
),
),
),
Positioned(
bottom: -30,
left: 18,
child: AccountImage(
content: item.avatar,
radius: 24,
fallbackWidget: const Icon(Symbols.group, size: 24),
),
),
],
),
),
const Gap(20 + 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(item.name).textStyle(Theme.of(context).textTheme.titleMedium!),
if (showPopularity)
Row(
children: [
Text(item.popularity.toString()),
const Icon(Symbols.local_fire_department, size: 16).padding(bottom: 2),
],
).padding(top: 6),
Text(item.description).textStyle(Theme.of(context).textTheme.bodySmall!),
],
).padding(horizontal: 24, bottom: 14),
],
),
onTap: () {
if (onTap != null) {
onTap!();
return;
}
GoRouter.of(context).pushNamed(
'realmDetail',
pathParameters: {'alias': item.alias},
).then((value) {
if (value == true) {
onUpdate?.call();
}
});
},
),
),
).center();
}
}