feat: 主页账号备份,并修好 B50 预览缩不小
账号备份(AccountBackupCard + AccountBackupService): - 并发读 GetUserData/Extend/Option/Rating/Charge/Activity/MissionData + Character + Map(分页)+ KaleidxScope + 各 itemKind 的 GetUserItemApi, 汇成一个 JSON 字符串可复制到剪贴板。舞里程与总游玩数据本就在 userData 的 point/totalPoint/playCount 里。 - itemKind 15(KaleidxScopeKey)不列进回读清单:客户端 ExportUserItems 从不输出它,下行也没有读它的路径,钥匙实际是 userKaleidxScopeList[].isKeyFound。 - 全程只发 GetUser*Api,不碰 UpsertUserAllApi。 - 逐段容错:私服没实现某个接口时其余照样备出,失败段单独列出—— 「读到但是空的」和「没读到」在 userItemList 里按 kind 分开存,可区分。 - 取数与拼装分开,buildBackupDocument/encode 部分是纯函数,单测覆盖。 B50 预览:原因是 InteractiveViewer(constrained:false) 给孩子无界约束, FittedBox(fit: fitWidth) 在无界下根本不缩放,海报按 1762x1850 原尺寸铺开; 而 minScale 又算成 1.0,于是既巨大又缩不动。改为给一个 contain 尺寸的 有限盒子让 FittedBox 真正缩放,minScale 固定 1.0(这个版本没有 initialScale 可用)。RepaintBoundary 留在 FittedBox 内侧,绘制尺寸仍是 1762x1850,导出分辨率不受影响,并加了断言守住这点。
This commit is contained in:
1 parent
a5fb2c97cd
commit
0cb5320290
8 files changed
+524
-36
No files matched your search
+32
-21
@@ -232,30 +232,41 @@ class _Best50PageState extends State<Best50Page> {
|
||||
Expanded(
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
// 按视口宽度适配:横屏/桌面下海报占满宽度、竖向拖动看全图,
|
||||
// 而不是被 contain 缩成中间一小块、两侧大片空白。
|
||||
// minScale 再允许缩到整张可见,导出仍是原始 1762x1850。
|
||||
// 海报要按视口整体缩放一档,进来才能看见完整一张(之前的现象是
|
||||
// 「太大且缩不小」)。两处坑:
|
||||
// 1. InteractiveViewer(constrained:false) 给子节点无界约束,
|
||||
// 此时 FittedBox 根本不会缩放,必须先给一个有限大小的盒子。
|
||||
// 2. InteractiveViewer 没有 initialScale,所以「初始就缩到位」
|
||||
// 只能靠把子节点做成 contain 尺寸、minScale 固定 1.0。
|
||||
// RepaintBoundary 放在 FittedBox 内侧:它自身的绘制尺寸仍是
|
||||
// 1762x1850,导出分辨率不受这里缩放影响。
|
||||
final fitWidth =
|
||||
constraints.maxWidth / Best50Poster.canvasWidth;
|
||||
final scaledHeight = Best50Poster.canvasHeight * fitWidth;
|
||||
final containScale = constraints.maxHeight / scaledHeight;
|
||||
final minScale = containScale < 1 ? containScale : 1.0;
|
||||
final fitHeight =
|
||||
constraints.maxHeight / Best50Poster.canvasHeight;
|
||||
final contain = fitWidth < fitHeight ? fitWidth : fitHeight;
|
||||
|
||||
return InteractiveViewer(
|
||||
constrained: false,
|
||||
minScale: minScale,
|
||||
maxScale: minScale * 6,
|
||||
boundaryMargin: const EdgeInsets.all(24),
|
||||
child: FittedBox(
|
||||
fit: BoxFit.fitWidth,
|
||||
child: RepaintBoundary(
|
||||
key: _posterKey,
|
||||
child: Best50Poster(
|
||||
userName: widget.userName,
|
||||
iconId: widget.iconId,
|
||||
playerRating: widget.playerRating,
|
||||
best35: best35,
|
||||
best15: best15,
|
||||
return Center(
|
||||
child: InteractiveViewer(
|
||||
constrained: false,
|
||||
minScale: 1.0,
|
||||
maxScale: 10.0,
|
||||
boundaryMargin: const EdgeInsets.all(24),
|
||||
child: SizedBox(
|
||||
width: Best50Poster.canvasWidth * contain,
|
||||
height: Best50Poster.canvasHeight * contain,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.fill,
|
||||
child: RepaintBoundary(
|
||||
key: _posterKey,
|
||||
child: Best50Poster(
|
||||
userName: widget.userName,
|
||||
iconId: widget.iconId,
|
||||
playerRating: widget.playerRating,
|
||||
best35: best35,
|
||||
best15: best15,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -10,6 +10,7 @@ import '../models/session_model.dart';
|
||||
import '../models/user_data.dart';
|
||||
import '../models/user_preview.dart';
|
||||
import '../services/title_api_service.dart';
|
||||
import '../widgets/account_backup_card.dart';
|
||||
import '../widgets/app_notice.dart';
|
||||
import '../widgets/connection_share_card.dart';
|
||||
import 'about_page.dart';
|
||||
@@ -539,6 +540,8 @@ class _HomePageState extends State<HomePage> {
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
ConnectionShareCard(userId: widget.userId, token: widget.token),
|
||||
const SizedBox(height: 12),
|
||||
AccountBackupCard(userId: widget.userId, cookies: _session.cookies),
|
||||
if (TitleApiService.lastRawResponse != null) ...[
|
||||
const SizedBox(height: 12),
|
||||
_DebugRawJsonCard(
|
||||
|
||||
Reference in new issue
Block a user