关怀模式

This commit is contained in:
Developer
2026-04-02 22:30:49 +08:00
parent 09fee0694c
commit 7872f2e78a
70 changed files with 4884 additions and 2752 deletions

View File

@@ -0,0 +1,219 @@
/// 时间: 2026-04-02
/// 功能: 关怀模式底部导航栏
/// 介绍: 关怀模式下显示的简化底部导航栏,只包含诗词和答题两个入口
/// 最新变化: 2026-04-02 初始创建
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../config/app_config.dart';
import '../../services/get/theme_controller.dart';
class CareModeNavigation extends StatelessWidget {
final int currentIndex;
final Function(int) onTap;
const CareModeNavigation({
super.key,
required this.currentIndex,
required this.onTap,
});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
return Obx(() {
final isDark = themeController.isDarkMode;
final enableBlur = themeController.enableBlurEffect;
final primaryColor = themeController.currentThemeColor;
return _buildGlassBar(context, isDark, enableBlur, primaryColor);
});
}
Widget _buildGlassBar(
BuildContext context,
bool isDark,
bool enableBlur,
Color primaryColor,
) {
return SafeArea(
top: false,
child: Container(
padding: EdgeInsets.only(
left: AppConfig.liquidGlassHorizontalMargin,
right: AppConfig.liquidGlassHorizontalMargin,
bottom: AppConfig.liquidGlassBottomMargin,
top: 8,
),
child: Container(
height: AppConfig.liquidGlassHeight,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
AppConfig.liquidGlassCornerRadius,
),
boxShadow: [
BoxShadow(
color: isDark
? Colors.black.withValues(alpha: 0.6)
: Colors.black.withValues(alpha: 0.15),
blurRadius: 35,
spreadRadius: -10,
offset: const Offset(0, 12),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(
AppConfig.liquidGlassCornerRadius,
),
child: enableBlur
? BackdropFilter(
filter: ImageFilter.blur(
sigmaX: AppConfig.liquidGlassBlur,
sigmaY: AppConfig.liquidGlassBlur,
),
child: _buildGlassContent(isDark, primaryColor),
)
: _buildGlassContent(isDark, primaryColor),
),
),
),
);
}
Widget _buildGlassContent(bool isDark, Color primaryColor) {
return Stack(
children: [
Container(
decoration: BoxDecoration(
color: isDark
? Colors.black.withValues(alpha: 0.4)
: Colors.white.withValues(alpha: 0.9),
borderRadius: BorderRadius.circular(
AppConfig.liquidGlassCornerRadius,
),
),
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
AppConfig.liquidGlassCornerRadius,
),
border: Border.all(
color: isDark
? Colors.white.withValues(alpha: 0.2)
: Colors.black.withValues(alpha: 0.1),
width: 0.6,
),
),
),
_buildNavItems(primaryColor),
],
);
}
Widget _buildNavItems(Color primaryColor) {
final items = [
_NavItem(Icons.menu_book_rounded, '📖', '诗词', 0),
_NavItem(Icons.psychology_rounded, '🎯', '答题', 1),
];
return Row(
children: List.generate(items.length, (index) {
return Expanded(
child: _buildNavItem(
items[index],
index == currentIndex,
primaryColor,
),
);
}),
);
}
Widget _buildNavItem(_NavItem item, bool isSelected, Color primaryColor) {
final themeController = Get.find<ThemeController>();
return Obx(() {
final isDark = themeController.isDarkMode;
final enableAnimation = themeController.enableAnimation;
return Material(
color: Colors.transparent,
child: InkWell(
onTap: () => onTap(item.index),
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
borderRadius: BorderRadius.circular(
AppConfig.liquidGlassCornerRadius,
),
child: AnimatedContainer(
duration: enableAnimation
? const Duration(milliseconds: 250)
: Duration.zero,
curve: Curves.easeOutCubic,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
enableAnimation
? AnimatedScale(
scale: isSelected ? 1.1 : 1.0,
duration: const Duration(milliseconds: 200),
curve: Curves.easeOutCubic,
child: _buildIcon(
item,
isSelected,
isDark,
primaryColor,
),
)
: _buildIcon(item, isSelected, isDark, primaryColor),
const SizedBox(height: 3),
AnimatedDefaultTextStyle(
duration: enableAnimation
? const Duration(milliseconds: 200)
: Duration.zero,
style: TextStyle(
fontSize: 12,
fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400,
color: isSelected
? primaryColor
: (isDark ? Colors.grey[400] : Colors.grey[600]),
letterSpacing: 0.15,
),
child: Text(item.label),
),
],
),
),
),
);
});
}
Widget _buildIcon(
_NavItem item,
bool isSelected,
bool isDark,
Color primaryColor,
) {
return Icon(
item.icon,
size: 24,
color: isSelected
? primaryColor
: (isDark ? Colors.grey[400] : Colors.grey[600]),
);
}
}
class _NavItem {
final IconData icon;
final String emoji;
final String label;
final int index;
_NavItem(this.icon, this.emoji, this.label, this.index);
}

View File

@@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../constants/app_constants.dart';
import '../services/get/theme_controller.dart';
// 自定义按钮组件
class CustomButton extends StatelessWidget {
@@ -28,13 +30,16 @@ class CustomButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final primaryColor = themeController.currentThemeColor;
return SizedBox(
width: width,
height: height ?? 48,
child: ElevatedButton(
onPressed: isLoading ? null : onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: backgroundColor ?? AppConstants.primaryColor,
backgroundColor: backgroundColor ?? primaryColor,
foregroundColor: textColor ?? Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(borderRadius ?? 8),
@@ -180,13 +185,16 @@ class LoadingIndicator extends StatelessWidget {
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final primaryColor = themeController.currentThemeColor;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
color ?? AppConstants.primaryColor,
color ?? primaryColor,
),
),
if (message != null) ...[

View File

@@ -1,15 +1,18 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../constants/app_constants.dart';
import '../views/home/home_page.dart';
import '../views/discover_page.dart';
import '../views/favorites_page.dart';
import '../views/profile/profile_page.dart';
import '../views/profile/level/poetry.dart';
import '../views/home/care/care_poetry_page.dart';
import '../services/get/main_navigation_controller.dart';
import '../services/get/profile_controller.dart';
import '../services/get/theme_controller.dart';
import '../services/get/tap_liquid_glass_controller.dart';
import '../services/get/care_controller.dart';
import 'tap-liquid-glass.dart';
import 'care/care_mode_navigation.dart';
/// 时间: 2025-03-21
/// 功能: 主导航页面包含底部导航栏和4个主要页面
@@ -24,9 +27,11 @@ class MainNavigation extends StatelessWidget {
// 初始化导航控制器(只初始化一次)
Get.lazyPut(() => MainNavigationController());
Get.lazyPut(() => TapLiquidGlassController());
Get.lazyPut(() => CareController());
final controller = Get.find<MainNavigationController>();
final themeController = Get.find<ThemeController>();
final glassController = Get.find<TapLiquidGlassController>();
final careController = Get.find<CareController>();
final List<Widget> pages = [
HomePage(),
@@ -35,25 +40,27 @@ class MainNavigation extends StatelessWidget {
const ProfilePage(),
];
final primaryColor = themeController.currentThemeColor;
final List<BottomNavigationBarItem> bottomNavItems = [
const BottomNavigationBarItem(
icon: Icon(Icons.home),
activeIcon: Icon(Icons.home, color: AppConstants.primaryColor),
BottomNavigationBarItem(
icon: const Icon(Icons.home),
activeIcon: Icon(Icons.home, color: primaryColor),
label: '主页',
),
const BottomNavigationBarItem(
icon: Icon(Icons.explore),
activeIcon: Icon(Icons.explore, color: AppConstants.primaryColor),
BottomNavigationBarItem(
icon: const Icon(Icons.explore),
activeIcon: Icon(Icons.explore, color: primaryColor),
label: '发现',
),
const BottomNavigationBarItem(
icon: Icon(Icons.favorite_border),
activeIcon: Icon(Icons.favorite, color: AppConstants.primaryColor),
BottomNavigationBarItem(
icon: const Icon(Icons.favorite_border),
activeIcon: Icon(Icons.favorite, color: primaryColor),
label: '收藏',
),
const BottomNavigationBarItem(
icon: Icon(Icons.person_outline),
activeIcon: Icon(Icons.person, color: AppConstants.primaryColor),
BottomNavigationBarItem(
icon: const Icon(Icons.person_outline),
activeIcon: Icon(Icons.person, color: primaryColor),
label: '个人',
),
];
@@ -61,11 +68,23 @@ class MainNavigation extends StatelessWidget {
return Obx(() {
final isDark = themeController.isDarkMode;
final useGlass = glassController.isEnabled;
final isCareMode = careController.isCareModeEnabled;
// 关怀模式开启时,使用关怀模式的导航栏
if (isCareMode) {
return Scaffold(body: _buildCareModeBody(careController));
}
return Scaffold(
body: useGlass
? _buildGlassBody(controller, pages)
: _buildClassicBody(controller, pages, isDark, bottomNavItems),
: _buildClassicBody(
controller,
pages,
isDark,
bottomNavItems,
primaryColor,
),
);
});
}
@@ -79,10 +98,7 @@ class MainNavigation extends StatelessWidget {
return Stack(
children: [
// 页面内容 - 延伸到屏幕底部
IndexedStack(
index: controller.currentIndex.value,
children: pages,
),
IndexedStack(index: controller.currentIndex.value, children: pages),
// 悬浮的液态玻璃导航栏
Positioned(
left: 0,
@@ -109,6 +125,7 @@ class MainNavigation extends StatelessWidget {
List<Widget> pages,
bool isDark,
List<BottomNavigationBarItem> items,
Color primaryColor,
) {
return Column(
children: [
@@ -143,7 +160,7 @@ class MainNavigation extends StatelessWidget {
}
},
type: BottomNavigationBarType.fixed,
selectedItemColor: AppConstants.primaryColor,
selectedItemColor: primaryColor,
unselectedItemColor: isDark ? Colors.grey[400] : Colors.grey[600],
backgroundColor: isDark ? const Color(0xFF2A2A2A) : Colors.white,
elevation: 0,
@@ -156,4 +173,34 @@ class MainNavigation extends StatelessWidget {
],
);
}
/// 构建关怀模式主体
/// 使用 Stack 布局,让导航栏悬浮在页面内容上方
Widget _buildCareModeBody(CareController careController) {
return Obx(() {
final carePages = [const CarePoetryPage(), const PoetryLevelPage()];
return Stack(
children: [
// 页面内容 - 延伸到屏幕底部
IndexedStack(
index: careController.careNavigationIndex,
children: carePages,
),
// 悬浮的关怀模式导航栏
Positioned(
left: 0,
right: 0,
bottom: 0,
child: CareModeNavigation(
currentIndex: careController.careNavigationIndex,
onTap: (index) {
careController.switchCareNavigation(index);
},
),
),
],
);
});
}
}

View File

@@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../utils/responsive_layout.dart';
import '../constants/app_constants.dart';
import '../services/get/theme_controller.dart';
class ResponsiveButton extends StatelessWidget {
final String text;
@@ -25,6 +27,8 @@ class ResponsiveButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final buttonSize = size ?? (ResponsiveLayout.isMobile(context) ? ButtonSize.small : ButtonSize.medium);
final themeController = Get.find<ThemeController>();
final primaryColor = themeController.currentThemeColor;
return SizedBox(
width: ResponsiveLayout.isMobile(context) ? double.infinity : null,
@@ -32,7 +36,7 @@ class ResponsiveButton extends StatelessWidget {
child: ElevatedButton(
onPressed: isLoading ? null : onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: backgroundColor ?? AppConstants.primaryColor,
backgroundColor: backgroundColor ?? primaryColor,
foregroundColor: textColor ?? Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(_getBorderRadius(buttonSize)),

View File

@@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../constants/app_constants.dart';
import '../services/get/theme_controller.dart';
/// 时间: 2026-03-22
/// 功能: 主导航内「标题 + Tab」共用 AppBar 构造
@@ -23,6 +25,8 @@ class TabbedNavAppBar {
Color? foregroundColor,
}) {
final isDark = backgroundColor != null && backgroundColor != Colors.white;
final themeController = Get.find<ThemeController>();
final primaryColor = themeController.currentThemeColor;
return AppBar(
title: Text(
@@ -55,10 +59,10 @@ class TabbedNavAppBar {
dividerHeight: 0,
dividerColor: Colors.transparent,
tabs: tabLabels.map((String e) => Tab(text: e)).toList(),
labelColor: AppConstants.primaryColor,
labelColor: primaryColor,
unselectedLabelColor: isDark ? Colors.grey[400] : Colors.grey[600],
indicator: UnderlineTabIndicator(
borderSide: BorderSide(color: AppConstants.primaryColor, width: 3),
borderSide: BorderSide(color: primaryColor, width: 3),
),
labelStyle: const TextStyle(fontWeight: FontWeight.bold),
),

View File

@@ -178,7 +178,7 @@ class TapLiquidGlassNavigation extends StatelessWidget {
fontSize: 12,
fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400,
color: isSelected
? AppConstants.primaryColor
? themeController.currentThemeColor
: (isDark ? Colors.grey[400] : Colors.grey[600]),
letterSpacing: 0.15,
),
@@ -193,11 +193,13 @@ class TapLiquidGlassNavigation extends StatelessWidget {
}
Widget _buildIcon(_NavItem item, bool isSelected, bool isDark) {
final themeController = Get.find<ThemeController>();
return Icon(
item.icon,
size: 24,
color: isSelected
? AppConstants.primaryColor
? themeController.currentThemeColor
: (isDark ? Colors.grey[400] : Colors.grey[600]),
);
}