Files
flutterApp/lib/features/my/presentation/web_view/user_info_pages.dart

337 lines
12 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:go_router/go_router.dart';
import 'package:maibu_satabot_v2/core/router/route_paths.dart';
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_cubit.dart';
import 'package:maibu_satabot_v2/features/my/presentation/bloc/my_cubit.dart';
import 'package:maibu_satabot_v2/features/my/presentation/bloc/my_state.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:get_it/get_it.dart';
import '../../../auth/presentation/bloc/auth_cubit.dart';
class UserInfoCard extends StatefulWidget {
final String userName;
final String userId;
const UserInfoCard({super.key, required this.userName, required this.userId});
@override
State<UserInfoCard> createState() => _UserInfoCardState();
}
class _UserInfoCardState extends State<UserInfoCard> {
late TextEditingController _nicknameController;
final GetIt _getIt = GetIt.I;
MyCubit? _myCubit; // 可空类型
@override
void initState() {
super.initState();
// 优先从 BlocProvider 获取,获取失败则从 GetIt 兜底
try {
_myCubit = BlocProvider.of<MyCubit>(context);
} catch (e) {
debugPrint('从 BlocProvider 获取 MyCubit 失败:$e,尝试从 GetIt 获取');
try {
_myCubit = _getIt<MyCubit>();
} catch (e) {
debugPrint('从 GetIt 获取 MyCubit 也失败:$e');
}
}
_nicknameController = TextEditingController(text: widget.userName);
}
@override
void dispose() {
_nicknameController.dispose();
super.dispose();
}
// 打开修改昵称的底部抽屉
void _openEditNicknameBottomSheet() {
// 修复1:先校验 _myCubit 是否为 null,避免空断言
final myCubit = _myCubit;
if (myCubit == null) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Cubit 初始化失败,无法修改昵称'), backgroundColor: Colors.red));
return;
}
showModalBottomSheet(
context: context,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
isScrollControlled: true,
backgroundColor: Colors.transparent,
constraints: BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.8),
builder: (context) => BlocProvider.value(
// 使用非空的局部变量,无需空断言
value: myCubit,
child: BlocBuilder<MyCubit, MyState>(
builder: (context, myState) {
final cubit = context.read<MyCubit>();
if (myState.errorMessage == '登录状态失效,请重新登录') {
context.go(RoutePaths.login);
}
return Padding(
padding: EdgeInsets.only(left: 16, right: 16, top: 20, bottom: MediaQuery.of(context).viewInsets.bottom + 80),
child: Container(
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('修改昵称', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
IconButton(padding: EdgeInsets.zero, icon: const Icon(Icons.close, size: 20), onPressed: () => Navigator.pop(context)),
],
),
const SizedBox(height: 16),
const Text('新昵称', style: TextStyle(fontSize: 12, color: Colors.grey)),
const SizedBox(height: 8),
TextField(
controller: _nicknameController,
style: const TextStyle(fontSize: 16),
decoration: InputDecoration(
border: const OutlineInputBorder(),
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
),
autofocus: true,
enabled: !myState.isLoading,
),
const SizedBox(height: 20),
// 核心:根据 MyState 显示加载/按钮/错误提示
if (myState.isLoading)
const Center(
child: Column(
children: [
CircularProgressIndicator(color: Colors.blue),
SizedBox(height: 8),
Text("修改中...", style: TextStyle(color: Colors.grey)),
],
),
)
else
Column(
children: [
// 修复2:使用 ?. 和 ?? 避免空断言,安全判断错误信息
if (myState.errorMessage?.isNotEmpty ?? false)
Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Text(myState.errorMessage ?? "修改失败", style: const TextStyle(color: Colors.red, fontSize: 14)),
),
// 确定修改按钮
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () => _submitNewNickname(context),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
child: const Text('确定修改', style: TextStyle(fontSize: 16, color: Colors.white)),
),
),
],
),
],
),
),
),
);
},
),
),
);
}
// 提交新昵称的业务逻辑
Future<void> _submitNewNickname(BuildContext context) async {
final newNickname = _nicknameController.text.trim();
if (newNickname.isEmpty) {
Navigator.pop(context);
return;
}
final myCubit = _myCubit;
if (myCubit == null) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Cubit 初始化失败'), backgroundColor: Colors.red));
}
Navigator.pop(context);
return;
}
// 移除重复的加载弹窗(由 BlocBuilder 显示加载状态)
await myCubit.updateName(newNickname);
// 成功后由 BlocBuilder 监听状态并处理弹窗关闭和提示
if (context.mounted && myCubit.state.errorMessage!.isEmpty ?? true) {
setState(() {
_nicknameController.text = newNickname;
});
Navigator.pop(context);
// 触发 AuthCubit 重新获取用户信息
}
}
// 扫码绑定设备逻辑(保留)
// 扫码绑定设备逻辑(修复版)
Future<void> _handleScanAndBind(BuildContext context) async {
bool isScanned = false;
// 1. 扫码逻辑 (保持不变)
final String? deviceCode = await Navigator.push<String>(
context,
MaterialPageRoute(
builder: (context) => Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
title: const Text('扫码绑定设备'),
backgroundColor: Colors.black54,
leading: IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),
),
body: MobileScanner(
onDetect: (BarcodeCapture capture) {
if (isScanned) return;
final List<Barcode> barcodes = capture.barcodes;
if (barcodes.isNotEmpty) {
final String? code = barcodes.first.rawValue;
if (code != null && code.isNotEmpty) {
isScanned = true;
WidgetsBinding.instance.addPostFrameCallback((_) {
if (context.mounted && Navigator.canPop(context)) {
Navigator.pop(context, code);
}
});
}
}
},
),
),
),
);
if (deviceCode == null || deviceCode.isEmpty) {
return;
}
// 2. 确认对话框 (保持不变)
final bool? confirmBind = await showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
title: const Text('确认绑定设备'),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('设备信息:'),
const SizedBox(height: 8),
Text('设备编码:$deviceCode'),
const Text('设备状态:未绑定'),
const Text('设备类型:智能设备'),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text('取消'),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, true),
child: const Text('确定绑定'),
style: ElevatedButton.styleFrom(backgroundColor: Colors.blue),
),
],
),
);
if (confirmBind != true) {
return;
}
// 3. 显示加载框:保存加载弹窗的 BuildContext
BuildContext? loadingContext; // 新增:保存加载弹窗的上下文
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
loadingContext = context; // 保存这个 context
return const Center(child: CircularProgressIndicator());
},
);
try {
final devicesCubit = GetIt.I<DevicesCubit>();
await devicesCubit.bindDevice(deviceCode, '');
// ✅ 成功:用 loadingContext 关闭弹窗
if (loadingContext != null && Navigator.canPop(loadingContext!)) {
Navigator.pop(loadingContext!);
}
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('设备绑定成功!'), backgroundColor: Colors.green),
);
}
} catch (e) {
// ❌ 失败:用 loadingContext 关闭弹窗
if (loadingContext != null && Navigator.canPop(loadingContext!)) {
Navigator.pop(loadingContext!);
}
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('绑定失败:${e.toString()}'),
backgroundColor: Colors.red,
duration: const Duration(seconds: 4),
),
);
}
debugPrint('设备绑定失败详情:$e');
}
}
@override
Widget build(BuildContext context) {
return Row(
children: [
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
image: DecorationImage(image: NetworkImage('https://pic4.zhimg.com/v2-c34c61a90095abb8713de9d1dca7ec7b_r.jpg'), fit: BoxFit.cover),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(_nicknameController.text, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
Text('用户 id:${widget.userId}', style: const TextStyle(fontSize: 12, color: Colors.grey)),
],
),
),
IconButton(icon: const Icon(Icons.edit), onPressed: _openEditNicknameBottomSheet),
const Spacer(),
IconButton(icon: const Icon(Icons.add), onPressed: () => _handleScanAndBind(context)),
],
);
}
}