Files
Ichino/lib/models/user_character.dart
T
Cookiues 8dda399f76 feat: 旅行伙伴发放与编组、修复退登卡死、优化宽屏布局、加 CI
自动退出登录卡死:_performLogoutAndExit 只 pop() 一层,而解锁/收藏品页是 push
在 HomePage 之上的,退登后被弹回「风险」hub,同时 _loggingOut 仍为 true,
PopScope(canPop:false) + 返回键转圈形成死局。改为 popUntil(isFirst)。

旅行伙伴发不了:它是 ItemKind.Character = 9,与搭档 Partner = 10 是两种东西,
原先收藏品的类型列表 [1,2,3,10,11,12] 里根本没有 9。按客户端实现,角色不走
userItemList(ExportUserItems 不导出 itemKind 9),而是
upsertUserAll.userCharacterList 的 {characterId, level, awakening, useCount},
配 isNewCharacterList 一行一字符。新增发放与 5 槽 charaSlot 编组页,
含「把槽 0 复制到其他槽位」,并校验所选角色确为已拥有或本次待发放。

宽屏布局:各页只有 ConstrainedBox 限宽、没有居中容器,1440 窗口下内容贴左、
右侧大片空白。抽出 responsiveBody 统一居中;B50 预览改用按宽度适配
(InteractiveViewer constrained:false + FittedBox fitWidth),横屏下不再被
contain 缩成中间一小块,导出仍是原始 1762x1850。

CI:.github/workflows/build.yml 先跑 analyze+test 作为门槛,再产出
Windows exe、Android apk、iOS 未签名 ipa(macOS runner,无证书故不可安装)。
README 从 Flutter 模板改写为实际文档,含 path_provider/jni 与 JBR jlink
两个构建环境坑的说明。
2026-09-26 00:37:54 +08:00

69 lines
2.3 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.
/// GetUserRatingApi 之外的角色(旅行伙伴)数据。
///
/// 对应客户端 `Net.VO.Mai2.UserCharacter`:只有 4 个字段,走
/// `UpsertUserAllApi` 的 `upsertUserAll.userCharacterList`,主键是
/// `characterId`(见 VOExtensions.cs:265 的 BuildListData 比较器)。
///
/// 注意:旅行伙伴不是「搭档」。搭档是 ItemKind.Partner = 10,走 userItemList;
/// 角色是 ItemKind.Character = 9,客户端的 ExportUserItems() 并不导出它,
/// 角色的拥有状态由 userCharacterList 表达。
class UserCharacterBean {
final int characterId;
final int level;
final int awakening;
final int useCount;
const UserCharacterBean({
required this.characterId,
required this.level,
required this.awakening,
required this.useCount,
});
/// wire 上的 UserCharacter,字段与顺序与客户端声明一致。
Map<String, dynamic> toWireJson() => {
'characterId': characterId,
'level': level,
'awakening': awakening,
'useCount': useCount,
};
UserCharacterBean copyWith({int? level, int? awakening, int? useCount}) =>
UserCharacterBean(
characterId: characterId,
level: level ?? this.level,
awakening: awakening ?? this.awakening,
useCount: useCount ?? this.useCount,
);
static int _toInt(dynamic v) {
if (v == null) return 0;
if (v is int) return v;
if (v is num) return v.toInt();
if (v is String) return int.tryParse(v) ?? 0;
return 0;
}
factory UserCharacterBean.fromJson(Map<String, dynamic> json) =>
UserCharacterBean(
characterId: _toInt(json['characterId']),
level: _toInt(json['level']),
awakening: _toInt(json['awakening']),
useCount: _toInt(json['useCount']),
);
static List<UserCharacterBean> listFromResponse(Map<String, dynamic> json) {
final raw = json['userCharacterList'];
if (raw is! List) return const [];
return raw
.whereType<Map<String, dynamic>>()
.map(UserCharacterBean.fromJson)
.toList();
}
}
/// `UserDetail.charaSlot` 固定 5 个槽,槽 0 是队长
/// (客户端 CharactorSlotController.cs:27 对 i==0 用 leaderCharaSlotPrefab)。
List<int> normalizeCharaSlot(List<int> slots) =>
List<int>.generate(5, (i) => i < slots.length ? slots[i] : 0);