366 lines
14 KiB
Dart
366 lines
14 KiB
Dart
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('网络错误,请稍后重试');
|
||
});
|
||
},
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|