feat(theme): system light/dark mode and Material You dynamic colors

This commit is contained in:
klockky
2026-04-04 16:02:36 +03:00
parent 9a0100f5ed
commit 23a4ec268a
4 changed files with 87 additions and 48 deletions
@@ -502,14 +502,17 @@ class _ChatListScreenState extends State<ChatListScreen>
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<ChatListScreen>
Symbols.check,
color: cs.onPrimary,
size: 14,
weight: 600,
),
),
)
@@ -1471,8 +1473,6 @@ class _ChatListScreenState extends State<ChatListScreen>
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<ChatListScreen>
);
}
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,
+43 -38
View File
@@ -104,18 +104,17 @@ class _ChatScreenState extends State<ChatScreen>
@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<ChatScreen>
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<ChatScreen>
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<ChatScreen>
),
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<ChatScreen>
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<ChatScreen>
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<ChatScreen>
width: width1,
height: 10,
decoration: BoxDecoration(
color: const Color(0xFF2B2B2B),
color: placeholder,
borderRadius: BorderRadius.circular(5),
),
),
@@ -240,7 +240,7 @@ class _ChatScreenState extends State<ChatScreen>
width: width2,
height: 32,
decoration: BoxDecoration(
color: const Color(0xFF2B2B2B),
color: placeholder,
borderRadius: BorderRadius.circular(10),
),
),
@@ -250,7 +250,7 @@ class _ChatScreenState extends State<ChatScreen>
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<ChatScreen>
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<ChatScreen>
}
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<ChatScreen>
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<ChatScreen>
children: [
Icon(
Symbols.face,
color: Colors.white.withOpacity(0.7),
color: mutedIcon,
size: 24,
weight: 400,
),
@@ -321,24 +326,24 @@ class _ChatScreenState extends State<ChatScreen>
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<ChatScreen>
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<ChatScreen>
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,
),
@@ -201,7 +201,7 @@ class _DevicesScreenState extends State<DevicesScreen>
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),
+38 -4
View File
@@ -100,7 +100,7 @@ class KometAppState extends State<KometApp> {
}
}
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<KometApp> {
);
}
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(),