ar_tourism_flutter_unity/lib/pages/mine/setting/myInfo.dart
2025-05-14 17:04:13 +08:00

366 lines
14 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 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:http_parser/http_parser.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart'; // 用于选择图片
import '../../../apis/app.dart';
import '../../../components/appbar.dart';
import '../../../components/gradientButton.dart';
class MyInfoPage extends StatefulWidget {
const MyInfoPage({super.key, required this.userInfo});
final Map userInfo;
@override
State<MyInfoPage> createState() => _MyInfoPageState();
}
class _MyInfoPageState extends State<MyInfoPage> {
final TextEditingController _nameController = TextEditingController();
final TextEditingController _introController = TextEditingController();
XFile? _imageFile; // 用于存储选中的图片
// 添加字数计数器
int _nameLength = 0;
int _introLength = 0;
@override
void initState() {
super.initState();
print("我的信息页面接受到的参数:${widget.userInfo}");
// 初始化文本框的内容
_nameController.text = widget.userInfo["nickName"] ?? "";
_introController.text = widget.userInfo["signature"] ?? "";
// 初始化字数计数
_nameLength = _nameController.text.length;
_introLength = _introController.text.length;
// 添加文本变化监听器
_nameController.addListener(() {
setState(() {
_nameLength = _nameController.text.length;
});
});
_introController.addListener(() {
setState(() {
_introLength = _introController.text.length;
});
});
}
// 检查文本是否包含表情符号
bool _containsEmoji(String text) {
// 表情符号的Unicode范围
final RegExp emojiRegExp = RegExp(
r'(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])',
);
return emojiRegExp.hasMatch(text);
}
// 显示错误提示
void _showErrorSnackBar(String message) {
if (!mounted) return;
// 去掉当前可能存在的SnackBar
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Center(
child: Text(
message,
style: const TextStyle(
color: Colors.white,
fontSize: 14,
),
),
),
backgroundColor: const Color.fromRGBO(43, 46, 61, 0.9),
behavior: SnackBarBehavior.floating,
margin: EdgeInsets.only(
bottom: MediaQuery.of(context).size.height - 120,
left: 20,
right: 20,
),
duration: const Duration(seconds: 2),
),
);
}
// 添加焦点控制器
final FocusNode _introFocusNode = FocusNode();
@override
void dispose() {
_introFocusNode.dispose();
super.dispose();
}
// 选择头像图片
Future<void> _pickImage() async {
final ImagePicker picker = ImagePicker();
XFile? image = await picker.pickImage(source: ImageSource.gallery);
setState(() {
_imageFile = image; // 保存选中的图片
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false, // 解决点击输入框弹出键盘导致底部溢出的问题
appBar: const CustomAppBar(title: '我的信息'),
body: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
decoration: const BoxDecoration(
color: Color(0xff0C0C16),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 第一行 - 居中的头像和camera图标
Center(
child: Stack(
children: [
// 头像
CircleAvatar(
radius: 45,
backgroundImage: _imageFile != null
? FileImage(File(_imageFile!.path))
: NetworkImage(widget.userInfo["avatar"])
as ImageProvider<Object>,
),
// 相机按钮定位在右下角
Positioned(
bottom: -10,
right: -10,
child: IconButton(
icon: Image.asset(
'images/camera.png',
width: 30,
),
onPressed: _pickImage,
),
),
],
),
),
const SizedBox(height: 20),
// 第二行 - 名字
const Text(
'昵称',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white),
),
const SizedBox(height: 10),
// 第三行 - 背景为1F212E的矩形输入框
Container(
child: Stack(
children: [
TextFormField(
controller: _nameController,
maxLength: 24,
maxLines: 2,
style: const TextStyle(
color: Color.fromRGBO(255, 255, 255, 0.5)),
decoration: InputDecoration(
fillColor: const Color(0xFF1F212E),
counterText: '', // 隐藏默认计数器
filled: true,
hintText: widget.userInfo["nickname"]?.toString() ??
"请输入姓名",
hintStyle: const TextStyle(
color: Color.fromRGBO(255, 255, 255, 0.5)),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide:
const BorderSide(color: Color(0xFF1F212E)),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide:
const BorderSide(color: Color(0xff1F212E)),
),
),
),
Positioned(
right: 8,
bottom: 8,
child: Text(
'$_nameLength/24',
style: const TextStyle(
color: Color.fromRGBO(255, 255, 255, 0.5),
fontSize: 12,
),
),
),
],
),
),
const SizedBox(height: 20),
// 第四行 - 介绍
const Text(
'介绍',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white),
),
const SizedBox(height: 10),
// 第五行 - 背景为1F212E的矩形输入框
Container(
child: Stack(
children: [
TextFormField(
controller: _introController,
maxLength: 45,
maxLines: 3,
style: const TextStyle(
color: Color.fromRGBO(255, 255, 255, 0.5)),
decoration: InputDecoration(
fillColor: const Color(0xFF1F212E),
counterText: '', // 隐藏默认计数器
filled: true,
hintText: "",
hintStyle: const TextStyle(
color: Color.fromRGBO(255, 255, 255, 0.5)),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide:
const BorderSide(color: Color(0xFF1F212E)),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide:
const BorderSide(color: Color(0xff1F212E)),
),
),
),
Positioned(
right: 8,
bottom: 8,
child: Text(
'$_introLength/45',
style: const TextStyle(
color: Color.fromRGBO(255, 255, 255, 0.5),
fontSize: 12,
),
),
),
],
),
),
const SizedBox(height: 20),
],
),
Positioned(
bottom: 50,
left: 0,
right: 0,
child: GradientButton(
text: '保存修改',
onPressed: () async {
// 检查昵称和介绍是否包含表情符号
if (_containsEmoji(_nameController.text)) {
_showErrorSnackBar('昵称不可输入表情');
return;
}
if (_containsEmoji(_introController.text)) {
_showErrorSnackBar('介绍不可输入表情');
return;
}
var infoMsg = {
"username": widget.userInfo["phone"],
'nickname': _nameController.text,
'signature': _introController.text,
};
var infoMsgJson = jsonEncode(infoMsg);
print("infoMsgJson: $infoMsgJson");
FormData formData;
if (_imageFile != null) {
print("选择了新头像");
// 如果选择了新头像,则上传新头像
File avatarFile = File(_imageFile!.path);
formData = FormData.fromMap({
"avatar": await MultipartFile.fromFile(
avatarFile.path,
filename: "avatar.jpg",
contentType: MediaType("image", "jpg"),
),
"updateNickSignDto": infoMsgJson,
});
} else {
print("没有选择新头像");
// 如果没有选择新头像,只更新其他信息
// 创建一个空文件
formData = FormData.fromMap({
"avatar": MultipartFile.fromBytes(
Uint8List(0), // 空字节数组
filename: "empty.txt",
),
"updateNickSignDto": infoMsgJson,
});
}
userApi.getUpdateUserInfo(formData).then((result) {
if (result['code'] == 200) {
print("更新用户信息成功");
Navigator.pushNamedAndRemoveUntil(
context,
'/tabs',
(route) => false,
arguments: {'initialIndex': 4},
);
} else {
print("更新失败:${result['msg']}");
// 检查是否包含"表情"关键词
if (result['msg']?.toString().contains('表情') ?? false) {
_showErrorSnackBar('不可输入表情');
} else {
// 显示其他错误提示
_showErrorSnackBar('更新失败: ${result['msg']}');
}
}
}).catchError((error) {
print("发送请求时发生错误: $error");
// 显示网络错误提示
_showErrorSnackBar('网络错误,请稍后重试');
});
},
),
),
],
),
),
),
);
}
}