Files
nj/app/lib/features/stickers/bloc/sticker_bloc.dart
iven e57c3427a4 fix(app): 18 处 catch(e) 添加 debugPrint 异常日志
- parent_bloc: 6 处 (LoadChildren/BindChild/ViewJournals/ExportData/DeleteData/UnbindChild)
- search_bloc: 3 处 (SearchByMood/SearchByTag/SearchByKeyword)
- achievement_bloc: 1 处 (_fetchAchievements)
- sticker_bloc: 2 处 (_fetchPacks/fetchStickersInPack)
- template_bloc: 1 处 (_fetchTemplates)
- mood_bloc: 1 处 (_loadStats)
- home_bloc: 1 处 (_onLoadData)
- calendar_bloc: 1 处 (_onMonthChanged)
- sync_engine: 1 处 (trySync)
- weekly_page: 已有 debugPrint,无需修改
2026-06-02 23:21:16 +08:00

204 lines
5.2 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 贴纸 BLoC — 通过 API 加载贴纸包和贴纸数据
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:nuanji_app/data/remote/api_client.dart';
// ===== 模型 =====
/// 贴纸包
class StickerPack {
final String id;
final String name;
final String? description;
final String? coverImageUrl;
final int stickerCount;
final bool isFree;
final String? category;
const StickerPack({
required this.id,
required this.name,
this.description,
this.coverImageUrl,
this.stickerCount = 0,
this.isFree = true,
this.category,
});
/// 兼容旧 UI 的 emoji 封面(优先用 coverImageUrl否则用默认
String get displayCover => coverImageUrl ?? '🎨';
}
/// 单张贴纸
class Sticker {
final String id;
final String packId;
final String name;
final String imageUrl;
final String? category;
const Sticker({
required this.id,
required this.packId,
required this.name,
required this.imageUrl,
this.category,
});
}
// ===== State =====
/// 贴纸页面状态
class StickerState {
final List<StickerPack> packs;
final String selectedCategory;
final String searchQuery;
final bool isLoading;
final String? errorMessage;
const StickerState({
this.packs = const [],
this.selectedCategory = '全部',
this.searchQuery = '',
this.isLoading = false,
this.errorMessage,
});
/// 按分类 + 搜索关键词过滤贴纸包
List<StickerPack> get filteredPacks {
var result = selectedCategory == '全部'
? packs
: packs.where((p) => p.category == selectedCategory).toList();
if (searchQuery.isNotEmpty) {
final query = searchQuery.toLowerCase();
result = result.where((p) => p.name.toLowerCase().contains(query)).toList();
}
return result;
}
/// 所有分类(去重 + 加"全部"
List<String> get categories {
final cats = packs
.map((p) => p.category)
.whereType<String>()
.toSet()
.toList();
return ['全部', ...cats];
}
StickerState copyWith({
List<StickerPack>? packs,
String? selectedCategory,
String? searchQuery,
bool? isLoading,
String? errorMessage,
}) =>
StickerState(
packs: packs ?? this.packs,
selectedCategory: selectedCategory ?? this.selectedCategory,
searchQuery: searchQuery ?? this.searchQuery,
isLoading: isLoading ?? this.isLoading,
errorMessage: errorMessage,
);
}
// ===== BLoC =====
/// 贴纸 BLoC — ChangeNotifier 模式
class StickerBloc extends ChangeNotifier {
final ApiClient _api;
StickerState _state = const StickerState();
StickerState get state => _state;
StickerBloc({required ApiClient api}) : _api = api;
/// 加载贴纸包列表
void load() {
_state = _state.copyWith(isLoading: true);
notifyListeners();
_fetchPacks();
}
/// 选择分类
void selectCategory(String category) {
_state = _state.copyWith(selectedCategory: category);
notifyListeners();
}
/// 搜索贴纸包(按名称前端过滤)
void search(String query) {
_state = _state.copyWith(searchQuery: query);
notifyListeners();
}
/// 按分类加载贴纸包
void loadByCategory(String? category) {
_state = _state.copyWith(isLoading: true);
notifyListeners();
_fetchPacks(category: category);
}
Future<void> _fetchPacks({String? category}) async {
try {
final queryParams = category != null && category != '全部'
? {'category': category}
: null;
final response = await _api.get(
'/diary/sticker-packs',
queryParams: queryParams,
);
final body = response.data as Map<String, dynamic>;
final list = body['data'] as List? ?? [];
final packs = list.map((item) {
final m = item as Map<String, dynamic>;
return StickerPack(
id: m['id'] as String,
name: m['name'] as String,
description: m['description'] as String?,
coverImageUrl: m['cover_image_url'] as String?,
stickerCount: (m['sticker_count'] as num?)?.toInt() ?? 0,
isFree: m['is_free'] as bool? ?? true,
category: m['category'] as String?,
);
}).toList();
_state = _state.copyWith(isLoading: false, packs: packs);
} catch (e) {
debugPrint('StickerBloc._fetchPacks 失败: $e');
_state = _state.copyWith(
isLoading: false,
errorMessage: '加载贴纸包失败',
);
}
notifyListeners();
}
/// 获取贴纸包内的贴纸列表
Future<List<Sticker>> fetchStickersInPack(String packId) async {
try {
final response = await _api.get('/diary/sticker-packs/$packId/stickers');
final body = response.data as Map<String, dynamic>;
final list = body['data'] as List? ?? [];
return list.map((item) {
final m = item as Map<String, dynamic>;
return Sticker(
id: m['id'] as String,
packId: m['pack_id'] as String,
name: m['name'] as String,
imageUrl: m['image_url'] as String,
category: m['category'] as String?,
);
}).toList();
} catch (e) {
debugPrint('StickerBloc.fetchStickersInPack 失败: $e');
return [];
}
}
}