feat(frontend): 实现我的页面其他设置4项功能
- 消息通知: 添加开关控制,状态持久化到SharedPreferences - 深色模式: 添加开关控制,状态持久化到SharedPreferences - 帮助中心: 新建页面,包含常见问题FAQ和联系方式 - 关于我们: 新建页面,包含应用简介、功能特点、联系方式和法律条款 新增文件: - settings_providers.dart: 设置状态管理 - help_center_page.dart: 帮助中心页面 - about_page.dart: 关于我们页面 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
1e33ab178d
commit
d5f3f3b868
|
|
@ -78,6 +78,8 @@ services:
|
|||
KAFKA_BROKERS: kafka:29092
|
||||
# JWT 配置 (与 auth-service 共享密钥以验证 token)
|
||||
JWT_SECRET: ${JWT_SECRET:-your-jwt-secret-change-in-production}
|
||||
# 2.0 内部服务调用
|
||||
CONTRIBUTION_SERVICE_URL: http://contribution-service:3020
|
||||
ports:
|
||||
- "3021:3021"
|
||||
healthcheck:
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ import '../../presentation/pages/c2c/c2c_publish_page.dart';
|
|||
import '../../presentation/pages/c2c/c2c_order_detail_page.dart';
|
||||
import '../../presentation/pages/profile/team_page.dart';
|
||||
import '../../presentation/pages/profile/trading_records_page.dart';
|
||||
import '../../presentation/pages/profile/help_center_page.dart';
|
||||
import '../../presentation/pages/profile/about_page.dart';
|
||||
import '../../presentation/widgets/main_shell.dart';
|
||||
import '../../presentation/providers/user_providers.dart';
|
||||
import 'routes.dart';
|
||||
|
|
@ -155,6 +157,14 @@ final appRouterProvider = Provider<GoRouter>((ref) {
|
|||
path: Routes.tradingRecords,
|
||||
builder: (context, state) => const TradingRecordsPage(),
|
||||
),
|
||||
GoRoute(
|
||||
path: Routes.helpCenter,
|
||||
builder: (context, state) => const HelpCenterPage(),
|
||||
),
|
||||
GoRoute(
|
||||
path: Routes.about,
|
||||
builder: (context, state) => const AboutPage(),
|
||||
),
|
||||
ShellRoute(
|
||||
builder: (context, state, child) => MainShell(child: child),
|
||||
routes: [
|
||||
|
|
|
|||
|
|
@ -23,4 +23,7 @@ class Routes {
|
|||
static const String myTeam = '/my-team';
|
||||
// 交易记录
|
||||
static const String tradingRecords = '/trading-records';
|
||||
// 其他设置
|
||||
static const String helpCenter = '/help-center';
|
||||
static const String about = '/about';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,437 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
/// 关于我们页面
|
||||
class AboutPage extends StatefulWidget {
|
||||
const AboutPage({super.key});
|
||||
|
||||
@override
|
||||
State<AboutPage> createState() => _AboutPageState();
|
||||
}
|
||||
|
||||
class _AboutPageState extends State<AboutPage> {
|
||||
static const Color _orange = Color(0xFFFF6B00);
|
||||
static const Color _darkText = Color(0xFF1F2937);
|
||||
static const Color _grayText = Color(0xFF6B7280);
|
||||
static const Color _bgGray = Color(0xFFF3F4F6);
|
||||
|
||||
String _version = '';
|
||||
String _buildNumber = '';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadPackageInfo();
|
||||
}
|
||||
|
||||
Future<void> _loadPackageInfo() async {
|
||||
try {
|
||||
final info = await PackageInfo.fromPlatform();
|
||||
setState(() {
|
||||
_version = info.version;
|
||||
_buildNumber = info.buildNumber;
|
||||
});
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
_version = '1.0.0';
|
||||
_buildNumber = '1';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: _bgGray,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back_ios, color: _darkText, size: 20),
|
||||
onPressed: () => context.pop(),
|
||||
),
|
||||
title: const Text(
|
||||
'关于我们',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: _darkText,
|
||||
),
|
||||
),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 32),
|
||||
// Logo和应用名称
|
||||
_buildAppHeader(),
|
||||
const SizedBox(height: 32),
|
||||
// 应用简介
|
||||
_buildIntroSection(),
|
||||
const SizedBox(height: 16),
|
||||
// 功能特点
|
||||
_buildFeaturesSection(),
|
||||
const SizedBox(height: 16),
|
||||
// 联系信息
|
||||
_buildContactSection(),
|
||||
const SizedBox(height: 16),
|
||||
// 版本信息和法律条款
|
||||
_buildLegalSection(),
|
||||
const SizedBox(height: 24),
|
||||
// 版权信息
|
||||
_buildCopyright(),
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAppHeader() {
|
||||
return Column(
|
||||
children: [
|
||||
// 应用图标
|
||||
Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(
|
||||
color: _orange,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: _orange.withOpacity(0.3),
|
||||
blurRadius: 20,
|
||||
offset: const Offset(0, 8),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.eco,
|
||||
color: Colors.white,
|
||||
size: 48,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// 应用名称
|
||||
const Text(
|
||||
'股行',
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: _darkText,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
// 版本号
|
||||
Text(
|
||||
'Version $_version${_buildNumber.isNotEmpty ? ' ($_buildNumber)' : ''}',
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: _grayText,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildIntroSection() {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionTitle('应用简介'),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'股行是一款创新的数字资产管理平台,致力于为用户提供便捷、安全的榴莲树认种和积分管理服务。'
|
||||
'通过认种榴莲树,用户可以获得贡献值,并根据贡献值占比获得每日积分股分配。',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: _grayText.withOpacity(0.9),
|
||||
height: 1.6,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFeaturesSection() {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionTitle('核心功能'),
|
||||
const SizedBox(height: 16),
|
||||
_buildFeatureItem(
|
||||
icon: Icons.eco,
|
||||
title: '榴莲认种',
|
||||
description: '认种真实榴莲树,获得贡献值奖励',
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildFeatureItem(
|
||||
icon: Icons.trending_up,
|
||||
title: '每日收益',
|
||||
description: '根据贡献值占比,每日自动分配积分股',
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildFeatureItem(
|
||||
icon: Icons.swap_horiz,
|
||||
title: '便捷交易',
|
||||
description: '支持积分股卖出兑换,积分值自由转账',
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildFeatureItem(
|
||||
icon: Icons.people,
|
||||
title: '团队收益',
|
||||
description: '邀请好友,获得团队贡献值奖励',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFeatureItem({
|
||||
required IconData icon,
|
||||
required String title,
|
||||
required String description,
|
||||
}) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: _orange.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Icon(icon, color: _orange, size: 22),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: _darkText,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
description,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: _grayText.withOpacity(0.9),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContactSection() {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionTitle('联系方式'),
|
||||
const SizedBox(height: 16),
|
||||
_buildContactItem(
|
||||
icon: Icons.email_outlined,
|
||||
label: '客服邮箱',
|
||||
value: 'support@guhang.com',
|
||||
onTap: () {
|
||||
Clipboard.setData(const ClipboardData(text: 'support@guhang.com'));
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('邮箱已复制'),
|
||||
duration: Duration(seconds: 1),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const Divider(height: 24),
|
||||
_buildContactItem(
|
||||
icon: Icons.language,
|
||||
label: '官方网站',
|
||||
value: 'www.guhang.com',
|
||||
onTap: () {
|
||||
// TODO: 打开官网
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContactItem({
|
||||
required IconData icon,
|
||||
required String label,
|
||||
required String value,
|
||||
VoidCallback? onTap,
|
||||
}) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(icon, size: 20, color: _orange),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
'$label: ',
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: _grayText,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
value,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: _darkText,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (onTap != null)
|
||||
const Icon(Icons.chevron_right, size: 20, color: _grayText),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLegalSection() {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionTitle('法律条款'),
|
||||
const SizedBox(height: 16),
|
||||
_buildLegalItem(
|
||||
title: '用户协议',
|
||||
onTap: () {
|
||||
// TODO: 跳转用户协议页面
|
||||
},
|
||||
),
|
||||
const Divider(height: 24),
|
||||
_buildLegalItem(
|
||||
title: '隐私政策',
|
||||
onTap: () {
|
||||
// TODO: 跳转隐私政策页面
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLegalItem({
|
||||
required String title,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: _darkText,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Icon(Icons.chevron_right, size: 20, color: _grayText),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSectionTitle(String title) {
|
||||
return Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 4,
|
||||
height: 16,
|
||||
decoration: BoxDecoration(
|
||||
color: _orange,
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: _darkText,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCopyright() {
|
||||
return Column(
|
||||
children: [
|
||||
Text(
|
||||
'Copyright © ${DateTime.now().year} 股行',
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: _grayText,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
const Text(
|
||||
'All Rights Reserved',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: _grayText,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,276 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
/// 帮助中心页面
|
||||
class HelpCenterPage extends StatelessWidget {
|
||||
const HelpCenterPage({super.key});
|
||||
|
||||
static const Color _orange = Color(0xFFFF6B00);
|
||||
static const Color _darkText = Color(0xFF1F2937);
|
||||
static const Color _grayText = Color(0xFF6B7280);
|
||||
static const Color _bgGray = Color(0xFFF3F4F6);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: _bgGray,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back_ios, color: _darkText, size: 20),
|
||||
onPressed: () => context.pop(),
|
||||
),
|
||||
title: const Text(
|
||||
'帮助中心',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: _darkText,
|
||||
),
|
||||
),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
// 常见问题分类
|
||||
_buildSection('常见问题', [
|
||||
_FAQItem(
|
||||
question: '如何认种榴莲树?',
|
||||
answer: '在首页点击"去认种"按钮,选择认种数量和支付方式,完成支付后即可认种成功。认种成功后,您将获得对应的贡献值。',
|
||||
),
|
||||
_FAQItem(
|
||||
question: '贡献值是什么?',
|
||||
answer: '贡献值是您在平台认种榴莲树后获得的一种权益凭证。贡献值越高,您每日可获得的积分股分配越多。贡献值有效期为730天。',
|
||||
),
|
||||
_FAQItem(
|
||||
question: '如何获得积分股?',
|
||||
answer: '系统每日会根据您的贡献值占比,自动分配积分股到您的账户。积分股可用于兑换或交易。',
|
||||
),
|
||||
_FAQItem(
|
||||
question: '积分值和积分股有什么区别?',
|
||||
answer: '积分股是通过贡献值分配获得的,可用于卖出兑换。积分值是一种通用积分,可以转账给其他用户。两者用途不同,请注意区分。',
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
_buildSection('交易相关', [
|
||||
_FAQItem(
|
||||
question: '如何卖出积分股?',
|
||||
answer: '进入"兑换"页面,输入要卖出的积分股数量,确认后即可完成卖出。卖出时会扣除10%进入积分股池。',
|
||||
),
|
||||
_FAQItem(
|
||||
question: '卖出积分股为什么要扣除10%?',
|
||||
answer: '卖出积分股时,10%会进入积分股池,用于系统生态建设和价值稳定。这是系统规则的一部分。',
|
||||
),
|
||||
_FAQItem(
|
||||
question: '如何发送积分值给其他用户?',
|
||||
answer: '进入"资产"页面,点击"发送"按钮,输入对方手机号和转账金额,确认后即可完成转账。注意:积分值转账不可撤销。',
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
_buildSection('账户安全', [
|
||||
_FAQItem(
|
||||
question: '如何修改登录密码?',
|
||||
answer: '进入"我的"页面,点击"账户安全",可以修改登录密码。建议定期更换密码以确保账户安全。',
|
||||
),
|
||||
_FAQItem(
|
||||
question: '忘记密码怎么办?',
|
||||
answer: '在登录页面点击"忘记密码",通过手机号验证后可以重置密码。',
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
_buildSection('团队收益', [
|
||||
_FAQItem(
|
||||
question: '如何邀请好友?',
|
||||
answer: '您的手机号就是您的邀请码。好友注册时填写您的手机号作为邀请人,即可建立引荐关系。',
|
||||
),
|
||||
_FAQItem(
|
||||
question: '团队收益如何计算?',
|
||||
answer: '当您引荐的好友认种榴莲树后,您将获得团队下级贡献值奖励。引荐的用户越多、认种数量越多,您的团队收益越高。',
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
// 联系客服
|
||||
_buildContactSection(context),
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSection(String title, List<_FAQItem> items) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 4,
|
||||
height: 16,
|
||||
decoration: BoxDecoration(
|
||||
color: _orange,
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: _darkText,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
...items.asMap().entries.map((entry) {
|
||||
final isLast = entry.key == items.length - 1;
|
||||
return _buildFAQTile(entry.value, showDivider: !isLast);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFAQTile(_FAQItem item, {bool showDivider = true}) {
|
||||
return Theme(
|
||||
data: ThemeData(
|
||||
dividerColor: Colors.transparent,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
ExpansionTile(
|
||||
tilePadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
childrenPadding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
|
||||
title: Text(
|
||||
item.question,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: _darkText,
|
||||
),
|
||||
),
|
||||
iconColor: _orange,
|
||||
collapsedIconColor: _grayText,
|
||||
children: [
|
||||
Text(
|
||||
item.answer,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: _grayText.withOpacity(0.9),
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (showDivider)
|
||||
const Divider(height: 1, indent: 16, endIndent: 16),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContactSection(BuildContext context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 4,
|
||||
height: 16,
|
||||
decoration: BoxDecoration(
|
||||
color: _orange,
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const Text(
|
||||
'联系我们',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: _darkText,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'如果您有其他问题,可以通过以下方式联系我们:',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: _grayText.withOpacity(0.9),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildContactItem(
|
||||
icon: Icons.email_outlined,
|
||||
label: '客服邮箱',
|
||||
value: 'support@guhang.com',
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildContactItem(
|
||||
icon: Icons.access_time,
|
||||
label: '服务时间',
|
||||
value: '周一至周五 9:00-18:00',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContactItem({
|
||||
required IconData icon,
|
||||
required String label,
|
||||
required String value,
|
||||
}) {
|
||||
return Row(
|
||||
children: [
|
||||
Icon(icon, size: 20, color: _orange),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
'$label: ',
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
color: _grayText,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
value,
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
color: _darkText,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _FAQItem {
|
||||
final String question;
|
||||
final String answer;
|
||||
|
||||
_FAQItem({required this.question, required this.answer});
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import '../../../core/router/routes.dart';
|
||||
import '../../providers/user_providers.dart';
|
||||
import '../../providers/profile_providers.dart';
|
||||
import '../../providers/settings_providers.dart';
|
||||
import '../../widgets/shimmer_loading.dart';
|
||||
|
||||
class ProfilePage extends ConsumerWidget {
|
||||
|
|
@ -78,7 +78,7 @@ class ProfilePage extends ConsumerWidget {
|
|||
const SizedBox(height: 16),
|
||||
|
||||
// 其他设置
|
||||
_buildOtherSettings(context),
|
||||
_buildOtherSettings(context, ref),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
|
|
@ -463,7 +463,10 @@ class ProfilePage extends ConsumerWidget {
|
|||
);
|
||||
}
|
||||
|
||||
Widget _buildOtherSettings(BuildContext context) {
|
||||
Widget _buildOtherSettings(BuildContext context, WidgetRef ref) {
|
||||
final notificationsEnabled = ref.watch(notificationsEnabledProvider);
|
||||
final darkModeEnabled = ref.watch(darkModeEnabledProvider);
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
|
|
@ -487,24 +490,28 @@ class ProfilePage extends ConsumerWidget {
|
|||
_buildSwitchItem(
|
||||
icon: Icons.notifications_outlined,
|
||||
label: '消息通知',
|
||||
value: true,
|
||||
onChanged: (value) {},
|
||||
value: notificationsEnabled,
|
||||
onChanged: (value) {
|
||||
ref.read(notificationsEnabledProvider.notifier).setEnabled(value);
|
||||
},
|
||||
),
|
||||
_buildSwitchItem(
|
||||
icon: Icons.dark_mode_outlined,
|
||||
label: '深色模式',
|
||||
value: false,
|
||||
onChanged: (value) {},
|
||||
value: darkModeEnabled,
|
||||
onChanged: (value) {
|
||||
ref.read(darkModeEnabledProvider.notifier).setEnabled(value);
|
||||
},
|
||||
),
|
||||
_buildSettingItem(
|
||||
icon: Icons.help_outline,
|
||||
label: '帮助中心',
|
||||
onTap: () {},
|
||||
onTap: () => context.push(Routes.helpCenter),
|
||||
),
|
||||
_buildSettingItem(
|
||||
icon: Icons.info_outline,
|
||||
label: '关于我们',
|
||||
onTap: () {},
|
||||
onTap: () => context.push(Routes.about),
|
||||
showDivider: false,
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
// ==================== 设置键名常量 ====================
|
||||
|
||||
const String _keyNotificationsEnabled = 'settings_notifications_enabled';
|
||||
const String _keyDarkModeEnabled = 'settings_dark_mode_enabled';
|
||||
|
||||
// ==================== 消息通知设置 ====================
|
||||
|
||||
/// 消息通知开关状态 Provider
|
||||
final notificationsEnabledProvider = StateNotifierProvider<NotificationsNotifier, bool>((ref) {
|
||||
return NotificationsNotifier();
|
||||
});
|
||||
|
||||
class NotificationsNotifier extends StateNotifier<bool> {
|
||||
NotificationsNotifier() : super(true) {
|
||||
_loadFromStorage();
|
||||
}
|
||||
|
||||
Future<void> _loadFromStorage() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
state = prefs.getBool(_keyNotificationsEnabled) ?? true;
|
||||
}
|
||||
|
||||
Future<void> toggle() async {
|
||||
state = !state;
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setBool(_keyNotificationsEnabled, state);
|
||||
}
|
||||
|
||||
Future<void> setEnabled(bool enabled) async {
|
||||
state = enabled;
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setBool(_keyNotificationsEnabled, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 深色模式设置 ====================
|
||||
|
||||
/// 深色模式开关状态 Provider
|
||||
final darkModeEnabledProvider = StateNotifierProvider<DarkModeNotifier, bool>((ref) {
|
||||
return DarkModeNotifier();
|
||||
});
|
||||
|
||||
class DarkModeNotifier extends StateNotifier<bool> {
|
||||
DarkModeNotifier() : super(false) {
|
||||
_loadFromStorage();
|
||||
}
|
||||
|
||||
Future<void> _loadFromStorage() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
state = prefs.getBool(_keyDarkModeEnabled) ?? false;
|
||||
}
|
||||
|
||||
Future<void> toggle() async {
|
||||
state = !state;
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setBool(_keyDarkModeEnabled, state);
|
||||
}
|
||||
|
||||
Future<void> setEnabled(bool enabled) async {
|
||||
state = enabled;
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setBool(_keyDarkModeEnabled, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 主题模式 Provider ====================
|
||||
|
||||
/// 应用主题模式 Provider
|
||||
final themeModeProvider = Provider<ThemeMode>((ref) {
|
||||
final isDarkMode = ref.watch(darkModeEnabledProvider);
|
||||
return isDarkMode ? ThemeMode.dark : ThemeMode.light;
|
||||
});
|
||||
|
|
@ -44,6 +44,7 @@ dependencies:
|
|||
# 其他
|
||||
intl: ^0.18.0
|
||||
logger: ^2.0.0
|
||||
package_info_plus: ^8.0.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
|
|||
Loading…
Reference in New Issue