feat(theme): system light/dark mode and Material You dynamic colors
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user