fix(client): 登录页 tab 滑块塌陷 + 滚动弹性(design-distill 校准)
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled

- 胶囊 tab 活动滑块高度塌成 0(FractionallySizedBox 缺 heightFactor)→ 活动态
  不可见、和背景糊一起。补 heightFactor:1.0,滑块正常填满。
- 列表无弹性回弹:SingleChildScrollView 改 ClampingScrollPhysics,大窗口放得下
  就不滚(无上下拖动/滚动条),仅窗口过矮才滚。
- 加 auth_redesign golden(900x700 明/暗),按 design-distill 截图 diff 校准:
  与原型 design/preview/auth_redesign.html 对比,结构/位置/尺寸零偏移
  (残余差异为 headless 测试缺 CJK 字体的豆腐块,真 app 字体正常)。

flutter analyze 0 error;测试通过。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-19 08:25:56 +08:00
parent 9de3a6c743
commit 7827660a36
4 changed files with 56 additions and 0 deletions
+4
View File
@@ -154,6 +154,9 @@ class _AuthScreenState extends ConsumerState<AuthScreen>
SafeArea(
child: Center(
child: SingleChildScrollView(
// 无弹性回弹:内容放得下就不滚(大窗口不出现上下拖动/滚动条),
// 仅窗口太矮放不下时才滚动。
physics: const ClampingScrollPhysics(),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 420),
@@ -277,6 +280,7 @@ class _AuthScreenState extends ConsumerState<AuthScreen>
alignment: reg ? Alignment.centerRight : Alignment.centerLeft,
child: FractionallySizedBox(
widthFactor: 0.5,
heightFactor: 1.0,
child: DecoratedBox(
decoration: BoxDecoration(
color: c.accent,
@@ -0,0 +1,52 @@
// auth_redesign_golden_test.dart — 登录页 Flutter 渲染图(供 design-distill 截图 diff)
// 900x700 @1x,与原型截图同 viewport;明/暗各一张。
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pangolin_vpn/l10n/strings_zh.dart';
import 'package:pangolin_vpn/pangolin_theme.dart';
import 'package:pangolin_vpn/services/token_store.dart';
import 'package:pangolin_vpn/state/auth_provider.dart';
import 'package:pangolin_vpn/widgets/auth_screen.dart';
class _NullTokenStore implements TokenStore {
const _NullTokenStore();
@override
Future<void> saveTokens({required String access, required String refresh}) async {}
@override
Future<String?> loadAccessToken() async => null;
@override
Future<String?> loadRefreshToken() async => null;
@override
Future<void> clear() async {}
@override
Future<void> markOnboarded() async {}
@override
Future<bool> isOnboarded() async => true;
@override
Future<void> saveLastEmail(String email) async {}
@override
Future<String?> loadLastEmail() async => null;
}
Future<void> _shoot(WidgetTester tester, ThemeData theme, String name) async {
tester.view.physicalSize = const Size(900, 700);
tester.view.devicePixelRatio = 1.0;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
await tester.pumpWidget(ProviderScope(
overrides: [tokenStoreProvider.overrideWithValue(const _NullTokenStore())],
child: MaterialApp(
debugShowCheckedModeBanner: false,
theme: theme,
home: AuthScreen(t: const StringsZh(), onDone: () {}),
),
));
await tester.pumpAndSettle();
await expectLater(find.byType(AuthScreen), matchesGoldenFile('goldens/$name.png'));
}
void main() {
testWidgets('auth login light', (t) => _shoot(t, PangolinTheme.light, 'auth_login_light'));
testWidgets('auth login dark', (t) => _shoot(t, PangolinTheme.dark, 'auth_login_dark'));
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB