factor:布局颜色重构

This commit is contained in:
2025-11-20 11:04:29 +08:00
parent a582ebb3e4
commit 6e97d7f33f
16 changed files with 311 additions and 284 deletions

58
lib/layout/app_body.dart Normal file
View File

@@ -0,0 +1,58 @@
import 'package:flisp_app/pages/calendar_page.dart';
import 'package:flisp_app/pages/flisp_page.dart';
import 'package:flisp_app/pages/stats_page.dart';
import 'package:flisp_app/pages/todo_page.dart';
import 'package:flisp_app/provider/app_provider.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class AppBody extends StatefulWidget {
const AppBody({super.key});
@override
State<AppBody> createState() => AppBodyState();
}
class AppBodyState extends State<AppBody> {
final GlobalKey<FlispPageState> _flispPageKey = GlobalKey();
final GlobalKey<TodoPageState> _todoPageKey = GlobalKey();
final GlobalKey<CalendarPageState> _calendarPageKey = GlobalKey();
Widget _buildPage(int index) {
switch (index) {
case 0:
return FlispPage(key: _flispPageKey);
case 1:
return TodoPage(key: _todoPageKey);
case 2:
return CalendarPage(key: _calendarPageKey);
case 3:
return StatsPage();
default:
return FlispPage(key: _flispPageKey);
}
}
void showPageAddDialog(int index) {
if (index == 0) {
if (_flispPageKey.currentState != null) {
_flispPageKey.currentState!.showAddDialog();
}
} else if (index == 1) {
if (_todoPageKey.currentState != null) {
_todoPageKey.currentState!.showAddDialog();
}
} else if (index == 2) {
if (_calendarPageKey.currentState != null) {
_calendarPageKey.currentState!.showAddDialog();
}
}
}
@override
Widget build(BuildContext context) {
final appProvider = context.watch<AppProvider>();
return _buildPage(appProvider.currentTab);
}
}

View File

@@ -6,7 +6,7 @@ import 'package:provider/provider.dart';
class AppDrawer extends StatelessWidget {
const AppDrawer({super.key});
DrawerHeader buildDrawerHeader(BuildContext context) {
DrawerHeader _buildDrawerHeader(BuildContext context) {
return DrawerHeader(
decoration: BoxDecoration(color: Theme.of(context).colorScheme.primary),
child: Column(
@@ -86,7 +86,7 @@ class AppDrawer extends StatelessWidget {
);
}
Widget buildVersionInfo({
Widget _buildVersionInfo({
required BuildContext context,
required String fullVersion,
}) {
@@ -109,18 +109,16 @@ class AppDrawer extends StatelessWidget {
@override
Widget build(BuildContext context) {
final appProvider = Provider.of<AppProvider>(context);
final appProvider = context.watch<AppProvider>();
return Drawer(
child: Column(
children: [
// 主要内容区域,可以滚动
Expanded(
child: ListView(
padding: EdgeInsets.zero,
children: [
// 抽屉头部
buildDrawerHeader(context),
_buildDrawerHeader(context),
_buildThemeSection(context, appProvider),
const Divider(),
_buildDrawerItem(
@@ -135,15 +133,10 @@ class AppDrawer extends StatelessWidget {
],
),
),
Consumer<AppProvider>(
builder: (context, appProvider, child) {
return buildVersionInfo(
context: context,
fullVersion: appProvider.fullVersion,
);
},
),
_buildVersionInfo(
context: context,
fullVersion: appProvider.fullVersion,
)
],
),
);

View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
class AppFloatingButton extends StatelessWidget {
final VoidCallback onPressed;
const AppFloatingButton({super.key, required this.onPressed});
@override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: onPressed,
backgroundColor: Colors.transparent,
elevation: 0,
shape: CircleBorder(),
child: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).colorScheme.primary,
),
child: Icon(Icons.add, color: Colors.white, size: 36),
),
);
}
}

View File

@@ -0,0 +1,43 @@
import 'package:flisp_app/provider/app_provider.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class AppNavbar extends StatelessWidget {
AppNavbar({super.key});
final List<BottomNavigationBarItem> navItems = [
BottomNavigationBarItem(icon: Icon(Icons.flash_on), label: '闪灵'),
BottomNavigationBarItem(icon: Icon(Icons.checklist), label: '待办'),
BottomNavigationBarItem(icon: Icon(Icons.calendar_month), label: '日程'),
BottomNavigationBarItem(icon: Icon(Icons.insert_chart), label: '统计'),
];
@override
Widget build(BuildContext context) {
final appProvider = context.watch<AppProvider>();
final colors = Theme.of(context).colorScheme;
return Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: colors.shadow.withAlpha(20),
blurRadius: 8,
offset: const Offset(0, -2),
),
],
),
child: BottomNavigationBar(
currentIndex: appProvider.currentTab,
onTap: (index) => appProvider.changeTab(index),
type: BottomNavigationBarType.fixed,
backgroundColor: colors.surface,
selectedItemColor: colors.primary,
unselectedItemColor: colors.onSurface.withAlpha(120),
showSelectedLabels: true,
showUnselectedLabels: true,
items: navItems,
),
);
}
}

View File

@@ -1,8 +1,7 @@
import 'package:flisp_app/layout/app_body.dart';
import 'package:flisp_app/layout/app_drawer.dart';
import 'package:flisp_app/pages/calendar_page.dart';
import 'package:flisp_app/pages/flisp_page.dart';
import 'package:flisp_app/pages/stats_page.dart';
import 'package:flisp_app/pages/todo_page.dart';
import 'package:flisp_app/layout/app_floating_button.dart';
import 'package:flisp_app/layout/app_navbar.dart';
import 'package:flisp_app/provider/app_provider.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
@@ -15,119 +14,35 @@ class MainScreen extends StatefulWidget {
}
class _MainScreenState extends State<MainScreen> {
final GlobalKey<FlispPageState> _flispPageKey = GlobalKey();
final GlobalKey<TodoPageState> _todoPageKey = GlobalKey();
final GlobalKey<CalendarPageState> _calendarPageKey = GlobalKey();
final GlobalKey<AppBodyState> _appBodyKey = GlobalKey();
List<BottomNavigationBarItem> navItems = [
BottomNavigationBarItem(icon: Icon(Icons.flash_on), label: '闪灵'),
BottomNavigationBarItem(icon: Icon(Icons.checklist), label: '待办'),
BottomNavigationBarItem(
icon: Icon(Icons.calendar_month_rounded),
label: '日程',
),
BottomNavigationBarItem(icon: Icon(Icons.insert_chart), label: '统计'),
];
void _onPressedFloatingButton(int index) {
_appBodyKey.currentState?.showPageAddDialog(index);
}
@override
Widget build(BuildContext context) {
return Consumer<AppProvider>(
builder: (context, appProvider, child) {
return Scaffold(
drawer: const AppDrawer(),
appBar: AppBar(
title: Text(_getAppBarTitle(appProvider.currentTab)),
backgroundColor: Theme.of(context).colorScheme.primary,
foregroundColor: Theme.of(context).colorScheme.onPrimary,
elevation: 0,
),
body: _buildPage(appProvider.currentTab),
bottomNavigationBar: Container(
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),
type: BottomNavigationBarType.fixed,
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,
),
),
floatingActionButton: _buildFloatingButton(
context,
appProvider.currentTab,
),
);
},
);
}
final provider = context.watch<AppProvider>();
void _onPressFloatingButton(int index) {
if (index == 0) {
if (_flispPageKey.currentState != null) {
_flispPageKey.currentState!.showAddDialog();
}
} else if (index == 1) {
if (_todoPageKey.currentState != null) {
_todoPageKey.currentState!.showAddDialog();
}
} else if (index == 2) {
if (_calendarPageKey.currentState != null) {
_calendarPageKey.currentState!.showAddDialog();
}
}
}
Widget? _buildFloatingButton(BuildContext context, int index) {
if (index == 3) return null;
return FloatingActionButton(
onPressed: () => _onPressFloatingButton(index),
backgroundColor: Colors.transparent,
elevation: 0,
shape: CircleBorder(),
child: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).colorScheme.primary,
),
child: Icon(Icons.add, color: Colors.white, size: 36),
return Scaffold(
drawer: const AppDrawer(),
appBar: AppBar(
title: Text(provider.currentAppBarTitle),
backgroundColor: Theme.of(context).colorScheme.primary,
foregroundColor: Theme.of(context).colorScheme.onPrimary,
elevation: 0,
),
body: Padding(
padding: EdgeInsets.all(10),
child: AppBody(key: _appBodyKey),
),
bottomNavigationBar: AppNavbar(),
floatingActionButton:
provider.currentTab == 3
? null
: AppFloatingButton(
onPressed: () => _onPressedFloatingButton(provider.currentTab),
),
);
}
Widget _buildPage(int index) {
switch (index) {
case 0:
return FlispPage(key: _flispPageKey);
case 1:
return TodoPage(key: _todoPageKey);
case 2:
return CalendarPage(key: _calendarPageKey);
case 3:
return StatsPage();
default:
return FlispPage(key: _flispPageKey);
}
}
String _getAppBarTitle(int index) {
final titles = {0: '闪灵', 1: '待办', 2: '日程', 3: '统计'};
return titles[index] ?? '闪灵';
}
}

View File

@@ -128,50 +128,51 @@ class CalendarPageState extends State<CalendarPage> {
}
}
Widget _buildCalendarTabs() {
return buildTabs(
context: context,
currentTab: _currentTab,
onTabChanged: (value) {
setState(() {
_currentTab = value;
_calendarController.view = value.view;
});
},
);
}
@override
Widget build(BuildContext context) {
final provider = Provider.of<CalendarProvider>(context, listen: false);
return Padding(
padding: EdgeInsets.all(10),
child: Column(
children: [
buildTabs(
return Column(
children: [
_buildCalendarTabs(),
Expanded(
child: buildCalendar(
context: context,
currentTab: _currentTab,
onTabChanged: (value) {
setState(() {
_currentTab = value;
_calendarController.view = value.view;
});
controller: _calendarController,
dataSource: AppointmentDataSource(_appointments),
onAdd: () {
if (_calendarController.view == CalendarView.week) {
_showDialog(false, provider.formItem);
}
},
onEdit: (appointment) {
final calendar = calendarService.getCalendarById(
appointment.id as int,
);
_showDialog(true, calendar);
},
onDelete: (appointment) {
final calendar = calendarService.getCalendarById(
appointment.id as int,
);
_deleteCalendar(calendar);
},
),
Expanded(
child: buildCalendar(
context: context,
controller: _calendarController,
dataSource: AppointmentDataSource(_appointments),
onAdd: () {
if (_calendarController.view == CalendarView.week) {
_showDialog(false, provider.formItem);
}
},
onEdit: (appointment) {
final calendar = calendarService.getCalendarById(
appointment.id as int,
);
_showDialog(true, calendar);
},
onDelete: (appointment) {
final calendar = calendarService.getCalendarById(
appointment.id as int,
);
_deleteCalendar(calendar);
},
),
),
],
),
),
],
);
}
}

View File

@@ -40,23 +40,23 @@ class FlispPageState extends State<FlispPage> {
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(10),
child: Column(
children: [
buildTabs(
context: context,
currentTab: _currentTab,
onTabChanged: (value) {
setState(() {
_currentTab = value;
});
},
),
// 待办事项列表
Expanded(child: _buildActiveFlispList(context)),
],
),
return Column(
children: [
_buildFlispTabs(),
Expanded(child: _buildActiveFlispList(context)),
],
);
}
Widget _buildFlispTabs() {
return buildTabs(
context: context,
currentTab: _currentTab,
onTabChanged: (value) {
setState(() {
_currentTab = value;
});
},
);
}

View File

@@ -224,52 +224,44 @@ class StatsPageState extends State<StatsPage> {
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(10),
child: Column(
children: [
YearSelector(
initialYear: DateTime.now().year,
minYear: 2000,
maxYear: 2100,
onYearChanged: (year) => {
setState(() {
currentYear = year;
refreshStats();
})
},
),
SizedBox(
height: statsHeight,
child: _buildFlispStats(context),
),
SizedBox(height: 10),
SizedBox(
height: statsHeight,
child: _buildTodoStats(context),
),
SizedBox(height: 10),
SizedBox(
height: chartHeight,
child: BuildCard(child: _buildFlispMonthlyStats(context)),
),
SizedBox(height: 10),
SizedBox(
height: chartHeight,
child: BuildCard(child: _buildFlispCategoryStats(context)),
),
SizedBox(height: 10),
SizedBox(
height: chartHeight,
child: BuildCard(child: _buildTodoMonthlyStats(context)),
),
SizedBox(height: 10),
SizedBox(
height: chartHeight,
child: BuildCard(child: _buildTodoPriorityStats(context)),
),
],
),
child: Column(
children: [
YearSelector(
initialYear: DateTime.now().year,
minYear: 2000,
maxYear: 2100,
onYearChanged:
(year) => {
setState(() {
currentYear = year;
refreshStats();
}),
},
),
SizedBox(height: statsHeight, child: _buildFlispStats(context)),
SizedBox(height: 10),
SizedBox(height: statsHeight, child: _buildTodoStats(context)),
SizedBox(height: 10),
SizedBox(
height: chartHeight,
child: BuildCard(child: _buildFlispMonthlyStats(context)),
),
SizedBox(height: 10),
SizedBox(
height: chartHeight,
child: BuildCard(child: _buildFlispCategoryStats(context)),
),
SizedBox(height: 10),
SizedBox(
height: chartHeight,
child: BuildCard(child: _buildTodoMonthlyStats(context)),
),
SizedBox(height: 10),
SizedBox(
height: chartHeight,
child: BuildCard(child: _buildTodoPriorityStats(context)),
),
],
),
);
}

View File

@@ -42,23 +42,23 @@ class TodoPageState extends State<TodoPage> {
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(10),
child: Column(
children: [
buildTabs(
context: context,
currentTab: _currentTab,
onTabChanged: (value) {
setState(() {
_currentTab = value;
});
},
),
// 待办事项列表
Expanded(child: _buildActiveTodoList(context)),
],
),
return Column(
children: [
_buildTodoTabs(),
Expanded(child: _buildActiveTodoList(context)),
],
);
}
Widget _buildTodoTabs() {
return buildTabs(
context: context,
currentTab: _currentTab,
onTabChanged: (value) {
setState(() {
_currentTab = value;
});
},
);
}

View File

@@ -6,15 +6,17 @@ import 'package:shared_preferences/shared_preferences.dart';
class AppProvider with ChangeNotifier {
static const String _themeKey = 'selected_theme';
static const String _darkModeKey = 'is_dark_mode';
static const List<String> appBarTitles = ['闪灵', '待办', '日程', '统计'];
int _currentTab = 0;
bool _isDarkMode = false;
ThemeColor _currentTheme = defaultThemes[0];
String _appVersion = '1.0.0';
String _buildNumber = '1';
int currentTab = 0;
String currentAppBarTitle = '闪灵';
bool isDarkMode = false;
ThemeColor currentTheme = defaultThemes[0];
String appVersion = '1.0.0';
String buildNumber = '1';
late SharedPreferences _prefs;
bool _isInitialized = false;
bool isInitialized = false;
AppProvider() {
_initPreferences();
@@ -26,26 +28,26 @@ class AppProvider with ChangeNotifier {
_loadPreferences();
await _getVersionInfo();
_isInitialized = true;
isInitialized = true;
notifyListeners();
}
// 加载存储的设置
void _loadPreferences() {
_isDarkMode = _prefs.getBool(_darkModeKey) ?? false;
isDarkMode = _prefs.getBool(_darkModeKey) ?? false;
// 加载主题色
final themeName = _prefs.getString(_themeKey);
if (themeName != null) {
_currentTheme = getThemeColor(themeName);
currentTheme = getThemeColor(themeName);
}
}
Future<void> _getVersionInfo() async {
try {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
_appVersion = packageInfo.version;
_buildNumber = packageInfo.buildNumber;
appVersion = packageInfo.version;
buildNumber = packageInfo.buildNumber;
notifyListeners();
} catch (e) {
print('获取版本信息失败: $e');
@@ -54,51 +56,44 @@ class AppProvider with ChangeNotifier {
// 保存主题色
Future<void> _saveTheme() async {
await _prefs.setString(_themeKey, _currentTheme.name);
await _prefs.setString(_themeKey, currentTheme.name);
}
// 保存暗黑模式
Future<void> _saveDarkMode() async {
await _prefs.setBool(_darkModeKey, _isDarkMode);
await _prefs.setBool(_darkModeKey, isDarkMode);
}
int get currentTab => _currentTab;
bool get isDarkMode => _isDarkMode;
ThemeColor get currentTheme => _currentTheme;
List<ThemeColor> get availableThemes => defaultThemes;
bool get isInitialized => _isInitialized;
String get fullVersion => '$_appVersion+$_buildNumber';
String get fullVersion => '$appVersion+$buildNumber';
void changeTab(int index) {
_currentTab = index;
currentTab = index;
currentAppBarTitle = appBarTitles[index];
notifyListeners();
}
// 切换明暗模式
void toggleDarkMode(bool value) {
_isDarkMode = value;
isDarkMode = value;
_saveDarkMode();
notifyListeners();
}
// 更改主题色
void changeTheme(ThemeColor theme) {
_currentTheme = theme;
currentTheme = theme;
_saveTheme();
notifyListeners();
}
ThemeData get currentThemeData {
return ThemeData(
primarySwatch: _currentTheme.materialColor,
primarySwatch: currentTheme.materialColor,
colorScheme: ColorScheme.fromSeed(
seedColor: _currentTheme.primaryColor,
brightness: _isDarkMode ? Brightness.dark : Brightness.light,
seedColor: currentTheme.primaryColor,
brightness: isDarkMode ? Brightness.dark : Brightness.light,
),
useMaterial3: true,
fontFamily: 'CustomFont',

View File

@@ -12,7 +12,7 @@ void showAwesomeDialog({
dialogType: DialogType.noHeader,
animType: AnimType.scale,
body: body,
dialogBackgroundColor: Theme.of(context).colorScheme.surface,
dialogBackgroundColor: Theme.of(context).colorScheme.surfaceContainer,
btnOkText: "确认",
btnCancelText: "取消",
btnOkColor: Colors.orange,
@@ -31,7 +31,7 @@ void showAwesomeDialog({
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 6),
textStyle: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
textStyle: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, fontFamily: 'CustomFont'),
),
child: Text("确认"),
),

View File

@@ -37,6 +37,7 @@ class _CalendarFormState extends State<CalendarForm> {
Widget buildTitle() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
widget.isEditing ? Icons.edit_note : Icons.add_task,
@@ -281,7 +282,7 @@ class _CalendarFormState extends State<CalendarForm> {
}
return Padding(
padding: const EdgeInsets.all(16),
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 10),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [buildTitle(), SizedBox(height: 20), buildForm()],

View File

@@ -61,6 +61,10 @@ Widget buildCalendar({
return SfCalendar(
view: CalendarView.week,
controller: controller,
backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
headerStyle: CalendarHeaderStyle(
backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
),
dataSource: dataSource,
firstDayOfWeek: 1,
showDatePickerButton: true,
@@ -75,8 +79,8 @@ Widget buildCalendar({
scheduleViewSettings: ScheduleViewSettings(
monthHeaderSettings: MonthHeaderSettings(
backgroundColor: Theme.of(context).colorScheme.primary,
height: 85
)
height: 85,
),
),
// 时间区域设置
timeSlotViewSettings: TimeSlotViewSettings(

View File

@@ -34,9 +34,9 @@ class BuildCard extends StatelessWidget {
return Container(
decoration: BoxDecoration(
color: colors.surface,
color: colors.surfaceContainer,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: colors.outline.withAlpha(50), width: 1),
// border: Border.all(color: colors.outline.withAlpha(50), width: 1),
),
padding: const EdgeInsets.all(16),
child: child,
@@ -70,7 +70,7 @@ void buildModalBottom({
builder: (_, controller) {
return Container(
decoration: BoxDecoration(
color: colors.surface,
color: colors.surfaceContainer,
border: Border.all(
color: colors.outline.withAlpha(50),
width: 1,
@@ -159,7 +159,7 @@ Future<bool?> showDeleteConfirmationDialog(BuildContext context) async {
builder: (BuildContext context) {
return AlertDialog(
title: const Text('确认删除'),
backgroundColor: Colors.white,
backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
content: const Text('确定要删除这个项目吗?'),
actions: [
TextButton(

View File

@@ -68,10 +68,9 @@ Widget buildFlispList({
itemCount: flisps.length,
separatorBuilder: (context, index) => SizedBox(height: 8),
itemBuilder: (context, index) {
final flisp = flisps[index];
return _buildFlispItem(
context: context,
flisp: flisp,
flisp: flisps[index],
onEdit: onEdit,
onDelete: onDelete,
);

View File

@@ -36,6 +36,7 @@ class _TodoFormState extends State<TodoForm> {
Widget buildTitle() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
widget.isEditing ? Icons.edit_note : Icons.add_task,
@@ -322,7 +323,6 @@ class _TodoFormState extends State<TodoForm> {
decoration: BoxDecoration(
color: colors.surface,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade300, width: 1),
),
padding: EdgeInsets.all(12),
child: Column(
@@ -362,7 +362,7 @@ class _TodoFormState extends State<TodoForm> {
}
return Padding(
padding: const EdgeInsets.all(16),
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 10),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [buildTitle(), SizedBox(height: 20), buildForm()],