Files
slogan/lib/features/outfit/plan_effect_page.dart
T

126 lines
4.8 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../core/ads/ads_service.dart';
import '../../core/config/app_config.dart';
import '../../shared/widgets/empty_view.dart';
import '../../shared/widgets/loading_view.dart';
import 'outfit_provider.dart';
const _angleLabels = {
'front': '正面',
'side': '侧面',
'back': '背面',
};
/// 效果图查看:3 视角(正面/侧面/背面)切换
class PlanEffectPage extends ConsumerStatefulWidget {
final List<PlanEffectImageInfo> images;
const PlanEffectPage({super.key, required this.images});
@override
ConsumerState<PlanEffectPage> createState() => _PlanEffectPageState();
}
class _PlanEffectPageState extends ConsumerState<PlanEffectPage> {
String? _selected; // 当前角度,null = 展示全部
@override
Widget build(BuildContext context) {
final done = widget.images.where((i) => i.done).toList();
final pending = widget.images.where((i) => !i.done).toList();
final angles = widget.images.map((i) => i.angle).toSet();
final ads = ref.watch(adsServiceProvider);
return Scaffold(
appBar: AppBar(title: const Text('效果图')),
body: Column(
children: [
if (angles.length > 1)
SizedBox(
height: 48,
child: ListView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 12),
children: [
Padding(
padding: const EdgeInsets.only(right: 8),
child: ChoiceChip(
label: const Text('全部'),
selected: _selected == null,
onSelected: (_) => setState(() => _selected = null),
),
),
for (final a in angles)
Padding(
padding: const EdgeInsets.only(right: 8),
child: ChoiceChip(
label: Text(_angleLabels[a] ?? a),
selected: _selected == a,
onSelected: (_) => setState(() => _selected = a),
),
),
],
),
),
Expanded(
child: done.isEmpty
? (pending.isEmpty
? const EmptyView(
message: '暂无效果图,选定主方案后自动生成(每日限 3 次)')
: const LoadingView(text: '效果图生成中,请稍后刷新...'))
: GridView.builder(
padding: const EdgeInsets.all(12),
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
childAspectRatio: 0.8,
),
itemCount: _selected == null
? done.length
: done.where((i) => i.angle == _selected).length,
itemBuilder: (ctx, idx) {
final shown = _selected == null
? done
: done.where((i) => i.angle == _selected).toList();
final img = shown[idx];
return Card(
clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: Image.network(
AppConfig.resolveUrl(img.url),
fit: BoxFit.cover,
errorBuilder: (_, _, _) => const Icon(
Icons.broken_image_outlined,
size: 48,
color: Colors.grey),
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Text(_angleLabels[img.angle] ?? img.angle,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.bold)),
),
],
),
);
},
),
),
// 信息流广告位:广告未开通(pangleAppId 为空)不渲染
if (ads.enabled) ads.showBanner(context),
],
),
);
}
}