Files
CloudHome-Ops/lib/widgets/store_theme_grid.dart

147 lines
4.1 KiB
Dart
Raw Normal View History

import 'package:flutter/material.dart';
import '../models/theme_template.dart';
/// 主题模板网格卡片(封面图 + 名称)。
///
/// 用于门店表单中「主题皮肤」选择;[selectedId] 高亮选中态,
/// [onSelected] 回传选中模板的 [ThemeTemplate.id]。
class StoreThemeGrid extends StatelessWidget {
const StoreThemeGrid({
super.key,
required this.templates,
required this.onSelected,
this.selectedId,
});
/// 可选主题模板列表。
final List<ThemeTemplate> templates;
/// 选中回调,回传模板 id。
final ValueChanged<String> onSelected;
/// 当前选中的模板 id(高亮)。
final String? selectedId;
@override
Widget build(BuildContext context) {
if (templates.isEmpty) {
return const SizedBox(
height: 80,
child: Center(child: Text('暂无可用主题模板')),
);
}
return GridView.count(
crossAxisCount: 3,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
mainAxisSpacing: 12,
crossAxisSpacing: 12,
childAspectRatio: 0.9,
children: templates.map((template) {
final selected = template.id == selectedId;
return _ThemeCard(
template: template,
selected: selected,
onTap: () => onSelected(template.id),
);
}).toList(),
);
}
}
class _ThemeCard extends StatelessWidget {
const _ThemeCard({
required this.template,
required this.selected,
required this.onTap,
});
final ThemeTemplate template;
final bool selected;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
final primary = _accentColor(template.name);
final coverUrl = template.coverUrl;
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: selected
? Theme.of(context).colorScheme.primary
: Colors.transparent,
width: selected ? 2.5 : 1,
),
),
child: Column(
children: [
// 封面预览(优先 coverUrl,否则用名称哈希色块)。
Expanded(
child: Container(
decoration: BoxDecoration(
color: coverUrl == null || coverUrl.isEmpty ? primary : null,
borderRadius:
const BorderRadius.vertical(top: Radius.circular(10)),
image: coverUrl != null && coverUrl.isNotEmpty
? DecorationImage(
image: NetworkImage(coverUrl),
fit: BoxFit.cover,
)
: null,
),
alignment: Alignment.center,
child: coverUrl == null || coverUrl.isEmpty
? Text(
template.name.isEmpty ? '主题' : template.name[0],
style: const TextStyle(
color: Colors.white,
fontSize: 28,
fontWeight: FontWeight.bold,
),
)
: null,
),
),
// 名称。
Container(
height: 34,
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Text(
template.name,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600),
),
),
],
),
),
);
}
static const _palette = [
Color(0xFF3B82F6),
Color(0xFF8B5CF6),
Color(0xFFEC4899),
Color(0xFFF59E0B),
Color(0xFF10B981),
Color(0xFF6366F1),
];
static Color _accentColor(String name) {
var hash = 0;
for (var i = 0; i < name.length; i++) {
hash = (hash + name.codeUnitAt(i) * (i + 1)) % _palette.length;
}
return _palette[hash];
}
}