274 lines
7.3 KiB
Dart
274 lines
7.3 KiB
Dart
// Author: fruit.com.cn
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import '../../../core/theme/app_colors.dart';
|
|
import '../../../core/theme/app_dimens.dart';
|
|
import '../../../core/widgets/app_press_scale.dart';
|
|
import '../../../l10n/app_localizations.dart';
|
|
import '../mine_types.dart';
|
|
|
|
const int _gridColumnCount = 4;
|
|
const double _gridMaxTextScale = 1.2;
|
|
|
|
/// 「我的」页统一操作宫格:常用入口与服务入口按 4×2 排列。
|
|
class MineActionGrid extends StatelessWidget {
|
|
const MineActionGrid({
|
|
super.key,
|
|
required this.primaryActions,
|
|
required this.serviceActions,
|
|
});
|
|
|
|
final MinePrimaryActions primaryActions;
|
|
final MineServiceActions serviceActions;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l10n = AppLocalizations.of(context)!;
|
|
final items = [..._primaryItems(l10n), ..._serviceItems(l10n)];
|
|
return SizedBox(
|
|
height: AppDimens.mineGridHeight,
|
|
child: DecoratedBox(
|
|
key: const Key('mine-menu'),
|
|
decoration: BoxDecoration(
|
|
color: AppColors.mineGridSurface,
|
|
border: Border.all(color: AppColors.glassHairline),
|
|
borderRadius: BorderRadius.circular(AppDimens.mineGridRadius),
|
|
),
|
|
child: ClipRRect(
|
|
borderRadius: BorderRadius.circular(AppDimens.mineGridRadius),
|
|
child: MediaQuery.withClampedTextScaling(
|
|
maxScaleFactor: _gridMaxTextScale,
|
|
child: Column(
|
|
children: [
|
|
_row(items.take(_gridColumnCount).toList()),
|
|
const _MineHorizontalDivider(),
|
|
_row(items.skip(_gridColumnCount).toList()),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
List<_MineActionItem> _primaryItems(AppLocalizations l10n) {
|
|
return [
|
|
_MineActionItem(
|
|
key: const Key('mine-wallet'),
|
|
icon: Icons.account_balance_wallet_outlined,
|
|
label: l10n.wallet,
|
|
onTap: primaryActions.onWallet,
|
|
),
|
|
_MineActionItem(
|
|
key: const Key('mine-favorites'),
|
|
icon: Icons.star_outline_rounded,
|
|
label: l10n.myFavorites,
|
|
onTap: primaryActions.onFavorites,
|
|
),
|
|
_MineActionItem(
|
|
key: const Key('mine-downloads'),
|
|
icon: Icons.download_for_offline_outlined,
|
|
label: l10n.offlineDownloads,
|
|
onTap: primaryActions.onDownloads,
|
|
),
|
|
_MineActionItem(
|
|
key: const Key('mine-history'),
|
|
icon: Icons.history_rounded,
|
|
label: l10n.watchHistory,
|
|
onTap: primaryActions.onHistory,
|
|
),
|
|
];
|
|
}
|
|
|
|
List<_MineActionItem> _serviceItems(AppLocalizations l10n) {
|
|
return [
|
|
_MineActionItem(
|
|
key: const Key('mine-help'),
|
|
icon: Icons.support_agent_outlined,
|
|
label: l10n.contactSupport,
|
|
onTap: () => serviceActions.onOpen(MineDestination.help),
|
|
),
|
|
_MineActionItem(
|
|
key: const Key('mine-language'),
|
|
icon: Icons.language_rounded,
|
|
label: l10n.language,
|
|
onTap: serviceActions.onLanguage,
|
|
),
|
|
_MineActionItem(
|
|
key: const Key('mine-settings'),
|
|
icon: Icons.settings_outlined,
|
|
label: l10n.settings,
|
|
onTap: () => serviceActions.onOpen(MineDestination.settings),
|
|
),
|
|
_MineActionItem(
|
|
key: const Key('mine-notices'),
|
|
icon: Icons.notifications_none_rounded,
|
|
label: l10n.notices,
|
|
onTap: () => serviceActions.onOpen(MineDestination.notices),
|
|
),
|
|
];
|
|
}
|
|
|
|
Widget _row(List<_MineActionItem> items) {
|
|
return SizedBox(
|
|
height: AppDimens.mineGridCellHeight,
|
|
child: Row(
|
|
children: [
|
|
for (var index = 0; index < items.length; index++) ...[
|
|
Expanded(child: _MineActionCell(item: items[index])),
|
|
if (index < items.length - 1) const _MineVerticalDivider(),
|
|
],
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 宫格第一排的常用功能回调。
|
|
class MinePrimaryActions {
|
|
const MinePrimaryActions({
|
|
required this.onWallet,
|
|
required this.onFavorites,
|
|
required this.onDownloads,
|
|
required this.onHistory,
|
|
});
|
|
|
|
final VoidCallback onWallet;
|
|
final VoidCallback onFavorites;
|
|
final VoidCallback onDownloads;
|
|
final VoidCallback onHistory;
|
|
}
|
|
|
|
/// 宫格第二排的服务功能回调。
|
|
class MineServiceActions {
|
|
const MineServiceActions({required this.onOpen, required this.onLanguage});
|
|
|
|
final ValueChanged<MineDestination> onOpen;
|
|
final VoidCallback onLanguage;
|
|
}
|
|
|
|
class _MineActionItem {
|
|
const _MineActionItem({
|
|
required this.key,
|
|
required this.icon,
|
|
required this.label,
|
|
required this.onTap,
|
|
});
|
|
|
|
final Key key;
|
|
final IconData icon;
|
|
final String label;
|
|
final VoidCallback onTap;
|
|
|
|
Key get iconSurfaceKey =>
|
|
ValueKey('${(key as ValueKey<String>).value}-icon-surface');
|
|
}
|
|
|
|
class _MineActionCell extends StatelessWidget {
|
|
const _MineActionCell({required this.item});
|
|
|
|
final _MineActionItem item;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Material(
|
|
color: AppColors.transparent,
|
|
child: InkWell(
|
|
key: item.key,
|
|
onTap: item.onTap,
|
|
child: AppPressScale(
|
|
child: Semantics(
|
|
button: true,
|
|
label: item.label,
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
_iconSurface(),
|
|
const SizedBox(height: AppDimens.spaceSm),
|
|
_label(),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _iconSurface() {
|
|
return Container(
|
|
key: item.iconSurfaceKey,
|
|
width: AppDimens.mineGridIconSurface,
|
|
height: AppDimens.mineGridIconSurface,
|
|
decoration: BoxDecoration(
|
|
gradient: const RadialGradient(
|
|
center: Alignment(-0.35, -0.45),
|
|
radius: 0.95,
|
|
colors: [
|
|
AppColors.mineOutline,
|
|
AppColors.contentChip,
|
|
AppColors.mineGridIconSurface,
|
|
],
|
|
),
|
|
shape: BoxShape.circle,
|
|
border: Border.all(color: AppColors.glassHairline),
|
|
boxShadow: [
|
|
const BoxShadow(
|
|
color: AppColors.shadowSoft,
|
|
blurRadius: AppDimens.spaceSm,
|
|
offset: Offset(0, AppDimens.spaceXs),
|
|
),
|
|
BoxShadow(
|
|
color: AppColors.brandRed.withValues(alpha: 0.12),
|
|
blurRadius: AppDimens.spaceSm,
|
|
),
|
|
],
|
|
),
|
|
child: Icon(
|
|
item.icon,
|
|
color: AppColors.mineGridIconAccent,
|
|
size: AppDimens.mineGridIcon,
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _label() {
|
|
return Text(
|
|
item.label,
|
|
maxLines: 2,
|
|
overflow: TextOverflow.ellipsis,
|
|
textAlign: TextAlign.center,
|
|
style: const TextStyle(
|
|
color: AppColors.mineGridLabel,
|
|
fontSize: AppDimens.mineGridLabel,
|
|
fontWeight: FontWeight.w500,
|
|
height: AppDimens.mineGridLabelLineHeight,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _MineVerticalDivider extends StatelessWidget {
|
|
const _MineVerticalDivider();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
width: AppDimens.mineGridHairline,
|
|
color: AppColors.mineGridDivider,
|
|
);
|
|
}
|
|
}
|
|
|
|
class _MineHorizontalDivider extends StatelessWidget {
|
|
const _MineHorizontalDivider();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
height: AppDimens.mineGridHairline,
|
|
color: AppColors.mineGridDivider,
|
|
);
|
|
}
|
|
}
|