feat:增加主题颜色和暗黑模式

This commit is contained in:
2025-11-11 20:02:09 +08:00
parent 703a5a6aeb
commit 741d6842b8
13 changed files with 446 additions and 183 deletions

BIN
fonts/custom.ttf Normal file

Binary file not shown.

View File

@@ -1,21 +1,24 @@
import 'package:flisp_app/provider/app_provider.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../provider/app_provider.dart';
class AppDrawer extends StatelessWidget { class AppDrawer extends StatelessWidget {
const AppDrawer({super.key}); const AppDrawer({super.key});
DrawerHeader buildDrawerHeader() { DrawerHeader buildDrawerHeader(BuildContext context) {
return DrawerHeader( return DrawerHeader(
decoration: BoxDecoration(color: Colors.blue.shade700), decoration: BoxDecoration(color: Theme.of(context).primaryColor),
child: const Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
CircleAvatar( CircleAvatar(
radius: 30, radius: 30,
backgroundColor: Colors.white, backgroundColor: Colors.white,
child: Icon(Icons.flash_on, color: Colors.blue, size: 40), child: Icon(
Icons.flash_on,
color: Theme.of(context).colorScheme.primary,
size: 40,
),
), ),
SizedBox(height: 10), SizedBox(height: 10),
Text( Text(
@@ -35,36 +38,170 @@ class AppDrawer extends StatelessWidget {
); );
} }
Widget _buildDarkModeSection(BuildContext context, AppProvider appProvider) {
return ListTile(
leading: Icon(
appProvider.isDarkMode ? Icons.dark_mode : Icons.light_mode,
color: Theme.of(context).colorScheme.primary,
),
title: const Text('暗黑模式'),
trailing: Switch(
value: appProvider.isDarkMode,
onChanged: (value) {
appProvider.toggleDarkMode(value);
},
),
onTap: () {
appProvider.toggleDarkMode(!appProvider.isDarkMode);
},
);
}
Widget _buildThemeColorList(BuildContext context, AppProvider appProvider) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
childAspectRatio: 1.2,
),
itemCount: appProvider.availableThemes.length,
itemBuilder: (context, index) {
final themeColor = appProvider.availableThemes[index];
final isSelected = appProvider.currentTheme == themeColor;
return _buildThemeColorItem(
themeColor: themeColor,
isSelected: isSelected,
onTap: () => appProvider.changeTheme(themeColor),
);
},
),
);
}
Widget _buildThemeColorItem({
required ThemeColor themeColor,
required bool isSelected,
required VoidCallback onTap,
}) {
return GestureDetector(
onTap: onTap,
child: Container(
decoration: BoxDecoration(
color:
isSelected
? themeColor.primaryColor.withAlpha(50)
: Colors.transparent,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: isSelected ? themeColor.primaryColor : Colors.grey.shade300,
width: isSelected ? 2 : 1,
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 颜色圆点
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: themeColor.primaryColor,
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(10),
blurRadius: 2,
offset: const Offset(0, 1),
),
],
),
),
const SizedBox(height: 4),
Text(
themeColor.name,
style: TextStyle(
fontSize: 10,
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
color:
isSelected ? themeColor.primaryColor : Colors.grey.shade600,
),
),
],
),
),
);
}
Widget _buildThemeSection(BuildContext context, AppProvider appProvider) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
child: Text('主题颜色', style: Theme.of(context).textTheme.titleMedium),
),
_buildDarkModeSection(context, appProvider),
_buildThemeColorList(context, appProvider),
],
);
}
// 构建抽屉菜单项
Widget _buildDrawerItem({
required BuildContext context,
required IconData icon,
required String title,
required VoidCallback onTap,
}) {
return ListTile(
leading: Icon(icon, color: Theme.of(context).colorScheme.primary),
title: Text(title),
onTap: onTap,
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final appProvider = Provider.of<AppProvider>(context);
return Drawer( return Drawer(
child: ListView( child: ListView(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
children: [ children: [
// 抽屉头部 // 抽屉头部
buildDrawerHeader(), buildDrawerHeader(context),
// 菜单项 // 菜单项
_buildDrawerItem( _buildDrawerItem(
context: context,
icon: Icons.flash_on, icon: Icons.flash_on,
title: '闪灵', title: '闪灵',
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
Provider.of<AppProvider>(context, listen: false).changeTab(0); appProvider.changeTab(0);
}, },
), ),
_buildDrawerItem( _buildDrawerItem(
context: context,
icon: Icons.checklist, icon: Icons.checklist,
title: '待办事项', title: '待办事项',
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
Provider.of<AppProvider>(context, listen: false).changeTab(1); appProvider.changeTab(1);
}, },
), ),
const Divider(), const Divider(),
_buildDrawerItem( _buildDrawerItem(
context: context,
icon: Icons.analytics, icon: Icons.analytics,
title: '统计', title: '统计',
onTap: () { onTap: () {
@@ -73,6 +210,7 @@ class AppDrawer extends StatelessWidget {
}, },
), ),
_buildDrawerItem( _buildDrawerItem(
context: context,
icon: Icons.archive, icon: Icons.archive,
title: '归档', title: '归档',
onTap: () { onTap: () {
@@ -83,15 +221,12 @@ class AppDrawer extends StatelessWidget {
const Divider(), const Divider(),
_buildThemeSection(context, appProvider),
const Divider(),
_buildDrawerItem( _buildDrawerItem(
icon: Icons.settings, context: context,
title: '设置',
onTap: () {
Navigator.pop(context);
// 这里可以导航到设置页面
},
),
_buildDrawerItem(
icon: Icons.help, icon: Icons.help,
title: '帮助与反馈', title: '帮助与反馈',
onTap: () { onTap: () {
@@ -103,17 +238,4 @@ class AppDrawer extends StatelessWidget {
), ),
); );
} }
// 构建抽屉菜单项
Widget _buildDrawerItem({
required IconData icon,
required String title,
required VoidCallback onTap,
}) {
return ListTile(
leading: Icon(icon, color: Colors.blue.shade600),
title: Text(title),
onTap: onTap,
);
}
} }

View File

@@ -28,20 +28,38 @@ class _MainScreenState extends State<MainScreen> {
return Scaffold( return Scaffold(
drawer: const AppDrawer(), drawer: const AppDrawer(),
appBar: AppBar( appBar: AppBar(
title: Text(_getAppBarTitle(appProvider.currentIndex)), title: Text(_getAppBarTitle(appProvider.currentTab)),
backgroundColor: Colors.blue, backgroundColor: Theme.of(context).colorScheme.primary,
foregroundColor: Colors.white, foregroundColor: Theme.of(context).colorScheme.onPrimary,
elevation: 0, elevation: 0,
), ),
body: _buildPage(appProvider.currentIndex), body: _buildPage(appProvider.currentTab),
bottomNavigationBar: BottomNavigationBar( bottomNavigationBar: Container(
currentIndex: appProvider.currentIndex, decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Theme.of(context).colorScheme.shadow.withAlpha(20),
blurRadius: 8,
offset: const Offset(0, -2),
),
],
),
child: BottomNavigationBar(
currentIndex: appProvider.currentTab,
onTap: (index) => appProvider.changeTab(index), onTap: (index) => appProvider.changeTab(index),
type: BottomNavigationBarType.fixed, type: BottomNavigationBarType.fixed,
backgroundColor: Colors.white, backgroundColor: Theme.of(context).colorScheme.surface,
selectedItemColor: Theme.of(context).colorScheme.primary,
unselectedItemColor: Theme.of(context).colorScheme.onSurface.withAlpha(120),
showSelectedLabels: true,
showUnselectedLabels: true,
items: navItems, items: navItems,
), ),
floatingActionButton: _buildFloatingButton(appProvider.currentIndex), ),
floatingActionButton: _buildFloatingButton(
context,
appProvider.currentTab,
),
); );
}, },
); );
@@ -59,7 +77,7 @@ class _MainScreenState extends State<MainScreen> {
} }
} }
Widget _buildFloatingButton(int index) { Widget _buildFloatingButton(BuildContext context, int index) {
return FloatingActionButton( return FloatingActionButton(
onPressed: () => _onPressFloatingButton(index), onPressed: () => _onPressFloatingButton(index),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
@@ -70,18 +88,7 @@ class _MainScreenState extends State<MainScreen> {
height: 50, height: 50,
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
gradient: LinearGradient( color: Theme.of(context).colorScheme.primary,
colors: [Colors.orange.shade300, Colors.orange.shade500],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: Colors.orange.shade100,
blurRadius: 12,
offset: Offset(0, 6),
),
],
), ),
child: Icon(Icons.add, color: Colors.white, size: 36), child: Icon(Icons.add, color: Colors.white, size: 36),
), ),

View File

@@ -50,22 +50,23 @@ class MyApp extends StatelessWidget {
providers: [ providers: [
ChangeNotifierProvider(create: (_) => AppProvider()), ChangeNotifierProvider(create: (_) => AppProvider()),
ChangeNotifierProvider(create: (_) => TodoProvider()), ChangeNotifierProvider(create: (_) => TodoProvider()),
ChangeNotifierProvider(create: (_) => FlispProvider()) ChangeNotifierProvider(create: (_) => FlispProvider()),
], ],
child: MaterialApp( child: Consumer<AppProvider>(
builder: (context, appProvider, child) {
return MaterialApp(
title: '闪灵', title: '闪灵',
localizationsDelegates: [ localizationsDelegates: [
GlobalMaterialLocalizations.delegate, GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate, GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
const Locale('zh'),
const Locale('zh', 'CN'),
], ],
supportedLocales: [const Locale('zh'), const Locale('zh', 'CN')],
locale: Locale('zh', 'CN'), locale: Locale('zh', 'CN'),
theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true), theme: appProvider.currentThemeData,
home: const MainScreen(), home: const MainScreen(),
);
},
), ),
); );
} }

View File

@@ -40,11 +40,13 @@ class FlispPageState extends State<FlispPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return buildBody( return Padding(
padding: EdgeInsets.all(10),
child: Column( child: Column(
children: [ children: [
buildStatsCard(_activeFlisps), buildStatsCard(_activeFlisps),
buildTabs( buildTabs(
context: context,
currentTab: _currentTab, currentTab: _currentTab,
onTabChanged: (value) { onTabChanged: (value) {
setState(() { setState(() {
@@ -53,15 +55,15 @@ class FlispPageState extends State<FlispPage> {
}, },
), ),
// 待办事项列表 // 待办事项列表
Expanded(child: _buildActiveFlispList()), Expanded(child: _buildActiveFlispList(context)),
], ],
), ),
); );
} }
Widget _buildActiveFlispList() { Widget _buildActiveFlispList(BuildContext context) {
return _activeFlisps.isEmpty return _activeFlisps.isEmpty
? buildEmptyState() ? buildEmptyState(context)
: buildFlispList( : buildFlispList(
context: context, context: context,
flisps: _activeFlisps, flisps: _activeFlisps,

View File

@@ -42,11 +42,13 @@ class TodoPageState extends State<TodoPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return buildBody( return Padding(
padding: EdgeInsets.all(10),
child: Column( child: Column(
children: [ children: [
buildStatsCard(_todos), buildStatsCard(_todos),
buildTabs( buildTabs(
context: context,
currentTab: _currentTab, currentTab: _currentTab,
onTabChanged: (value) { onTabChanged: (value) {
setState(() { setState(() {
@@ -55,15 +57,15 @@ class TodoPageState extends State<TodoPage> {
}, },
), ),
// 待办事项列表 // 待办事项列表
Expanded(child: _buildActiveTodoList()), Expanded(child: _buildActiveTodoList(context)),
], ],
), ),
); );
} }
Widget _buildActiveTodoList() { Widget _buildActiveTodoList(BuildContext context) {
return _activeTodos.isEmpty return _activeTodos.isEmpty
? buildEmptyState(_currentTab) ? buildEmptyState(context, _currentTab)
: buildTodoList( : buildTodoList(
todos: _activeTodos, todos: _activeTodos,
onToggleTodo: (todo) { onToggleTodo: (todo) {

View File

@@ -1,12 +1,188 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class AppProvider with ChangeNotifier { class ThemeColor {
int _currentIndex = 0; final String name;
final Color primaryColor;
final MaterialColor materialColor;
int get currentIndex => _currentIndex; ThemeColor({
required this.name,
required this.primaryColor,
required this.materialColor,
});
}
class AppProvider with ChangeNotifier {
int _currentTab = 0;
bool _isDarkMode = false;
ThemeColor _currentTheme = _defaultThemes[0];
// 预定义主题色
static final List<ThemeColor> _defaultThemes = [
ThemeColor(
name: '科技蓝',
primaryColor: Color(0xFF2563EB),
materialColor: MaterialColor(0xFF2563EB, {
50: Color(0xFFDBEAFE),
100: Color(0xFFBFDBFE),
200: Color(0xFF93C5FD),
300: Color(0xFF60A5FA),
400: Color(0xFF3B82F6),
500: Color(0xFF2563EB),
600: Color(0xFF1D4ED8),
700: Color(0xFF1E40AF),
800: Color(0xFF1E3A8A),
900: Color(0xFF1E3A8A),
}),
),
ThemeColor(
name: '翡翠绿',
primaryColor: Color(0xFF10B981),
materialColor: MaterialColor(0xFF10B981, {
50: Color(0xFFECFDF5),
100: Color(0xFFD1FAE5),
200: Color(0xFFA7F3D0),
300: Color(0xFF6EE7B7),
400: Color(0xFF34D399),
500: Color(0xFF10B981),
600: Color(0xFF059669),
700: Color(0xFF047857),
800: Color(0xFF065F46),
900: Color(0xFF064E3B),
}),
),
ThemeColor(
name: '活力橙',
primaryColor: Color(0xFFF59E0B),
materialColor: MaterialColor(0xFFF59E0B, {
50: Color(0xFFFFFBEB),
100: Color(0xFFFEF3C7),
200: Color(0xFFFDE68A),
300: Color(0xFFFCD34D),
400: Color(0xFFFBBF24),
500: Color(0xFFF59E0B),
600: Color(0xFFD97706),
700: Color(0xFFB45309),
800: Color(0xFF92400E),
900: Color(0xFF78350F),
}),
),
ThemeColor(
name: '梦幻紫',
primaryColor: Color(0xFF8B5CF6),
materialColor: MaterialColor(0xFF8B5CF6, {
50: Color(0xFFF5F3FF),
100: Color(0xFFEDE9FE),
200: Color(0xFFDDD6FE),
300: Color(0xFFC4B5FD),
400: Color(0xFFA78BFA),
500: Color(0xFF8B5CF6),
600: Color(0xFF7C3AED),
700: Color(0xFF6D28D9),
800: Color(0xFF5B21B6),
900: Color(0xFF4C1D95),
}),
),
ThemeColor(
name: '浪漫粉',
primaryColor: Color(0xFFEC4899),
materialColor: MaterialColor(0xFFEC4899, {
50: Color(0xFFFDF2F8),
100: Color(0xFFFCE7F3),
200: Color(0xFFFBCFE8),
300: Color(0xFFF9A8D4),
400: Color(0xFFF472B6),
500: Color(0xFFEC4899),
600: Color(0xFFDB2777),
700: Color(0xFFBE185D),
800: Color(0xFF9D174D),
900: Color(0xFF831843),
}),
),
ThemeColor(
name: '清新青',
primaryColor: Color(0xFF06B6D4),
materialColor: MaterialColor(0xFF06B6D4, {
50: Color(0xFFF0FDFA),
100: Color(0xFFCCFBF1),
200: Color(0xFF99F6E4),
300: Color(0xFF5EEAD4),
400: Color(0xFF2DD4BF),
500: Color(0xFF06B6D4),
600: Color(0xFF0891B2),
700: Color(0xFF0E7490),
800: Color(0xFF155E75),
900: Color(0xFF164E63),
}),
),
ThemeColor(
name: '深空蓝',
primaryColor: Color(0xFF1E40AF),
materialColor: MaterialColor(0xFF1E40AF, {
50: Color(0xFFEFF6FF),
100: Color(0xFFDBEAFE),
200: Color(0xFFBFDBFE),
300: Color(0xFF93C5FD),
400: Color(0xFF60A5FA),
500: Color(0xFF3B82F6),
600: Color(0xFF2563EB),
700: Color(0xFF1D4ED8),
800: Color(0xFF1E40AF),
900: Color(0xFF1E3A8A),
}),
),
ThemeColor(
name: '落日红',
primaryColor: Color(0xFFEF4444),
materialColor: MaterialColor(0xFFEF4444, {
50: Color(0xFFFEF2F2),
100: Color(0xFFFEE2E2),
200: Color(0xFFFECACA),
300: Color(0xFFFCA5A5),
400: Color(0xFFF87171),
500: Color(0xFFEF4444),
600: Color(0xFFDC2626),
700: Color(0xFFB91C1C),
800: Color(0xFF991B1B),
900: Color(0xFF7F1D1D),
}),
),
];
int get currentTab => _currentTab;
bool get isDarkMode => _isDarkMode;
ThemeColor get currentTheme => _currentTheme;
List<ThemeColor> get availableThemes => _defaultThemes;
void changeTab(int index) { void changeTab(int index) {
_currentIndex = index; _currentTab = index;
notifyListeners(); notifyListeners();
} }
// 切换明暗模式
void toggleDarkMode(bool value) {
_isDarkMode = value;
notifyListeners();
}
// 更改主题色
void changeTheme(ThemeColor theme) {
_currentTheme = theme;
notifyListeners();
}
ThemeData get currentThemeData {
return ThemeData(
primarySwatch: _currentTheme.materialColor,
colorScheme: ColorScheme.fromSeed(
seedColor: _currentTheme.primaryColor,
brightness: _isDarkMode ? Brightness.dark : Brightness.light,
),
useMaterial3: true,
fontFamily: 'CustomFont',
);
}
} }

View File

@@ -25,7 +25,7 @@ void showAwesomeDialog({
onPressed: onOk, onPressed: onOk,
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
elevation: 0, elevation: 0,
backgroundColor: Colors.orange, backgroundColor: Theme.of(context).primaryColor,
foregroundColor: Colors.white, foregroundColor: Colors.white,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
@@ -61,7 +61,7 @@ void showErrorDialog(BuildContext context, String message) {
animType: AnimType.scale, animType: AnimType.scale,
title: message, title: message,
btnOkText: "好的", btnOkText: "好的",
btnOkColor: Colors.green, btnOkColor: Colors.red,
btnOkOnPress: () {}, btnOkOnPress: () {},
autoHide: Duration(seconds: 2), autoHide: Duration(seconds: 2),
).show(); ).show();

View File

@@ -8,21 +8,25 @@ BoxDecoration buildBoxDecoration() {
); );
} }
Container buildBody({Widget? child}) { class BuildCard extends StatelessWidget {
final Widget? child;
const BuildCard({super.key, this.child});
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
return Container( return Container(
width: double.infinity, decoration: BoxDecoration(
color: Colors.grey[50], color: colors.surface,
padding: EdgeInsets.all(8), borderRadius: BorderRadius.circular(12),
border: Border.all(color: colors.outline.withAlpha(50), width: 1),
),
padding: const EdgeInsets.all(16),
child: child, child: child,
); );
} }
Container buildCard({Widget? child}) {
return Container(
decoration: buildBoxDecoration(),
padding: EdgeInsets.all(10),
child: child,
);
} }
void buildModalBottom({ void buildModalBottom({

View File

@@ -11,7 +11,7 @@ Widget buildStatsCard(List<Flisp> flisps) {
int workCount = flisps.where((flisp) => flisp.tag == FlispTag.work).length; int workCount = flisps.where((flisp) => flisp.tag == FlispTag.work).length;
int studyCount = flisps.where((flisp) => flisp.tag == FlispTag.study).length; int studyCount = flisps.where((flisp) => flisp.tag == FlispTag.study).length;
return buildCard( return BuildCard(
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
@@ -42,6 +42,7 @@ Widget _buildStatItem(String label, int count, Color color) {
} }
Widget buildTabs({ Widget buildTabs({
required BuildContext context,
required FlispTab currentTab, required FlispTab currentTab,
required ValueChanged<FlispTab> onTabChanged, required ValueChanged<FlispTab> onTabChanged,
}) { }) {
@@ -53,7 +54,7 @@ Widget buildTabs({
initialLabelIndex: FlispTab.values.indexOf(currentTab), initialLabelIndex: FlispTab.values.indexOf(currentTab),
totalSwitches: FlispTab.values.length, totalSwitches: FlispTab.values.length,
labels: FlispTab.values.map((e) => e.label).toList(), labels: FlispTab.values.map((e) => e.label).toList(),
activeBgColor: [Colors.orange.shade600], activeBgColor: [Theme.of(context).colorScheme.primary],
activeFgColor: Colors.white, activeFgColor: Colors.white,
inactiveBgColor: Colors.grey.shade200, inactiveBgColor: Colors.grey.shade200,
inactiveFgColor: Colors.grey.shade700, inactiveFgColor: Colors.grey.shade700,
@@ -89,11 +90,15 @@ Widget buildFlispList({
); );
} }
Widget _buildFlispContent(Flisp flisp) { Widget _buildFlispContent(BuildContext context, Flisp flisp) {
return RichText( return RichText(
text: TextSpan( text: TextSpan(
text: flisp.content, text: flisp.content,
style: const TextStyle(color: Colors.black87, fontSize: 16), style: TextStyle(
fontFamily: 'CustomFont',
color: Theme.of(context).colorScheme.onSurface,
fontSize: 16,
),
), ),
); );
} }
@@ -281,15 +286,13 @@ Widget _buildFlispItem({
onDismissed: (direction) { onDismissed: (direction) {
onDelete(flisp); onDelete(flisp);
}, },
child: Container( child: BuildCard(
decoration: buildBoxDecoration(),
padding: EdgeInsets.all(16),
child: InkWell( child: InkWell(
onTap: () => onEdit(flisp), onTap: () => onEdit(flisp),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildFlispContent(flisp), _buildFlispContent(context, flisp),
const SizedBox(height: 8), const SizedBox(height: 8),
if (hasImage) buildFlispImage(flisp: flisp, showDelete: false), if (hasImage) buildFlispImage(flisp: flisp, showDelete: false),
const SizedBox(height: 4), const SizedBox(height: 4),
@@ -337,16 +340,19 @@ Widget _buildDismissBackground() {
} }
// 空状态 // 空状态
Widget buildEmptyState() { Widget buildEmptyState(BuildContext context) {
final colors = Theme.of(context).colorScheme;
return Center( return Center(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Icon(Icons.flash_on, size: 80, color: Colors.orange.shade600), Icon(Icons.flash_on, size: 80, color: colors.primary),
const SizedBox(height: 16),
Text( Text(
'📝 还没有闪灵\n点击➕号添加第一个灵感吧~', '📝 还没有该类别闪灵\n点击➕号添加第一个灵感吧~',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle(color: Colors.orange.shade600), style: TextStyle(color: colors.onSurface, fontSize: 16),
), ),
], ],
), ),

View File

@@ -38,7 +38,7 @@ class _TodoFormState extends State<TodoForm> {
children: [ children: [
Icon( Icon(
widget.isEditing ? Icons.edit_note : Icons.add_task, widget.isEditing ? Icons.edit_note : Icons.add_task,
color: Colors.orange, color: Theme.of(context).primaryColor,
size: 24, size: 24,
), ),
SizedBox(width: 8), SizedBox(width: 8),
@@ -47,7 +47,7 @@ class _TodoFormState extends State<TodoForm> {
style: TextStyle( style: TextStyle(
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.orange, color: Theme.of(context).primaryColor,
), ),
), ),
], ],

View File

@@ -13,7 +13,7 @@ Widget buildStatsCard(List<Todo> todos) {
int completedCount = todos.where((todo) => todo.isCompleted).length; int completedCount = todos.where((todo) => todo.isCompleted).length;
return buildCard( return BuildCard(
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
@@ -44,6 +44,7 @@ Widget _buildStatItem(String label, int count, Color color) {
// Tab页 // Tab页
Widget buildTabs({ Widget buildTabs({
required BuildContext context,
required TodoTab currentTab, required TodoTab currentTab,
required ValueChanged<TodoTab> onTabChanged, required ValueChanged<TodoTab> onTabChanged,
}) { }) {
@@ -55,7 +56,7 @@ Widget buildTabs({
initialLabelIndex: TodoTab.values.indexOf(currentTab), initialLabelIndex: TodoTab.values.indexOf(currentTab),
totalSwitches: TodoTab.values.length, totalSwitches: TodoTab.values.length,
labels: TodoTab.values.map((e) => e.label).toList(), labels: TodoTab.values.map((e) => e.label).toList(),
activeBgColor: [Colors.orange.shade600], activeBgColor: [Theme.of(context).colorScheme.primary],
activeFgColor: Colors.white, activeFgColor: Colors.white,
inactiveBgColor: Colors.grey.shade200, inactiveBgColor: Colors.grey.shade200,
inactiveFgColor: Colors.grey.shade700, inactiveFgColor: Colors.grey.shade700,
@@ -94,7 +95,7 @@ Widget _buildTodoItem({
required ValueChanged<Todo> onToggle, required ValueChanged<Todo> onToggle,
required ValueChanged<Todo> onEdit, required ValueChanged<Todo> onEdit,
}) { }) {
return buildCard( return BuildCard(
child: ListTile( child: ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 8, vertical: 0), contentPadding: EdgeInsets.symmetric(horizontal: 8, vertical: 0),
leading: SizedBox( leading: SizedBox(
@@ -267,7 +268,7 @@ Widget _buildPriorityBadge(TodoPriority priority) {
} }
// 空状态 // 空状态
Widget buildEmptyState(TodoTab tab) { Widget buildEmptyState(BuildContext context, TodoTab tab) {
final messages = { final messages = {
TodoTab.all: '📝 还没有待办事项\n点击➕号添加第一个任务吧~', TodoTab.all: '📝 还没有待办事项\n点击➕号添加第一个任务吧~',
TodoTab.active: '🎯 没有待完成的任务\n享受轻松时光吧!✨', TodoTab.active: '🎯 没有待完成的任务\n享受轻松时光吧!✨',
@@ -275,15 +276,18 @@ Widget buildEmptyState(TodoTab tab) {
TodoTab.today: '📅 今天没有安排任务\n好好放松一下吧~😊', TodoTab.today: '📅 今天没有安排任务\n好好放松一下吧~😊',
}; };
final colors = Theme.of(context).colorScheme;
return Center( return Center(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Icon(Icons.checklist, size: 80, color: Colors.orange.shade600), Icon(Icons.checklist, size: 80, color: colors.primary),
const SizedBox(height: 16),
Text( Text(
messages[tab] ?? '暂无数据', messages[tab] ?? '暂无数据',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle(color: Colors.orange.shade600), style: TextStyle(color: colors.onSurface, fontSize: 16),
), ),
], ],
), ),

View File

@@ -1,32 +1,12 @@
name: flisp_app name: flisp_app
description: "闪灵" description: "闪灵"
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1 version: 1.0.0+1
environment: environment:
sdk: ^3.7.0 sdk: ^3.7.0
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
@@ -70,52 +50,11 @@ dev_dependencies:
sdk: flutter sdk: flutter
hive_generator: ^2.0.1 hive_generator: ^2.0.1
build_runner: ^2.4.6 build_runner: ^2.4.6
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^5.0.0 flutter_lints: ^5.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter: flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true uses-material-design: true
fonts:
# To add assets to your application, add an assets section, like this: - family: CustomFont
# assets: fonts:
# - images/a_dot_burr.jpeg - asset: fonts/custom.ttf
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/to/asset-from-package
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/to/font-from-package