From 23a4ec268ad38a5ee1d0247856823494854488e5 Mon Sep 17 00:00:00 2001 From: klockky Date: Sat, 4 Apr 2026 16:02:36 +0300 Subject: [PATCH] feat(theme): system light/dark mode and Material You dynamic colors --- .../screens/chats/chat_list_screen.dart | 10 +-- lib/frontend/screens/chats/chat_screen.dart | 81 ++++++++++--------- .../screens/profile/devices_screen.dart | 2 +- lib/main.dart | 42 +++++++++- 4 files changed, 87 insertions(+), 48 deletions(-) diff --git a/lib/frontend/screens/chats/chat_list_screen.dart b/lib/frontend/screens/chats/chat_list_screen.dart index 1a17ef5..439c3e0 100644 --- a/lib/frontend/screens/chats/chat_list_screen.dart +++ b/lib/frontend/screens/chats/chat_list_screen.dart @@ -502,14 +502,17 @@ class _ChatListScreenState extends State child: Stack( children: [ _buildFoldedStory( + cs, 'https://i.pravatar.cc/150?u=dasha', 0, ), _buildFoldedStory( + cs, 'https://i.pravatar.cc/150?u=mastika', 1, ), _buildFoldedStory( + cs, 'https://i.pravatar.cc/150?u=stas', 2, ), @@ -1316,7 +1319,6 @@ class _ChatListScreenState extends State Symbols.check, color: cs.onPrimary, size: 14, - weight: 600, ), ), ) @@ -1471,8 +1473,6 @@ class _ChatListScreenState extends State icon, color: isSelected ? cs.onPrimary : cs.onSurface, size: 20, - weight: 400, - fill: isSelected ? 1.0 : 0.0, ), AnimatedContainer( duration: animDur, @@ -1583,13 +1583,13 @@ class _ChatListScreenState extends State ); } - Widget _buildFoldedStory(String imageUrl, int index) { + Widget _buildFoldedStory(ColorScheme cs, String imageUrl, int index) { return Positioned( left: index * 12.0, child: Container( decoration: BoxDecoration( shape: BoxShape.circle, - border: Border.all(color: Colors.black, width: 2), + border: Border.all(color: cs.surface, width: 2), ), child: CircleAvatar( radius: 12, diff --git a/lib/frontend/screens/chats/chat_screen.dart b/lib/frontend/screens/chats/chat_screen.dart index ab8c985..e7dfa6d 100644 --- a/lib/frontend/screens/chats/chat_screen.dart +++ b/lib/frontend/screens/chats/chat_screen.dart @@ -104,18 +104,17 @@ class _ChatScreenState extends State @override Widget build(BuildContext context) { + final cs = Theme.of(context).colorScheme; return Scaffold( - backgroundColor: Colors.black, + backgroundColor: cs.surface, appBar: AppBar( - backgroundColor: const Color(0xFF1B1B1B), + backgroundColor: cs.surfaceContainerHigh, + foregroundColor: cs.onSurface, elevation: 0, surfaceTintColor: Colors.transparent, + iconTheme: IconThemeData(color: cs.onSurface), leading: IconButton( - icon: const Icon( - Symbols.arrow_back, - color: Colors.white, - weight: 400, - ), + icon: const Icon(Symbols.arrow_back, weight: 400), onPressed: () => Navigator.pop(context), ), titleSpacing: 0, @@ -129,10 +128,13 @@ class _ChatScreenState extends State else CircleAvatar( radius: 18, - backgroundColor: Colors.blueGrey, + backgroundColor: cs.primaryContainer, child: Text( widget.name.isNotEmpty ? widget.name[0].toUpperCase() : '?', - style: const TextStyle(color: Colors.white, fontSize: 12), + style: TextStyle( + color: cs.onPrimaryContainer, + fontSize: 12, + ), ), ), const SizedBox(width: 12), @@ -142,17 +144,17 @@ class _ChatScreenState extends State children: [ Text( widget.name, - style: const TextStyle( - color: Colors.white, + style: TextStyle( + color: cs.onSurface, fontSize: 16, fontWeight: FontWeight.w600, fontFamily: 'Outfit', ), ), - const Text( + Text( 'last seen recently', style: TextStyle( - color: Colors.grey, + color: cs.onSurfaceVariant, fontSize: 12, fontWeight: FontWeight.w400, ), @@ -164,15 +166,11 @@ class _ChatScreenState extends State ), actions: [ IconButton( - icon: const Icon(Symbols.call, color: Colors.white, weight: 400), + icon: const Icon(Symbols.call, weight: 400), onPressed: () {}, ), IconButton( - icon: const Icon( - Symbols.more_vert, - color: Colors.white, - weight: 400, - ), + icon: const Icon(Symbols.more_vert, weight: 400), onPressed: () {}, ), ], @@ -196,6 +194,8 @@ class _ChatScreenState extends State return AnimatedBuilder( animation: _shimmerController, builder: (context, child) { + final cs = Theme.of(context).colorScheme; + final placeholder = cs.surfaceContainerHighest; final opacity = 0.3 + (0.4 * _shimmerController.value); return ListView.builder( padding: const EdgeInsets.all(16), @@ -217,8 +217,8 @@ class _ChatScreenState extends State Container( width: 36, height: 36, - decoration: const BoxDecoration( - color: Color(0xFF2B2B2B), + decoration: BoxDecoration( + color: placeholder, shape: BoxShape.circle, ), ), @@ -231,7 +231,7 @@ class _ChatScreenState extends State width: width1, height: 10, decoration: BoxDecoration( - color: const Color(0xFF2B2B2B), + color: placeholder, borderRadius: BorderRadius.circular(5), ), ), @@ -240,7 +240,7 @@ class _ChatScreenState extends State width: width2, height: 32, decoration: BoxDecoration( - color: const Color(0xFF2B2B2B), + color: placeholder, borderRadius: BorderRadius.circular(10), ), ), @@ -250,7 +250,7 @@ class _ChatScreenState extends State width: double.infinity, height: 120, decoration: BoxDecoration( - color: const Color(0xFF2B2B2B), + color: placeholder, borderRadius: BorderRadius.circular(12), ), ), @@ -265,7 +265,7 @@ class _ChatScreenState extends State height: 16, margin: const EdgeInsets.only(right: 6), decoration: BoxDecoration( - color: const Color(0xFF2B2B2B), + color: placeholder, borderRadius: BorderRadius.circular(8), ), ), @@ -286,6 +286,8 @@ class _ChatScreenState extends State } Widget _buildInputArea(BuildContext context) { + final cs = Theme.of(context).colorScheme; + final mutedIcon = cs.onSurfaceVariant.withValues(alpha: 0.85); return SafeArea( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0), @@ -300,10 +302,13 @@ class _ChatScreenState extends State maxHeight: 180, ), decoration: BoxDecoration( - color: const Color(0xFF1B1B1B).withOpacity(0.9), + color: Color.alphaBlend( + cs.surfaceContainerHighest.withValues(alpha: 0.92), + cs.surface, + ), borderRadius: BorderRadius.circular(28), border: Border.all( - color: Colors.white.withOpacity(0.1), + color: cs.outlineVariant.withValues(alpha: 0.5), width: 0.5, ), ), @@ -313,7 +318,7 @@ class _ChatScreenState extends State children: [ Icon( Symbols.face, - color: Colors.white.withOpacity(0.7), + color: mutedIcon, size: 24, weight: 400, ), @@ -321,24 +326,24 @@ class _ChatScreenState extends State Expanded( child: TextField( controller: _messageController, - style: const TextStyle( - color: Colors.white, + style: TextStyle( + color: cs.onSurface, fontSize: 16, ), maxLines: null, keyboardType: TextInputType.multiline, textAlignVertical: TextAlignVertical.center, - decoration: const InputDecoration( + decoration: InputDecoration( hintText: 'Message', hintStyle: TextStyle( - color: Colors.grey, + color: cs.onSurfaceVariant, fontSize: 16, ), border: InputBorder.none, isDense: true, - contentPadding: EdgeInsets.symmetric( + contentPadding: const EdgeInsets.symmetric( vertical: 14, - ), // Выравниваем по Y + ), ), ), ), @@ -354,7 +359,7 @@ class _ChatScreenState extends State padding: const EdgeInsets.only(left: 12), child: Icon( Symbols.attachment, - color: Colors.white.withOpacity(0.7), + color: mutedIcon, size: 24, weight: 400, ), @@ -370,13 +375,13 @@ class _ChatScreenState extends State width: 54, height: 54, alignment: Alignment.center, - decoration: const BoxDecoration( - color: Color(0xFF2B2B2B), + decoration: BoxDecoration( + color: _hasText ? cs.primary : cs.surfaceContainerHighest, shape: BoxShape.circle, ), child: Icon( _hasText ? Symbols.send : Symbols.mic, - color: Colors.white, + color: _hasText ? cs.onPrimary : cs.onSurface, size: 24, weight: 400, ), diff --git a/lib/frontend/screens/profile/devices_screen.dart b/lib/frontend/screens/profile/devices_screen.dart index c400d60..40ab60d 100644 --- a/lib/frontend/screens/profile/devices_screen.dart +++ b/lib/frontend/screens/profile/devices_screen.dart @@ -201,7 +201,7 @@ class _DevicesScreenState extends State width: 56, height: 56, decoration: BoxDecoration( - color: const Color(0xFF15151D), + color: cs.surfaceContainerHighest, shape: BoxShape.circle, border: Border.all( color: cs.onSurface.withValues(alpha: 0.1), diff --git a/lib/main.dart b/lib/main.dart index a210687..e95dcfa 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -100,7 +100,7 @@ class KometAppState extends State { } } - ColorScheme _adjustScheme(ColorScheme base) { + ColorScheme _adjustDarkScheme(ColorScheme base) { return base.copyWith( surface: Color.alphaBlend( base.primary.withValues(alpha: 0.05), @@ -117,29 +117,63 @@ class KometAppState extends State { ); } + ColorScheme _adjustLightScheme(ColorScheme base) { + return base.copyWith( + surface: Color.alphaBlend( + base.primary.withValues(alpha: 0.06), + const Color(0xFFF5F5FA), + ), + surfaceContainerHigh: Color.alphaBlend( + base.primary.withValues(alpha: 0.08), + const Color(0xFFEAEAF2), + ), + surfaceContainerHighest: Color.alphaBlend( + base.primary.withValues(alpha: 0.11), + const Color(0xFFDEDEE8), + ), + ); + } + @override Widget build(BuildContext context) { return DynamicColorBuilder( builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) { - final baseScheme = + final lightBase = + lightDynamic ?? + ColorScheme.fromSeed( + seedColor: _fallbackSeed, + brightness: Brightness.light, + ); + final darkBase = darkDynamic ?? ColorScheme.fromSeed( seedColor: _fallbackSeed, brightness: Brightness.dark, ); - final darkScheme = _adjustScheme(baseScheme); + final lightScheme = _adjustLightScheme(lightBase); + final darkScheme = _adjustDarkScheme(darkBase); return MaterialApp( title: 'Komet', debugShowCheckedModeBanner: false, locale: _locale, + themeMode: ThemeMode.system, localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, theme: ThemeData( + useMaterial3: true, + colorScheme: lightScheme, + textTheme: GoogleFonts.interTextTheme( + ThemeData(brightness: Brightness.light).textTheme, + ), + ), + darkTheme: ThemeData( useMaterial3: true, colorScheme: darkScheme, - textTheme: GoogleFonts.interTextTheme(ThemeData.dark().textTheme), + textTheme: GoogleFonts.interTextTheme( + ThemeData(brightness: Brightness.dark).textTheme, + ), ), navigatorKey: KometApp.navigatorKey, home: const _StartupScreen(),