style(theme): 客户端切换深色视觉风格
This commit is contained in:
1 parent
295c21513c
commit
4bc04fd8ae
75 files changed
+732
-330
No files matched your search
@@ -13,6 +13,7 @@ import 'package:fruitshorts/core/data/services/auth_controller.dart';
|
||||
import 'package:fruitshorts/core/data/services/auth_session_store.dart';
|
||||
import 'package:fruitshorts/core/platform/haptics.dart';
|
||||
import 'package:fruitshorts/core/theme/app_colors.dart';
|
||||
import 'package:fruitshorts/core/theme/app_dimens.dart';
|
||||
import 'package:fruitshorts/core/theme/app_theme.dart';
|
||||
import 'package:fruitshorts/l10n/app_localizations.dart';
|
||||
import 'package:fruitshorts/pages/auth/auth_view.dart';
|
||||
@@ -131,7 +132,10 @@ void main() {
|
||||
final card = tester.widget<Container>(find.byKey(const Key('auth-card')));
|
||||
final cardDecoration = card.decoration! as BoxDecoration;
|
||||
expect(cardDecoration.color, AppColors.card);
|
||||
expect(cardDecoration.borderRadius, BorderRadius.circular(20));
|
||||
expect(
|
||||
cardDecoration.borderRadius,
|
||||
BorderRadius.circular(AppDimens.authCardRadius),
|
||||
);
|
||||
expect(cardDecoration.boxShadow, contains(AppTheme.cardShadow));
|
||||
expect(card.padding, const EdgeInsets.all(20));
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import 'package:fruitshorts/core/theme/app_theme.dart';
|
||||
import 'package:fruitshorts/core/widgets/subpage_header.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('should_render_unified_light_header_dimensions', (tester) async {
|
||||
testWidgets('should_render_unified_dark_header_dimensions', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: AppTheme.light(),
|
||||
@@ -47,7 +47,7 @@ void main() {
|
||||
)
|
||||
.decoration
|
||||
as BoxDecoration;
|
||||
expect(shadow.boxShadow!.single.color, const Color(0x14000000));
|
||||
expect(shadow.boxShadow!.single.color, AppColors.shadowSubtle);
|
||||
expect(shadow.boxShadow!.single.blurRadius, 8);
|
||||
expect(shadow.boxShadow!.single.offset, const Offset(0, 2));
|
||||
});
|
||||
@@ -75,7 +75,7 @@ void main() {
|
||||
MaterialApp(
|
||||
theme: AppTheme.light(),
|
||||
home: const Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
backgroundColor: AppColors.mediaBlack,
|
||||
body: AppSubpageHeader(
|
||||
title: 'Title',
|
||||
backKey: Key('sample-back'),
|
||||
@@ -97,8 +97,8 @@ void main() {
|
||||
matching: find.byIcon(Icons.chevron_left_rounded),
|
||||
),
|
||||
);
|
||||
expect(material.color, const Color(0x59000000));
|
||||
expect(icon.color, Colors.white);
|
||||
expect(material.color, AppColors.headerScrim);
|
||||
expect(icon.color, AppColors.onSurface);
|
||||
expect(icon.size, 22);
|
||||
expect(icon.weight, 600);
|
||||
final shadow =
|
||||
@@ -108,7 +108,7 @@ void main() {
|
||||
)
|
||||
.decoration
|
||||
as BoxDecoration;
|
||||
expect(shadow.boxShadow!.single.color, const Color(0x33000000));
|
||||
expect(shadow.boxShadow!.single.color, AppColors.shadowSoft);
|
||||
expect(shadow.boxShadow!.single.blurRadius, 8);
|
||||
});
|
||||
}
|
||||
@@ -33,7 +33,7 @@ void main() {
|
||||
);
|
||||
expect(
|
||||
tester.widget<Icon>(find.byKey(const Key('empty-state-icon'))).color,
|
||||
AppColors.blue,
|
||||
AppColors.blueSoft,
|
||||
);
|
||||
expect(
|
||||
tester
|
||||
@@ -103,7 +103,7 @@ void main() {
|
||||
expect(find.byKey(const Key('empty-state-action')), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('should_tint_the_bare_icon_with_the_light_theme', (tester) async {
|
||||
testWidgets('should_tint_the_bare_icon_with_the_dark_theme', (tester) async {
|
||||
await _pump(
|
||||
tester,
|
||||
theme: AppTheme.light(),
|
||||
@@ -111,7 +111,7 @@ void main() {
|
||||
);
|
||||
expect(
|
||||
tester.widget<Icon>(find.byKey(const Key('empty-state-icon'))).color,
|
||||
AppColors.blue,
|
||||
AppColors.blueSoft,
|
||||
);
|
||||
|
||||
await _pump(
|
||||
@@ -121,7 +121,7 @@ void main() {
|
||||
);
|
||||
expect(
|
||||
tester.widget<Icon>(find.byKey(const Key('empty-state-icon'))).color,
|
||||
AppColors.blue,
|
||||
AppColors.blueSoft,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -144,6 +144,6 @@ void main() {
|
||||
matching: find.byType(ColoredBox),
|
||||
),
|
||||
);
|
||||
expect(coloredBox.color, Colors.black);
|
||||
expect(coloredBox.color, AppColors.bar);
|
||||
});
|
||||
}
|
||||
@@ -52,9 +52,9 @@ void main() {
|
||||
final bar = tester.widget<Container>(barFinder);
|
||||
final decoration = bar.decoration as BoxDecoration;
|
||||
final border = decoration.border as Border;
|
||||
// 首页 feed 用深色导航。
|
||||
expect(decoration.color, Colors.black);
|
||||
expect(border.top.color, Colors.white12);
|
||||
// 所有页面统一深色导航。
|
||||
expect(decoration.color, AppColors.bar);
|
||||
expect(border.top.color, AppColors.border);
|
||||
expect(
|
||||
tester
|
||||
.widget<Text>(
|
||||
@@ -65,7 +65,7 @@ void main() {
|
||||
)
|
||||
.style
|
||||
?.color,
|
||||
Colors.white70,
|
||||
AppColors.navUnselected,
|
||||
);
|
||||
expect(
|
||||
tester
|
||||
@@ -100,10 +100,10 @@ void main() {
|
||||
await tester.tap(find.byKey(const Key('tab-theater')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(haptics.selectionClicks, 1);
|
||||
final lightBar = tester.widget<Container>(barFinder);
|
||||
final lightDecoration = lightBar.decoration as BoxDecoration;
|
||||
expect(lightDecoration.color, AppColors.bar.withValues(alpha: 0.95));
|
||||
expect((lightDecoration.border as Border).top.color, AppColors.border);
|
||||
final theaterBar = tester.widget<Container>(barFinder);
|
||||
final theaterDecoration = theaterBar.decoration as BoxDecoration;
|
||||
expect(theaterDecoration.color, AppColors.bar.withValues(alpha: 0.96));
|
||||
expect((theaterDecoration.border as Border).top.color, AppColors.border);
|
||||
await tester.tap(find.byKey(const Key('tab-theater')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(haptics.selectionClicks, 1);
|
||||
|
||||
@@ -328,7 +328,7 @@ void main() {
|
||||
expect(find.byKey(const Key('home-feed-pip')), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('should_restore_light_theme_after_leaving_feed_tab', (
|
||||
testWidgets('should_keep_light_status_bar_after_leaving_feed_tab', (
|
||||
tester,
|
||||
) async {
|
||||
await _pumpApp(tester);
|
||||
@@ -338,7 +338,7 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byKey(const Key('home-page-gradient')), findsOneWidget);
|
||||
expect(_hasLightStatusBar(tester), isFalse);
|
||||
expect(_hasLightStatusBar(tester), isTrue);
|
||||
});
|
||||
|
||||
testWidgets('should_switch_feed_drama_when_swiped_up', (tester) async {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:fruitshorts/core/theme/app_colors.dart';
|
||||
import 'package:fruitshorts/pages/player/widgets/loading_progress_track.dart';
|
||||
|
||||
void main() {
|
||||
@@ -28,7 +29,7 @@ void main() {
|
||||
tester
|
||||
.widget<ColoredBox>(find.byKey(const Key('progress-loading-track')))
|
||||
.color,
|
||||
Colors.white24,
|
||||
AppColors.inactiveTrack,
|
||||
);
|
||||
expect(
|
||||
tester
|
||||
@@ -36,7 +37,7 @@ void main() {
|
||||
find.byKey(const Key('progress-loading-highlight')),
|
||||
)
|
||||
.color,
|
||||
Colors.white,
|
||||
AppColors.onPrimary,
|
||||
);
|
||||
|
||||
final initialLeft = tester
|
||||
|
||||
@@ -825,7 +825,10 @@ void main() {
|
||||
for (final style in [label, synopsis, separator]) {
|
||||
expect(style.fontSize, 12);
|
||||
expect(style.fontWeight, FontWeight.w500);
|
||||
expect(style.color?.withValues(alpha: 1), AppColors.playerMuted);
|
||||
expect(
|
||||
style.color?.withValues(alpha: 1),
|
||||
AppColors.playerMuted.withValues(alpha: 1),
|
||||
);
|
||||
}
|
||||
expect(separator.color, isNot(Colors.white));
|
||||
});
|
||||
|
||||
@@ -4,13 +4,15 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:fruitshorts/core/theme/app_dimens.dart';
|
||||
|
||||
void main() {
|
||||
test('should_expose_the_k20_spacing_and_type_scale', () {
|
||||
test('should_expose_dark_theme_radius_and_spacing_scale', () {
|
||||
expect(AppDimens.pageHorizontal, 16);
|
||||
expect(AppDimens.cardRadius, 16);
|
||||
expect(AppDimens.coverRadius, 16);
|
||||
expect(AppDimens.authCardRadius, 20);
|
||||
expect(AppDimens.cardRadius, 12);
|
||||
expect(AppDimens.coverRadius, 12);
|
||||
expect(AppDimens.authCardRadius, 12);
|
||||
expect(AppDimens.buttonRadius, 10);
|
||||
expect(AppDimens.inputRadius, 10);
|
||||
expect(AppDimens.chipRadius, 6);
|
||||
expect(AppDimens.pillRadius, 999);
|
||||
expect(AppDimens.buttonRadius, 999);
|
||||
expect(AppDimens.caption, 12);
|
||||
expect(AppDimens.body, 14);
|
||||
expect(AppDimens.listTitle, 15);
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:fruitshorts/app.dart';
|
||||
import 'package:fruitshorts/core/theme/app_colors.dart';
|
||||
import 'package:fruitshorts/core/theme/app_dimens.dart';
|
||||
import 'package:fruitshorts/core/theme/app_theme.dart';
|
||||
|
||||
double _contrast(Color foreground, Color background) {
|
||||
@@ -16,44 +17,59 @@ double _contrast(Color foreground, Color background) {
|
||||
}
|
||||
|
||||
void main() {
|
||||
test('should_expose_light_warm_orange_tokens', () {
|
||||
expect(AppColors.blue, const Color(0xFFFF7A1A));
|
||||
expect(AppColors.bluePressed, const Color(0xFFFF6A00));
|
||||
expect(AppColors.secondary, const Color(0xFFFF6A8A));
|
||||
expect(AppColors.ink, const Color(0xFFFFF8F0));
|
||||
expect(AppColors.surfaceTop, const Color(0xFFFFE9D6));
|
||||
expect(AppColors.card, const Color(0xFFFFFFFF));
|
||||
expect(AppColors.onSurface, const Color(0xFF1F1B16));
|
||||
expect(AppColors.muted, const Color(0xFF8C837B));
|
||||
expect(AppColors.subtle, const Color(0xFFB3AAA1));
|
||||
expect(AppColors.border, const Color(0xFFF0E4D6));
|
||||
expect(AppColors.badgePink, const Color(0xFFFFE3EC));
|
||||
expect(AppColors.badgePinkText, const Color(0xFFFF6A8A));
|
||||
test('should_expose_dark_visual_tokens', () {
|
||||
expect(AppColors.blue, const Color(0xFFFF6B2C));
|
||||
expect(AppColors.bluePressed, const Color(0xFFE55A1F));
|
||||
expect(AppColors.secondary, const Color(0xFF22D3EE));
|
||||
expect(AppColors.ink, const Color(0xFF0B1020));
|
||||
expect(AppColors.card, const Color(0xFF151B2E));
|
||||
expect(AppColors.raised, const Color(0xFF1C2436));
|
||||
expect(AppColors.border, const Color(0x0FFFFFFF));
|
||||
expect(AppColors.onSurface, const Color(0xEBFFFFFF));
|
||||
expect(AppColors.muted, const Color(0xA3FFFFFF));
|
||||
expect(AppColors.subtle, const Color(0x61FFFFFF));
|
||||
expect(AppDimens.cardRadius, 12);
|
||||
expect(AppDimens.buttonRadius, 10);
|
||||
expect(AppDimens.inputRadius, 10);
|
||||
expect(AppDimens.chipRadius, 6);
|
||||
});
|
||||
|
||||
test('should_keep_primary_text_readable_on_warm_background', () {
|
||||
test('should_keep_primary_and_secondary_text_readable_on_dark', () {
|
||||
expect(
|
||||
_contrast(AppColors.onSurface, AppColors.ink),
|
||||
greaterThanOrEqualTo(4.5),
|
||||
);
|
||||
expect(
|
||||
_contrast(AppColors.onSurface, AppColors.card),
|
||||
greaterThanOrEqualTo(4.5),
|
||||
);
|
||||
expect(_contrast(AppColors.muted, AppColors.card), greaterThanOrEqualTo(3));
|
||||
expect(_contrast(AppColors.gold, AppColors.ink), greaterThanOrEqualTo(3));
|
||||
expect(_contrast(AppColors.blue, AppColors.ink), greaterThanOrEqualTo(3));
|
||||
});
|
||||
|
||||
test('should_use_light_color_scheme_and_dark_system_icons', () {
|
||||
final theme = AppTheme.light();
|
||||
test('should_use_dark_color_scheme_and_light_system_icons', () {
|
||||
final theme = AppTheme.dark();
|
||||
|
||||
expect(theme.brightness, Brightness.light);
|
||||
expect(theme.brightness, Brightness.dark);
|
||||
expect(theme.colorScheme.primary, AppColors.blue);
|
||||
expect(theme.colorScheme.onPrimary, AppColors.onPrimary);
|
||||
expect(theme.colorScheme.secondary, AppColors.secondary);
|
||||
expect(theme.colorScheme.surface, AppColors.ink);
|
||||
expect(theme.colorScheme.onSurface, AppColors.onSurface);
|
||||
expect(theme.colorScheme.outline, AppColors.border);
|
||||
expect(theme.scaffoldBackgroundColor, AppColors.ink);
|
||||
expect(theme.appBarTheme.systemOverlayStyle, SystemUiOverlayStyle.dark);
|
||||
expect(theme.appBarTheme.backgroundColor, AppColors.card);
|
||||
expect(theme.appBarTheme.systemOverlayStyle, SystemUiOverlayStyle.light);
|
||||
expect(theme.cardTheme.elevation, 0);
|
||||
expect(theme.dialogTheme.backgroundColor, AppColors.raised);
|
||||
expect(theme.bottomNavigationBarTheme.backgroundColor, AppColors.bar);
|
||||
expect(theme.inputDecorationTheme.fillColor, AppColors.input);
|
||||
});
|
||||
|
||||
test('should_use_ink_navigation_bar_without_contrast_scrim', () {
|
||||
test('should_use_ink_navigation_bar_with_light_icons', () {
|
||||
expect(appSystemOverlayStyle.statusBarColor, Colors.transparent);
|
||||
expect(appSystemOverlayStyle.statusBarIconBrightness, Brightness.light);
|
||||
expect(appSystemOverlayStyle.systemNavigationBarColor, AppColors.ink);
|
||||
expect(
|
||||
appSystemOverlayStyle.systemNavigationBarDividerColor,
|
||||
@@ -61,20 +77,20 @@ void main() {
|
||||
);
|
||||
expect(
|
||||
appSystemOverlayStyle.systemNavigationBarIconBrightness,
|
||||
Brightness.dark,
|
||||
Brightness.light,
|
||||
);
|
||||
expect(appSystemOverlayStyle.systemNavigationBarContrastEnforced, isFalse);
|
||||
|
||||
final sheetTheme = AppTheme.light().bottomSheetTheme;
|
||||
final sheetTheme = AppTheme.dark().bottomSheetTheme;
|
||||
expect(sheetTheme.backgroundColor, Colors.transparent);
|
||||
expect(sheetTheme.modalBackgroundColor, Colors.transparent);
|
||||
expect(sheetTheme.surfaceTintColor, Colors.transparent);
|
||||
});
|
||||
|
||||
test('should_expose_page_gradient_and_soft_card_shadow', () {
|
||||
test('should_expose_flat_page_gradient_without_card_shadow', () {
|
||||
expect(AppTheme.pageGradient.colors, [AppColors.surfaceTop, AppColors.ink]);
|
||||
expect(AppTheme.cardShadow.color, AppColors.cardShadow);
|
||||
expect(AppTheme.cardShadow.blurRadius, 18);
|
||||
expect(AppTheme.cardShadow.offset, const Offset(0, 6));
|
||||
expect(AppTheme.cardShadow.blurRadius, 0);
|
||||
expect(AppTheme.cardShadow.offset, Offset.zero);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:fruitshorts/core/data/remote/api_client.dart';
|
||||
import 'package:fruitshorts/core/data/remote/auth_api.dart';
|
||||
import 'package:fruitshorts/core/data/services/auth_controller.dart';
|
||||
import 'package:fruitshorts/core/data/services/auth_session_store.dart';
|
||||
import 'package:fruitshorts/core/theme/app_colors.dart';
|
||||
import 'package:fruitshorts/core/theme/app_theme.dart';
|
||||
import 'package:fruitshorts/l10n/app_localizations.dart';
|
||||
import 'package:fruitshorts/pages/watch_records/watch_records_page.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:shared_preferences_platform_interface/in_memory_shared_preferences_async.dart';
|
||||
import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart';
|
||||
|
||||
import '../support/fake_http_adapter.dart';
|
||||
|
||||
void main() {
|
||||
setUp(() {
|
||||
SharedPreferencesAsyncPlatform.instance =
|
||||
InMemorySharedPreferencesAsync.empty();
|
||||
});
|
||||
|
||||
testWidgets('should_render_material_app_with_dark_default_theme', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: AppTheme.dark(),
|
||||
darkTheme: AppTheme.dark(),
|
||||
themeMode: ThemeMode.dark,
|
||||
locale: const Locale('en'),
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: Scaffold(
|
||||
key: const Key('dark-smoke-scaffold'),
|
||||
appBar: AppBar(title: const Text('Dark')),
|
||||
body: Card(
|
||||
key: const Key('dark-smoke-card'),
|
||||
child: ListTile(
|
||||
title: Text(
|
||||
'Primary',
|
||||
style: TextStyle(color: AppColors.onSurface),
|
||||
),
|
||||
subtitle: Text('Muted', style: TextStyle(color: AppColors.muted)),
|
||||
trailing: Text('128', style: TextStyle(color: AppColors.gold)),
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: NavigationBar(
|
||||
selectedIndex: 0,
|
||||
destinations: const [
|
||||
NavigationDestination(icon: Icon(Icons.home), label: 'Home'),
|
||||
NavigationDestination(icon: Icon(Icons.person), label: 'Mine'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final context = tester.element(
|
||||
find.byKey(const Key('dark-smoke-scaffold')),
|
||||
);
|
||||
final theme = Theme.of(context);
|
||||
expect(theme.brightness, Brightness.dark);
|
||||
expect(theme.scaffoldBackgroundColor, AppColors.ink);
|
||||
expect(theme.appBarTheme.backgroundColor, AppColors.card);
|
||||
expect(theme.cardTheme.color, AppColors.card);
|
||||
|
||||
final card = tester.widget<Card>(find.byKey(const Key('dark-smoke-card')));
|
||||
expect(card.color ?? theme.cardTheme.color, AppColors.card);
|
||||
expect(find.text('Primary'), findsOneWidget);
|
||||
expect(find.text('Muted'), findsOneWidget);
|
||||
expect(find.text('128'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should_render_watch_records_page_on_dark_surface', (
|
||||
tester,
|
||||
) async {
|
||||
final auth = AuthController(
|
||||
AuthApi(
|
||||
ApiClient(
|
||||
baseUrl: 'http://127.0.0.1:1',
|
||||
adapter: FakeHttpAdapter(
|
||||
(_) async =>
|
||||
FakeHttpResponse('{"code":0,"message":"ok","data":null}'),
|
||||
),
|
||||
),
|
||||
),
|
||||
AuthSessionStore(),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
ChangeNotifierProvider<AuthController>.value(
|
||||
value: auth,
|
||||
child: MaterialApp(
|
||||
theme: AppTheme.dark(),
|
||||
darkTheme: AppTheme.dark(),
|
||||
themeMode: ThemeMode.dark,
|
||||
locale: const Locale('en'),
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: const WatchRecordsPage(),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final scaffold = tester.widget<Scaffold>(
|
||||
find.byKey(const Key('watch-records-page')),
|
||||
);
|
||||
expect(scaffold.backgroundColor, AppColors.ink);
|
||||
expect(find.byKey(const Key('auth-gate-panel')), findsOneWidget);
|
||||
expect(find.byType(Scaffold), findsOneWidget);
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user