// 模板画廊页面 — 日记模板浏览和选择 import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:nuanji_app/core/theme/app_colors.dart'; import 'package:nuanji_app/core/theme/app_radius.dart'; import 'package:nuanji_app/data/remote/api_client.dart'; import '../bloc/template_bloc.dart'; /// 模板画廊页面 — 浏览和选择日记模板 class TemplateGalleryPage extends StatefulWidget { const TemplateGalleryPage({super.key}); @override State createState() => _TemplateGalleryPageState(); } class _TemplateGalleryPageState extends State { late final TemplateBloc _bloc; @override void initState() { super.initState(); _bloc = TemplateBloc(api: context.read()); _bloc.load(); } @override void dispose() { _bloc.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final theme = Theme.of(context); final colorScheme = theme.colorScheme; return Scaffold( appBar: AppBar(title: const Text('模板画廊')), body: ListenableBuilder( listenable: _bloc, builder: (context, _) { final state = _bloc.state; if (state.isLoading) { return const Center(child: CircularProgressIndicator()); } if (state.errorMessage != null) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.error_outline, size: 48, color: colorScheme.error), const SizedBox(height: 16), FilledButton.tonal( onPressed: _bloc.load, child: const Text('重试'), ), ], ), ); } final categories = state.categories; return Column( children: [ // 分类选择器 SizedBox( height: 48, child: ListView( scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric(horizontal: 16), children: categories.map((cat) { final isSelected = cat == state.selectedCategory; return Padding( padding: const EdgeInsets.only(right: 8), child: FilterChip( selected: isSelected, label: Text(cat), onSelected: (_) => _bloc.selectCategory(cat), selectedColor: colorScheme.primaryContainer, checkmarkColor: colorScheme.primary, ), ); }).toList(), ), ), const SizedBox(height: 8), // 模板网格 Expanded( child: state.filteredTemplates.isEmpty ? const Center(child: Text('暂无模板')) : GridView.builder( padding: const EdgeInsets.all(16), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, mainAxisSpacing: 12, crossAxisSpacing: 12, childAspectRatio: 0.78, ), itemCount: state.filteredTemplates.length, itemBuilder: (context, index) { return _TemplateCard( template: state.filteredTemplates[index], ); }, ), ), ], ); }, ), ); } } /// 模板卡片 class _TemplateCard extends StatelessWidget { const _TemplateCard({required this.template}); final Template template; @override Widget build(BuildContext context) { final theme = Theme.of(context); final colorScheme = theme.colorScheme; return Card( elevation: 0, shape: RoundedRectangleBorder( borderRadius: AppRadius.mdBorder, side: BorderSide(color: colorScheme.outlineVariant), ), child: InkWell( onTap: () { // 使用模板创建日记 context.push('/editor?template=${template.id}'); }, borderRadius: AppRadius.mdBorder, child: Padding( padding: const EdgeInsets.all(16), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // 模板预览区 Container( width: 72, height: 72, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ colorScheme.primaryContainer.withValues(alpha: 0.5), AppColors.tertiary.withValues(alpha: 0.3), ], ), borderRadius: AppRadius.mdBorder, ), alignment: Alignment.center, child: Text( template.emoji, style: const TextStyle(fontSize: 36), ), ), const SizedBox(height: 12), // 模板名称 Text( template.name, style: theme.textTheme.titleSmall?.copyWith( fontWeight: FontWeight.w600, ), ), const SizedBox(height: 4), // 描述 if (template.description != null) Text( template.description!, style: theme.textTheme.bodySmall?.copyWith( color: colorScheme.onSurface.withValues(alpha: 0.5), ), maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, ), ], ), ), ), ); } }