import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:go_router/go_router.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:jiu_client/core/auth/auth_state.dart'; import 'package:jiu_client/core/theme/themes.dart'; import 'package:jiu_client/models/shop.dart'; import 'package:jiu_client/providers/shop_provider.dart'; import 'package:jiu_client/providers/license_provider.dart'; import 'package:jiu_client/providers/update_provider.dart'; import 'package:jiu_client/providers/connectivity_provider.dart'; import 'package:jiu_client/providers/session_heartbeat.dart'; import 'package:jiu_client/screens/shell/app_shell.dart'; /// design-distill 阶段4:app 外壳(顶栏 + 侧栏 + 内容区)golden × 三主题。 /// 锁住导航 chrome 随主题换色——这是最显眼的主题面,之前各屏 golden 都不含外壳。 /// 需 MaterialApp.router(外壳依赖 StatefulNavigationShell),故为自定义 golden。 /// 更新基准:flutter test --update-goldens test/golden/app_shell_golden_test.dart class _FakeAuth extends AuthNotifier { _FakeAuth() { state = const AuthState( user: AuthUser( accessToken: 't', refreshToken: 'r', id: 1, username: 'wang', realName: '王经理', shopNo: 'DBZB-001', shopId: 1, role: 'admin'), ); } } class _FakeUpdate extends UpdateNotifier { @override Future build() async => null; } class _FakeLicense extends LicenseNotifier { @override Future build() async => null; } const _shop = ShopInfo( id: 1, code: 'DBZB-001', name: '鼎昌酒行', address: '浙江省杭州市', phone: '0571-88886666', managerName: '王经理'); List _overrides() => [ authStateProvider.overrideWith((ref) => _FakeAuth()), shopInfoProvider.overrideWith((ref) => _shop), licenseProvider.overrideWith(() => _FakeLicense()), updateProvider.overrideWith(() => _FakeUpdate()), // 跳过联网首检 / 不启动心跳,避免后台 Timer 泄漏 connectivityProvider .overrideWith((ref) => ConnectivityNotifier(skipInit: true)), sessionHeartbeatProvider.overrideWith((ref) => SessionHeartbeat(ref)), appVersionProvider.overrideWith((ref) => 'v1.0.72'), isReadonlyProvider.overrideWithValue(false), ]; GoRouter _router() => GoRouter( initialLocation: '/inventory', routes: [ StatefulShellRoute.indexedStack( builder: (ctx, state, shell) => AppShell(navigationShell: shell), branches: [ StatefulShellBranch(routes: [ GoRoute( path: '/inventory', builder: (_, __) => const SizedBox.expand()), ]), ], ), ], ); void main() { TestWidgetsFlutterBinding.ensureInitialized(); SharedPreferences.setMockInitialValues({}); for (final theme in const ['a', 'b', 'c']) { testWidgets('app shell · theme $theme', (tester) async { tester.view.physicalSize = const Size(2560, 1600); tester.view.devicePixelRatio = 2.0; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); await tester.pumpWidget(ProviderScope( overrides: _overrides(), child: MaterialApp.router( debugShowCheckedModeBanner: false, theme: buildTheme(theme), routerConfig: _router(), ), )); await tester.pumpAndSettle(); await expectLater( find.byType(MaterialApp), matchesGoldenFile('goldens/app_shell_$theme.png'), ); }); } }