import 'link_item.dart'; /// 一个分组,对应 `index.html` 里带标题的一组卡片。 class LinkSection { const LinkSection({ required this.id, required this.title, required this.items, }); /// 从 JSON 构造。 /// /// `items` 做了类型防御:字段缺失或类型不对时退化成空列表, /// 避免后端返回异常数据时整个页面直接崩掉。 factory LinkSection.fromJson(Map json) { final Object? rawItems = json['items']; final List items = rawItems is List ? rawItems .whereType>() .map(LinkItem.fromJson) .toList(growable: false) : const []; return LinkSection( id: json['id'] as String, title: json['title'] as String, items: items, ); } /// 唯一标识,同时用作锚点。 final String id; /// 分组标题。 final String title; /// 分组下的站点。 final List items; /// 站点数量。 int get itemCount => items.length; /// 序列化为 JSON。 Map toJson() => { 'id': id, 'title': title, 'items': items.map((LinkItem item) => item.toJson()).toList(growable: false), }; @override bool operator ==(Object other) { if (identical(this, other)) { return true; } if (other is! LinkSection || other.id != id || other.title != title || other.items.length != items.length) { return false; } for (int index = 0; index < items.length; index++) { if (items[index] != other.items[index]) { return false; } } return true; } @override int get hashCode => Object.hash(id, title, Object.hashAll(items)); @override String toString() => 'LinkSection(id: $id, title: $title, items: $itemCount)'; }