fix(client): 登录页 tab 滑块塌陷 + 滚动弹性(design-distill 校准)
- 胶囊 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:
@@ -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 |
Reference in New Issue
Block a user