jjsos_autosos_flutter/lib/pages/me/me_page.dart

329 lines
9.9 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import 'package:autosos_flutter/api/_api_client.dart';
import 'package:autosos_flutter/model/user.dart';
import 'package:autosos_flutter/provider/providers.dart';
import 'package:autosos_flutter/router/app_router.dart';
import 'package:autosos_flutter/util/sp_util.dart';
import 'package:autosos_flutter/widget/multi_state_view.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
/// 我的中心(对应 Android MeFragment
class MePage extends ConsumerStatefulWidget {
const MePage({super.key});
@override
ConsumerState<MePage> createState() => _MePageState();
}
class _MePageState extends ConsumerState<MePage> {
MultiState _state = MultiState.loading;
UserInfo? _user;
@override
void initState() {
super.initState();
_load();
}
Future<void> _load() async {
setState(() => _state = MultiState.loading);
try {
final resp = await ref.read(userApiProvider).info();
if (!mounted) return;
if (!resp.isSuccess || resp.data == null) {
setState(() => _state = MultiState.error);
return;
}
ref.read(currentUserProvider.notifier).state = resp.data;
setState(() {
_user = resp.data;
_state = MultiState.success;
});
} catch (e) {
if (mounted) setState(() => _state = MultiState.error);
}
}
Future<void> _logout() async {
final ok = await showDialog<bool>(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('退出登录'),
content: const Text('确定要退出当前账号吗?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx, false),
child: const Text('取消'),
),
ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
onPressed: () => Navigator.pop(ctx, true),
child: const Text('退出'),
),
],
),
);
if (ok != true) return;
EasyLoading.show(status: '退出中…');
try {
await ref.read(userApiProvider).logout();
} catch (_) {}
// 清 token + sp
SPUtil().remove('accessToken');
SPUtil().remove('user');
ApiClient.instance.clearToken();
ref.read(accessTokenProvider.notifier).state = null;
ref.read(currentUserProvider.notifier).state = null;
EasyLoading.dismiss();
if (mounted) context.go(Routes.login);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey.shade100,
body: RefreshIndicator(
onRefresh: _load,
child: MultiStateView(
state: _state,
onRetry: _load,
child: _user == null
? const SizedBox.shrink()
: ListView(
children: [
_header(_user!),
const SizedBox(height: 8),
_statsRow(_user!),
const SizedBox(height: 8),
_menu(),
const SizedBox(height: 24),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: OutlinedButton.icon(
onPressed: _logout,
icon: const Icon(Icons.logout, color: Colors.red),
label: const Text(
'退出登录',
style: TextStyle(color: Colors.red),
),
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Colors.red),
minimumSize: const Size.fromHeight(44),
),
),
),
],
),
),
),
);
}
Widget _header(UserInfo u) {
return Container(
padding: const EdgeInsets.fromLTRB(16, 36, 16, 16),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.red.shade400, Colors.red.shade700],
),
),
child: Row(
children: [
CircleAvatar(
radius: 32,
backgroundColor: Colors.white,
child: u.avatar != null && u.avatar!.isNotEmpty
? ClipOval(
child: CachedNetworkImage(
imageUrl: u.avatar!,
width: 60,
height: 60,
fit: BoxFit.cover,
),
)
: Text(
(u.realName?.isNotEmpty == true
? u.realName![0]
: (u.username?.isNotEmpty == true ? u.username![0] : '?'))
.toUpperCase(),
style: const TextStyle(
fontSize: 24,
color: Colors.red,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
u.realName ?? u.username ?? '',
style: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Row(
children: [
Text(u.phone ?? '',
style: const TextStyle(color: Colors.white70)),
const SizedBox(width: 8),
_badge(u.authStatus ?? 'none'),
],
),
if (u.company != null && u.company!.isNotEmpty) ...[
const SizedBox(height: 4),
Text(u.company!,
style: const TextStyle(color: Colors.white70)),
],
],
),
),
IconButton(
onPressed: () => context.push(Routes.setting),
icon: const Icon(Icons.settings, color: Colors.white),
),
],
),
);
}
Widget _badge(String status) {
Color bg;
String text;
switch (status) {
case 'personal':
bg = Colors.blue;
text = '个人认证';
break;
case 'enterprise':
bg = Colors.purple;
text = '企业认证';
break;
case 'driver':
bg = Colors.orange;
text = '司机认证';
break;
default:
bg = Colors.grey;
text = '未认证';
}
return Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: bg,
borderRadius: BorderRadius.circular(4),
),
child: Text(text,
style: const TextStyle(color: Colors.white, fontSize: 11)),
);
}
Widget _statsRow(UserInfo u) {
return Container(
color: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 12),
child: Row(
children: [
_stat('余额', '¥${u.balance?.toStringAsFixed(2) ?? "0.00"}'),
_divider(),
_stat('今日订单', '${u.todayOrders ?? 0}'),
_divider(),
_stat('累计订单', '${u.totalOrders ?? 0}'),
],
),
);
}
Widget _stat(String label, String value) {
return Expanded(
child: Column(
children: [
Text(value,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.red,
)),
const SizedBox(height: 2),
Text(label,
style: const TextStyle(fontSize: 12, color: Colors.grey)),
],
),
);
}
Widget _divider() =>
Container(width: 1, height: 30, color: Colors.grey.shade300);
Widget _menu() {
return Container(
color: Colors.white,
child: Column(
children: [
_menuItem(Icons.account_balance_wallet, '提现', () {
context.push(Routes.withdrawal);
}),
_menuItem(Icons.history, '提现记录', () {
context.push(Routes.withdrawalRecord);
}),
_menuItem(Icons.payment, '充值', () {
context.push(Routes.recharge);
}),
_menuItem(Icons.list_alt, '金额明细', () {
context.push(Routes.amountList);
}),
_dividerLine(),
_menuItem(Icons.edit, '完善资料', () {
context.push(Routes.perfect);
}),
_menuItem(Icons.verified_user, '认证', () {
context.push(Routes.auth);
}),
_dividerLine(),
_menuItem(Icons.message, '消息中心', () {
context.push(Routes.messenger);
}),
_menuItem(Icons.bug_report, '问题反馈', () {
context.push(Routes.feedback);
}),
_menuItem(Icons.tune, '性能优化', () {
context.push(Routes.optimization);
}),
],
),
);
}
Widget _menuItem(IconData icon, String label, VoidCallback onTap) {
return InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
child: Row(
children: [
Icon(icon, color: Colors.red, size: 22),
const SizedBox(width: 12),
Expanded(child: Text(label, style: const TextStyle(fontSize: 15))),
const Icon(Icons.chevron_right, color: Colors.grey),
],
),
),
);
}
Widget _dividerLine() => Divider(
height: 1,
thickness: 1,
color: Colors.grey.shade200,
indent: 16,
);
}