191 lines
6.3 KiB
Dart
191 lines
6.3 KiB
Dart
// Author: fruit.com.cn
|
|
|
|
import 'dart:async';
|
|
import 'dart:ui' as ui;
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:provider/provider.dart';
|
|
|
|
import '../../../core/data/services/auth_controller.dart';
|
|
import '../../../core/theme/app_colors.dart';
|
|
import '../../../core/theme/app_dimens.dart';
|
|
import '../../../core/widgets/centered_toast.dart';
|
|
import '../../../core/widgets/remote_image.dart';
|
|
import '../../../l10n/app_localizations.dart';
|
|
|
|
const double _buttonHeight = 40;
|
|
|
|
/// 「我的」页用户区。
|
|
class MineHeader extends StatelessWidget {
|
|
const MineHeader({super.key, required this.onLogin, required this.onEdit});
|
|
|
|
final VoidCallback onLogin;
|
|
final VoidCallback onEdit;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l10n = AppLocalizations.of(context)!;
|
|
final auth = context.watch<AuthController>();
|
|
final user = auth.user;
|
|
final signedIn = auth.isSignedIn;
|
|
final nickname = signedIn && (user?.nickname.trim().isNotEmpty ?? false)
|
|
? user!.nickname.trim()
|
|
: l10n.visitor;
|
|
final uid = signedIn ? user?.uid.trim() ?? '' : '';
|
|
return Padding(
|
|
key: const Key('mine-user-row'),
|
|
padding: const EdgeInsets.symmetric(vertical: AppDimens.spaceSm),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: [
|
|
_avatar(user?.avatarUrl ?? ''),
|
|
const SizedBox(width: AppDimens.spaceMd),
|
|
Expanded(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
nickname,
|
|
key: const Key('mine-nickname'),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: const TextStyle(
|
|
fontFamily: 'Outfit',
|
|
color: AppColors.textPrimary,
|
|
fontSize: AppDimens.mineUserName,
|
|
fontWeight: FontWeight.w400,
|
|
height: AppDimens.mineUserNameLineHeight,
|
|
),
|
|
),
|
|
const SizedBox(height: AppDimens.mineUserNameUidGap),
|
|
SizedBox(
|
|
key: const Key('mine-uid'),
|
|
height: AppDimens.mineUidRowHeight,
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: [
|
|
Flexible(
|
|
child: Text(
|
|
'${l10n.accountUid}: ${uid.isEmpty ? '--' : uid}',
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: const TextStyle(
|
|
color: AppColors.textTertiary,
|
|
fontSize: AppDimens.mineUid,
|
|
),
|
|
),
|
|
),
|
|
Semantics(
|
|
button: true,
|
|
label: l10n.copyUid,
|
|
child: GestureDetector(
|
|
key: const Key('mine-uid-copy'),
|
|
behavior: HitTestBehavior.opaque,
|
|
onTap: uid.isEmpty
|
|
? null
|
|
: () => unawaited(
|
|
_copyUid(context, uid, l10n.uidCopied),
|
|
),
|
|
child: const Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: 4),
|
|
child: Icon(
|
|
Icons.copy_rounded,
|
|
color: AppColors.textTertiary,
|
|
size: AppDimens.mineCopyIcon,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(width: AppDimens.spaceSm),
|
|
_accountButton(
|
|
key: signedIn ? const Key('mine-edit') : const Key('mine-login'),
|
|
label: signedIn ? l10n.edit : l10n.logIn,
|
|
onTap: signedIn ? onEdit : onLogin,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _avatar(String path) {
|
|
return Container(
|
|
key: const Key('mine-avatar'),
|
|
width: AppDimens.mineAvatar,
|
|
height: AppDimens.mineAvatar,
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
border: Border.all(
|
|
color: AppColors.textTertiary,
|
|
width: 1.2,
|
|
),
|
|
),
|
|
child: ClipOval(
|
|
child: BackdropFilter(
|
|
filter: ui.ImageFilter.blur(sigmaX: 10, sigmaY: 10),
|
|
child: Container(
|
|
color: AppColors.textPrimary.withValues(alpha: 0.06),
|
|
alignment: Alignment.center,
|
|
child: path.isEmpty
|
|
? const Icon(
|
|
Icons.person_outline_rounded,
|
|
color: AppColors.textPrimary,
|
|
size: 30,
|
|
)
|
|
: RemoteImage(path, fit: BoxFit.cover),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _accountButton({
|
|
required Key key,
|
|
required String label,
|
|
required VoidCallback onTap,
|
|
}) {
|
|
return TextButton(
|
|
key: key,
|
|
onPressed: onTap,
|
|
style: TextButton.styleFrom(
|
|
minimumSize: const Size(0, _buttonHeight),
|
|
padding: const EdgeInsets.symmetric(horizontal: AppDimens.spaceMd),
|
|
backgroundColor: AppColors.brandRed,
|
|
foregroundColor: AppColors.onPrimary,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(_buttonHeight / 2),
|
|
),
|
|
textStyle: const TextStyle(
|
|
fontSize: AppDimens.body,
|
|
fontWeight: FontWeight.w400,
|
|
),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(label),
|
|
const SizedBox(width: 2),
|
|
const Icon(Icons.chevron_right_rounded, size: 17),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<void> _copyUid(
|
|
BuildContext context,
|
|
String uid,
|
|
String message,
|
|
) async {
|
|
await Clipboard.setData(ClipboardData(text: uid));
|
|
if (context.mounted) showCenteredToast(context, message);
|
|
}
|
|
}
|