Files
ReelShort/lib/pages/following/following_grid_card.dart
T

243 lines
7.5 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/cover_play_count.dart';
import '../../core/widgets/remote_image.dart';
const double _selectMarkSize = 22;
const double _selectMarkInset = 6;
const double _selectMarkIconSize = 16;
const double _selectBorderWidth = 1.5;
class FollowingGridCard extends StatelessWidget {
const FollowingGridCard({
super.key,
required this.dramaId,
required this.title,
required this.posterUrl,
required this.viewCount,
required this.subtitle,
required this.onTap,
this.progress,
this.editing = false,
this.selected,
this.onRemove,
});
final String dramaId;
final String title;
final String posterUrl;
final int viewCount;
final String subtitle;
final VoidCallback onTap;
final double? progress;
final bool editing;
final bool? selected;
final VoidCallback? onRemove;
@override
Widget build(BuildContext context) {
return InkWell(
key: Key('following-card-$dramaId'),
borderRadius: BorderRadius.circular(AppDimens.homeCardRadius),
onTap: onTap,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AspectRatio(
key: Key('following-cover-$dramaId'),
aspectRatio: AppDimens.cardCoverAspectRatio,
child: Stack(
clipBehavior: Clip.hardEdge,
children: [
Positioned.fill(
child: ClipRRect(
borderRadius: BorderRadius.circular(
AppDimens.homeCardRadius,
),
child: RemoteImage(
posterUrl,
fit: BoxFit.cover,
showFallbackIcon: true,
fallbackColor: AppColors.contentSurface,
),
),
),
if (selected != null)
Positioned(
top: _selectMarkInset,
right: _selectMarkInset,
child: _SelectMark(
key: Key('following-select-$dramaId'),
selected: selected!,
),
)
else if (editing)
Positioned(
top: -2,
right: -2,
child: GestureDetector(
key: Key('following-remove-$dramaId'),
onTap: onRemove,
child: const CircleAvatar(
radius: 12,
backgroundColor: AppColors.contentChip,
child: Icon(
Icons.remove_rounded,
color: AppColors.textPrimary,
size: 17,
),
),
),
),
const Positioned(
left: 0,
right: 0,
bottom: 0,
child: CoverBottomScrim(),
),
Positioned(
right: AppDimens.cardPlayCountRight,
bottom: progress == null
? AppDimens.cardPlayCountBottom
: AppDimens.followingProgressHeight + 4,
child: CoverPlayCount(
key: Key('following-views-$dramaId'),
count: viewCount,
),
),
if (progress != null)
Positioned(
left: 0,
right: 0,
bottom: 0,
child: LinearProgressIndicator(
key: Key('following-progress-$dramaId'),
value: progress!.clamp(0.0, 1.0).toDouble(),
minHeight: AppDimens.followingProgressHeight,
backgroundColor: AppColors.inactiveTrack,
valueColor: const AlwaysStoppedAnimation<Color>(
AppColors.primary,
),
),
),
],
),
),
const SizedBox(height: AppDimens.cardCoverTitleGap),
Text(
title,
maxLines: 2,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.left,
style: const TextStyle(
color: AppColors.textPrimary,
fontSize: AppDimens.cardTitleFont,
fontWeight: FontWeight.w500,
height: AppDimens.cardTitleLineHeight,
),
),
const SizedBox(height: AppDimens.followingSubtitleGap),
Text(
subtitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.left,
style: const TextStyle(
color: AppColors.textTertiary,
fontSize: AppDimens.followingSubtitleFont,
),
),
],
),
);
}
}
class _SelectMark extends StatelessWidget {
const _SelectMark({super.key, required this.selected});
final bool selected;
@override
Widget build(BuildContext context) {
return Container(
width: _selectMarkSize,
height: _selectMarkSize,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: selected ? AppColors.blue : AppColors.transparent,
border: Border.all(
color: selected ? AppColors.blue : AppColors.textPrimary,
width: _selectBorderWidth,
),
),
child: selected
? const Icon(
Icons.check_rounded,
size: _selectMarkIconSize,
color: AppColors.onPrimary,
)
: null,
);
}
}
class FollowingAddCard extends StatelessWidget {
const FollowingAddCard({super.key, required this.onTap});
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return InkWell(
key: const Key('following-add-card'),
borderRadius: BorderRadius.circular(AppDimens.homeCardRadius),
onTap: onTap,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AspectRatio(
aspectRatio: AppDimens.cardCoverAspectRatio,
child: DecoratedBox(
decoration: BoxDecoration(
color: AppColors.contentSurface,
borderRadius: BorderRadius.circular(AppDimens.homeCardRadius),
),
child: const Center(
child: CustomPaint(
key: Key('following-add-icon'),
size: Size.square(AppDimens.followingAddIconSize),
painter: _FollowingPlusPainter(),
),
),
),
),
],
),
);
}
}
class _FollowingPlusPainter extends CustomPainter {
const _FollowingPlusPainter();
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = AppColors.textSecondary
..strokeWidth = AppDimens.followingAddIconStroke
..strokeCap = StrokeCap.round;
final center = size.center(Offset.zero);
canvas
..drawLine(Offset(center.dx, 0), Offset(center.dx, size.height), paint)
..drawLine(Offset(0, center.dy), Offset(size.width, center.dy), paint);
}
@override
bool shouldRepaint(covariant _FollowingPlusPainter oldDelegate) => false;
}