diff --git a/README.md b/README.md index ea8aaf0..a24fedf 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Luban Imager 是一个基于 Flutter 的一键图片压缩 App,目标是把优 ## 截图 -鲁班压图截图 +鲁班压图浅色与暗黑模式截图 ## 致谢 @@ -17,6 +17,7 @@ Luban Imager 是一个基于 Flutter 的一键图片压缩 App,目标是把优 - 一键压缩:选择图片后自动执行 Luban 风格压缩,不需要手动填写质量、尺寸等参数。 - 多入口导入:支持从 App 内选择图片,也支持从系统分享菜单接收图片。 - 压缩结果预览:展示原图、压缩图、前后对比三种视图,并显示尺寸、文件大小和节省比例。 +- 暗黑模式:支持跟随系统设置自动切换浅色/暗黑模式。 - 安全回退:如果压缩结果比原图更大,会自动使用原图,避免负优化。 - 分享导出:压缩完成后可通过系统分享面板导出压缩图片。 - 原位覆盖:对于系统授权可写入的图片来源,支持覆盖原图;覆盖前会进行两次确认。 diff --git a/docs/images/luban-imager-screenshot.jpg b/docs/images/luban-imager-screenshot.jpg deleted file mode 100644 index b354ee0..0000000 Binary files a/docs/images/luban-imager-screenshot.jpg and /dev/null differ diff --git a/docs/images/luban-imager-screenshot.webp b/docs/images/luban-imager-screenshot.webp new file mode 100644 index 0000000..c52924b Binary files /dev/null and b/docs/images/luban-imager-screenshot.webp differ diff --git a/lib/main.dart b/lib/main.dart index 67698e2..2cd9363 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -49,25 +49,65 @@ class _LubanImagerAppState extends State return MaterialApp( debugShowCheckedModeBanner: false, title: _appName, - theme: ThemeData( - useMaterial3: true, - colorScheme: ColorScheme.fromSeed( - seedColor: const Color(0xFF2F6F73), - brightness: Brightness.light, - ), - scaffoldBackgroundColor: const Color(0xFFF7F8F6), - appBarTheme: const AppBarTheme( - centerTitle: false, - backgroundColor: Color(0xFFF7F8F6), - foregroundColor: Color(0xFF172122), - elevation: 0, - ), - ), + theme: AppTheme.light(), + darkTheme: AppTheme.dark(), + themeMode: ThemeMode.system, home: ImagerHomePage(appName: _appName), ); } } +class AppTheme { + static const _seedColor = Color(0xFF2F6F73); + static const _lightBackground = Color(0xFFF7F8F6); + static const _darkBackground = Color(0xFF101817); + + static ThemeData light() { + final colorScheme = ColorScheme.fromSeed( + seedColor: _seedColor, + brightness: Brightness.light, + ); + return _build( + colorScheme: colorScheme, + scaffoldBackgroundColor: _lightBackground, + systemOverlayStyle: SystemUiOverlayStyle.dark, + ); + } + + static ThemeData dark() { + final colorScheme = ColorScheme.fromSeed( + seedColor: _seedColor, + brightness: Brightness.dark, + ); + return _build( + colorScheme: colorScheme, + scaffoldBackgroundColor: _darkBackground, + systemOverlayStyle: SystemUiOverlayStyle.light, + ); + } + + static ThemeData _build({ + required ColorScheme colorScheme, + required Color scaffoldBackgroundColor, + required SystemUiOverlayStyle systemOverlayStyle, + }) { + return ThemeData( + useMaterial3: true, + colorScheme: colorScheme, + scaffoldBackgroundColor: scaffoldBackgroundColor, + canvasColor: scaffoldBackgroundColor, + appBarTheme: AppBarTheme( + centerTitle: false, + backgroundColor: scaffoldBackgroundColor, + foregroundColor: colorScheme.onSurface, + surfaceTintColor: Colors.transparent, + elevation: 0, + systemOverlayStyle: systemOverlayStyle, + ), + ); + } +} + class AppName { static const english = 'Luban Imager'; static const chinese = '鲁班压图'; @@ -529,16 +569,18 @@ class _ImagerHomePageState extends State { } Widget _buildEmptyState() { + final colorScheme = Theme.of(context).colorScheme; + return Center( child: Padding( padding: const EdgeInsets.all(32), child: Column( mainAxisSize: MainAxisSize.min, children: [ - const Icon( + Icon( Icons.image_search_outlined, size: 72, - color: Color(0xFF5D6F70), + color: colorScheme.outline, ), const SizedBox(height: 20), Text( @@ -575,6 +617,7 @@ class _ImagerHomePageState extends State { final compressed = job.compressed; final canCompare = compressed != null; final mode = canCompare ? _previewMode : PreviewMode.original; + final colorScheme = Theme.of(context).colorScheme; return LayoutBuilder( builder: (context, constraints) { @@ -691,12 +734,12 @@ class _ImagerHomePageState extends State { child: Container( clipBehavior: Clip.antiAlias, decoration: BoxDecoration( - color: const Color(0xFFEDEFEA), + color: colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(8), ), foregroundDecoration: BoxDecoration( borderRadius: BorderRadius.circular(8), - border: Border.all(color: const Color(0xFFDADFD8)), + border: Border.all(color: colorScheme.outlineVariant), ), child: _buildImagePreview(job, mode), ), @@ -964,12 +1007,14 @@ class _MetricBlock extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: Colors.white, + color: colorScheme.surface, borderRadius: BorderRadius.circular(8), - border: Border.all(color: const Color(0xFFDADFD8)), + border: Border.all(color: colorScheme.outlineVariant), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -978,9 +1023,9 @@ class _MetricBlock extends StatelessWidget { label, maxLines: 1, overflow: TextOverflow.ellipsis, - style: Theme.of( - context, - ).textTheme.labelMedium?.copyWith(color: const Color(0xFF5D6F70)), + style: Theme.of(context).textTheme.labelMedium?.copyWith( + color: colorScheme.onSurfaceVariant, + ), ), const SizedBox(height: 7), Text(