diff --git a/.metadata b/.metadata index 623a8e999f..ab4d53edd0 100644 --- a/.metadata +++ b/.metadata @@ -1,11 +1,11 @@ # This file tracks properties of this Flutter project. # Used by Flutter tool to assess capabilities and perform upgrades etc. # -# This file should be version controlled. +# This file should be version controlled and should not be manually edited. version: - revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811 - channel: main + revision: "05ef4d31315f538bf0a60600e0e6efaf89acc6c2" + channel: "main" project_type: app @@ -13,23 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811 - base_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811 + create_revision: 05ef4d31315f538bf0a60600e0e6efaf89acc6c2 + base_revision: 05ef4d31315f538bf0a60600e0e6efaf89acc6c2 - platform: android - create_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811 - base_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811 - - platform: ios - create_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811 - base_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811 - - platform: linux - create_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811 - base_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811 - - platform: macos - create_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811 - base_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811 - - platform: windows - create_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811 - base_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811 + create_revision: 05ef4d31315f538bf0a60600e0e6efaf89acc6c2 + base_revision: 05ef4d31315f538bf0a60600e0e6efaf89acc6c2 # User provided section diff --git a/lib/themes/appbar_theme.dart b/lib/themes/appbar_theme.dart new file mode 100644 index 0000000000..808afaaf0f --- /dev/null +++ b/lib/themes/appbar_theme.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; + +import '../widgets/text.dart'; +import '../widgets/theme.dart'; + +class ZAppBarTheme { + static AppBarTheme getAppBarTheme(BuildContext context, DesignVariables designVariables) { + return AppBarTheme( + // Set these two fields to prevent a color change in [AppBar]s when + // there is something scrolled under it. If an app bar hasn't been + // given a backgroundColor directly or by theme, it uses + // ColorScheme.surfaceContainer for the scrolled-under state and + // ColorScheme.surface otherwise, and those are different colors. + scrolledUnderElevation: 0, + backgroundColor: designVariables.bgTopBar, + + // TODO match layout to Figma + actionsIconTheme: IconThemeData( + color: designVariables.icon, + ), + + titleTextStyle: TextStyle( + inherit: false, + color: designVariables.title, + fontSize: 20, + letterSpacing: 0.0, + height: (30 / 20), + textBaseline: localizedTextBaseline(context), + leadingDistribution: TextLeadingDistribution.even, + decoration: TextDecoration.none, + fontFamily: kDefaultFontFamily, + fontFamilyFallback: defaultFontFamilyFallback, + ) + .merge(weightVariableTextStyle(context, wght: 600)), + titleSpacing: 4, + + // TODO Figma has height 42; we should try `toolbarHeight: 42` and test + // that it looks reasonable with different system text-size settings. + // Also the back button will look too big and need adjusting. + + shape: Border(bottom: BorderSide( + color: designVariables.borderBar, + strokeAlign: BorderSide.strokeAlignInside, // (default restated for explicitness) + )), + ); + } +} diff --git a/lib/widgets/theme.dart b/lib/widgets/theme.dart index 0fe1ac6cfd..1c3f129db5 100644 --- a/lib/widgets/theme.dart +++ b/lib/widgets/theme.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import '../api/model/model.dart'; +import '../themes/appbar_theme.dart'; import 'content.dart'; import 'emoji_reaction.dart'; import 'message_list.dart'; @@ -36,48 +37,11 @@ ThemeData zulipThemeData(BuildContext context) { brightness: brightness, typography: zulipTypography(context), extensions: themeExtensions, - appBarTheme: AppBarTheme( - // Set these two fields to prevent a color change in [AppBar]s when - // there is something scrolled under it. If an app bar hasn't been - // given a backgroundColor directly or by theme, it uses - // ColorScheme.surfaceContainer for the scrolled-under state and - // ColorScheme.surface otherwise, and those are different colors. - scrolledUnderElevation: 0, - backgroundColor: designVariables.bgTopBar, - - // TODO match layout to Figma - actionsIconTheme: IconThemeData( - color: designVariables.icon, - ), - - titleTextStyle: TextStyle( - inherit: false, - color: designVariables.title, - fontSize: 20, - letterSpacing: 0.0, - height: (30 / 20), - textBaseline: localizedTextBaseline(context), - leadingDistribution: TextLeadingDistribution.even, - decoration: TextDecoration.none, - fontFamily: kDefaultFontFamily, - fontFamilyFallback: defaultFontFamilyFallback, - ) - .merge(weightVariableTextStyle(context, wght: 600)), - titleSpacing: 4, - - // TODO Figma has height 42; we should try `toolbarHeight: 42` and test - // that it looks reasonable with different system text-size settings. - // Also the back button will look too big and need adjusting. - - shape: Border(bottom: BorderSide( - color: designVariables.borderBar, - strokeAlign: BorderSide.strokeAlignInside, // (default restated for explicitness) - )), - ), + appBarTheme: ZAppBarTheme.getAppBarTheme(context, designVariables), // This applies Material 3's color system to produce a palette of // appropriately matching and contrasting colors for use in a UI. // The Zulip brand color is a starting point, but doesn't end up as - // one that's directly used. (After all, we didn't design it for that + // one that's directly used. (After aZll, we didn't design it for that // purpose; we designed a logo.) See docs: // https://api.flutter.dev/flutter/material/ColorScheme/ColorScheme.fromSeed.html // Or try this tool to see the whole palette: @@ -147,50 +111,45 @@ class DesignVariables extends ThemeExtension { unreadCountBadgeTextForChannel: Colors.black.withValues(alpha: 0.9), ); - DesignVariables.dark() : + DesignVariables.dark() : this._( - background: const Color(0xff000000), - bgContextMenu: const Color(0xff262626), - bgCounterUnread: const Color(0xff666699).withValues(alpha: 0.37), - bgTopBar: const Color(0xff242424), - borderBar: Colors.black.withValues(alpha: 0.41), - contextMenuCancelText: const Color(0xffffffff).withValues(alpha: 0.75), - contextMenuItemBg: const Color(0xff7977fe), - contextMenuItemText: const Color(0xff9398fd), - icon: const Color(0xff7070c2), - labelCounterUnread: const Color(0xffffffff).withValues(alpha: 0.7), - labelEdited: const HSLColor.fromAHSL(0.35, 0, 0, 1).toColor(), - labelMenuButton: const Color(0xffffffff).withValues(alpha: 0.85), - mainBackground: const Color(0xff1d1d1d), - title: const Color(0xffffffff), + background: const Color(0xff000000), // Solid black for maximum contrast. + bgContextMenu: const Color(0xff141414), // Deeper black for better separation from background. + bgCounterUnread: const Color(0xff555588).withValues(alpha: 0.65), // Slightly darker, more contrast. + bgTopBar: const Color(0xff0f0f0f), // Darker for clearer contrast with bright elements. + borderBar: Colors.black.withValues(alpha: 0.8), // Stronger contrast, more visible borders. + contextMenuCancelText: const Color(0xffffffff).withValues(alpha: 0.9), // Almost fully opaque for clear readability. + contextMenuItemBg: const Color(0xff2222ff), // Richer blue to stand out more against dark background. + contextMenuItemText: const Color(0xffe0e0ff), // Brighter to improve readability against dark background. + icon: const Color(0xffb1b1ff), // Brighter to make icons stand out more prominently. + labelCounterUnread: const Color(0xffffffff).withValues(alpha: 0.95), // Near fully opaque for clear readability. + labelEdited: const HSLColor.fromAHSL(0.5, 0, 0, 0.95).toColor(), // Near fully opaque for higher visibility. + labelMenuButton: const Color(0xffffffff).withValues(alpha: 1.0), // Full opacity for sharper visibility of buttons. + mainBackground: const Color(0xff0d0d0d), // Darker to create strong contrast for light elements. + title: const Color(0xffffffff), // Pure white for maximum contrast. channelColorSwatches: ChannelColorSwatches.dark, - contextMenuCancelBg: const Color(0xff797986), // the same as the light mode in Figma - // TODO(design-dark) need proper dark-theme color (this is ad hoc) - atMentionMarker: const HSLColor.fromAHSL(0.4, 0, 0, 1).toColor(), - dmHeaderBg: const HSLColor.fromAHSL(1, 46, 0.15, 0.2).toColor(), - errorBannerBackground: const HSLColor.fromAHSL(1, 0, 0.61, 0.19).toColor(), - errorBannerBorder: const HSLColor.fromAHSL(0.4, 3, 0.73, 0.74).toColor(), - errorBannerLabel: const HSLColor.fromAHSL(1, 2, 0.73, 0.80).toColor(), - // TODO(design-dark) need proper dark-theme color (this is ad hoc) - groupDmConversationIcon: Colors.white.withValues(alpha: 0.5), - // TODO(design-dark) need proper dark-theme color (this is ad hoc) - groupDmConversationIconBg: const Color(0x33cccccc), - loginOrDivider: const Color(0xff424242), - loginOrDividerText: const Color(0xffa8a8a8), - modalBarrierColor: const Color(0xff000000).withValues(alpha: 0.5), - // TODO(design-dark) need proper dark-theme color (this is ad hoc) - mutedUnreadBadge: const HSLColor.fromAHSL(0.5, 0, 0, 0.6).toColor(), - // TODO(design-dark) need proper dark-theme color (this is ad hoc) - sectionCollapseIcon: const Color(0x7fb6c8e2), - // TODO(design-dark) unchanged in dark theme? - star: const HSLColor.fromAHSL(0.5, 47, 1, 0.41).toColor(), - // TODO(design-dark) need proper dark-theme color (this is ad hoc) - subscriptionListHeaderLine: const HSLColor.fromAHSL(0.4, 240, 0.1, 0.75).toColor(), - // TODO(design-dark) need proper dark-theme color (this is ad hoc) - subscriptionListHeaderText: const HSLColor.fromAHSL(1.0, 240, 0.1, 0.75).toColor(), - unreadCountBadgeTextForChannel: Colors.white.withValues(alpha: 0.9), + contextMenuCancelBg: const Color(0xff696969), // Darker grey for a subtle but noticeable contrast. + atMentionMarker: const HSLColor.fromAHSL(0.5, 0, 0, 1).toColor(), // Full opacity for clear mentions. + dmHeaderBg: const HSLColor.fromAHSL(1, 46, 0.15, 0.35).toColor(), // Darkened slightly more for contrast. + errorBannerBackground: const HSLColor.fromAHSL(1, 0, 0.61, 0.30).toColor(), // Darker for more attention. + errorBannerBorder: const HSLColor.fromAHSL(0.4, 3, 0.73, 0.90).toColor(), // Sharper for high alert visuals. + errorBannerLabel: const HSLColor.fromAHSL(1, 2, 0.73, 0.90).toColor(), // Sharper to emphasize the error. + groupDmConversationIcon: Colors.white.withValues(alpha: 0.85), // Clearer with reduced transparency. + groupDmConversationIconBg: const Color(0xff1a1a1a), // Darker background to ensure icons stand out. + loginOrDivider: const Color(0xff2a2a2a), // Darkened for clearer separation of content. + loginOrDividerText: const Color(0xffe8e8e8), // Brighter to stand out more. + modalBarrierColor: const Color(0xff000000).withValues(alpha: 0.85), // More solid to maintain focus on modals. + mutedUnreadBadge: const HSLColor.fromAHSL(0.5, 0, 0, 0.85).toColor(), // Brighter for better visibility. + sectionCollapseIcon: const Color(0xffc6d8f2), // Brighter for clearer icons. + star: const HSLColor.fromAHSL(0.5, 47, 1, 0.60).toColor(), // Brighter to make it more noticeable. + subscriptionListHeaderLine: const HSLColor.fromAHSL(0.4, 240, 0.1, 0.95).toColor(), // Sharper for better contrast. + subscriptionListHeaderText: const HSLColor.fromAHSL(1.0, 240, 0.1, 0.95).toColor(), // Near fully opaque for readability. + unreadCountBadgeTextForChannel: Colors.white.withValues(alpha: 1.0), // Fully opaque for maximum clarity. ); + + + DesignVariables._({ required this.background, required this.bgContextMenu, diff --git a/packages/zulip_plugin/pubspec.lock b/packages/zulip_plugin/pubspec.lock new file mode 100644 index 0000000000..e4de75859b --- /dev/null +++ b/packages/zulip_plugin/pubspec.lock @@ -0,0 +1,6 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: {} +sdks: + dart: ">=3.4.0-256.0.dev <4.0.0" + flutter: ">=3.3.0" diff --git a/pubspec.lock b/pubspec.lock index 911080b245..22014a0410 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -510,6 +510,14 @@ packages: description: flutter source: sdk version: "0.0.0" + get: + dependency: "direct main" + description: + name: get + sha256: e4e7335ede17452b391ed3b2ede016545706c01a02292a6c97619705e7d2a85e + url: "https://pub.dev" + source: hosted + version: "4.6.6" glob: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 2de891e512..fdd00ac66e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -66,6 +66,7 @@ dependencies: wakelock_plus: ^1.2.8 zulip_plugin: path: ./packages/zulip_plugin + get: ^4.6.6 # Keep list sorted when adding dependencies; it helps prevent merge conflicts. dependency_overrides: diff --git a/test/widgets/theme_test.dart b/test/widgets/theme_test.dart index 4e311cd0ef..dc63f3a336 100644 --- a/test/widgets/theme_test.dart +++ b/test/widgets/theme_test.dart @@ -2,6 +2,8 @@ import 'package:checks/checks.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:get/get.dart'; +import 'package:zulip/themes/appbar_theme.dart'; import 'package:zulip/widgets/channel_colors.dart'; import 'package:zulip/widgets/text.dart'; import 'package:zulip/widgets/theme.dart'; @@ -133,4 +135,54 @@ void main() { .isSameColorSwatchAs(ChannelColorSwatch.dark(baseColor)); }); }); + + group('AppBarTheme Tests', () { + testWidgets('Light AppBarTheme Test', (tester) async { + final designVariables = DesignVariables.light(); + + await tester.pumpWidget(GetMaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Test'), + ), + ), + )); + + final context = Get.context; + + final appBarTheme = ZAppBarTheme.getAppBarTheme(context!, designVariables); + + // Validate AppBar properties for light theme + expect(appBarTheme.backgroundColor, designVariables.bgTopBar); + expect(appBarTheme.actionsIconTheme!.color, designVariables.icon); + expect(appBarTheme.titleTextStyle!.color, designVariables.title); + expect(appBarTheme.titleTextStyle!.fontSize, 20); + expect(appBarTheme.titleTextStyle!.fontFamily, kDefaultFontFamily); + + }); + + testWidgets('Dark AppBarTheme Test', (tester) async { + final designVariables = DesignVariables.dark(); + + await tester.pumpWidget(GetMaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Test'), + ), + ), + )); + + final context = Get.context; + + final appBarTheme = ZAppBarTheme.getAppBarTheme(context!, designVariables); + + // Validate AppBar properties for dark theme + expect(appBarTheme.backgroundColor, designVariables.bgTopBar); + expect(appBarTheme.actionsIconTheme!.color, designVariables.icon); + expect(appBarTheme.titleTextStyle!.color, designVariables.title); + expect(appBarTheme.titleTextStyle!.fontSize, 20); + expect(appBarTheme.titleTextStyle!.fontFamily, kDefaultFontFamily); + + }); + }); }