import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../core/theme/app_theme.dart'; import '../../models/video_item.dart'; import '../../providers/profile_provider.dart'; /// 他人公开主页(《03-接口文档》4.7/4.8,游客可看): /// 头像/昵称/简介/作品数/获赞数 + 已发布作品双列网格,点击进单视频播放页 class UserProfileScreen extends ConsumerWidget { const UserProfileScreen({super.key, required this.userId}); final int userId; @override Widget build(BuildContext context, WidgetRef ref) { final state = ref.watch(profileProvider(userId)); final notifier = ref.read(profileProvider(userId).notifier); return Scaffold( appBar: AppBar(title: Text(state.profile?.nickname ?? '用户主页')), body: state.isLoading && state.profile == null ? const Center( child: CircularProgressIndicator(color: AppColors.primary), ) : state.loadFailed ? Center( child: TextButton( onPressed: notifier.load, child: const Text('加载失败,点击重试', style: TextStyle(color: AppColors.primary)), ), ) : CustomScrollView( slivers: [ // 头部资料卡 SliverToBoxAdapter(child: _ProfileHeader(state: state)), // 作品网格 if (state.works.isEmpty) const SliverToBoxAdapter( child: Padding( padding: EdgeInsets.symmetric(vertical: 48), child: Center( child: Text('暂无作品', style: TextStyle( color: AppColors.textSecondary)), ), ), ) else SliverPadding( padding: const EdgeInsets.all(8), sliver: SliverGrid.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 8, mainAxisSpacing: 8, childAspectRatio: 0.68, ), itemCount: state.works.length, itemBuilder: (_, index) => _WorkCard(item: state.works[index]), ), ), ], ), ); } } class _ProfileHeader extends StatelessWidget { const _ProfileHeader({required this.state}); final ProfileState state; @override Widget build(BuildContext context) { final profile = state.profile; if (profile == null) return const SizedBox.shrink(); return Padding( padding: const EdgeInsets.all(16), child: Column( children: [ CircleAvatar( radius: 40, backgroundColor: AppColors.surface, backgroundImage: profile.avatarUrl.isNotEmpty ? CachedNetworkImageProvider(profile.avatarUrl) : null, child: profile.avatarUrl.isEmpty ? Text( profile.nickname.isNotEmpty ? profile.nickname.characters.first : '友', style: const TextStyle(fontSize: 28), ) : null, ), const SizedBox(height: 12), Text( profile.nickname, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w700), ), if (profile.bio.isNotEmpty) ...[ const SizedBox(height: 4), Text( profile.bio, textAlign: TextAlign.center, style: const TextStyle( color: AppColors.textSecondary, fontSize: 13, ), ), ], const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ _Stat(count: profile.videoCount, label: '作品'), const SizedBox(width: 40), _Stat(count: profile.totalLikes, label: '获赞'), ], ), ], ), ); } } class _Stat extends StatelessWidget { const _Stat({required this.count, required this.label}); final int count; final String label; @override Widget build(BuildContext context) { return Column( children: [ Text( count >= 10000 ? '${(count / 10000).toStringAsFixed(1)}w' : '$count', style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w700), ), Text( label, style: const TextStyle(color: AppColors.textSecondary, fontSize: 12), ), ], ); } } class _WorkCard extends StatelessWidget { const _WorkCard({required this.item}); final VideoItem item; @override Widget build(BuildContext context) { return GestureDetector( onTap: () => context.push('/search/play', extra: item), child: ClipRRect( borderRadius: BorderRadius.circular(8), child: Stack( fit: StackFit.expand, children: [ Container( color: AppColors.surface, child: item.coverUrl.isNotEmpty ? CachedNetworkImage( imageUrl: item.coverUrl, fit: BoxFit.cover, errorWidget: (_, __, ___) => const Icon( Icons.play_circle_outline, color: AppColors.textWeak, size: 40, ), ) : const Icon( Icons.play_circle_outline, color: AppColors.textWeak, size: 40, ), ), // 底部标题条 Positioned( left: 0, right: 0, bottom: 0, child: Container( color: Colors.black54, padding: const EdgeInsets.all(6), child: Text( item.title.isNotEmpty ? item.title : '未命名视频', maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 12), ), ), ), ], ), ), ); } }