目前我确实应用了新的http POST 请求来上传个人资料图片。我有几个页面需要个人资料图片,例如个人资料页面和我的详细信息页面。现在,每次我上传新的个人资料图片时,详细信息页面中的个人资料图片都不会立即更新。我需要导航回个人资料页面并返回我的详细信息页面,然后更新个人资料图片。我已经被困在这件事上 5 天了。谁能帮助我吗?
目前,这是我的实现。我尝试在堆栈中使用 blocbuilder,但上传新图像后,它只是不断重新加载。需要返回主页导航到个人资料页面,然后导航到我的详细信息页面,然后它就会正常工作。我还尝试将 NetworkImage() 更改为 FileImage() if image != null 但后来出现此错误 Cannot检索文件长度,path = 'https://abcde.my/0/12/1' (操作系统错误: 没有这样的文件或目录,errno = 2)。我也尝试改变我构造 pickImageFromSource() 的方式,但还是一样,个人资料图片没有立即更新。
我期待在用户选择新图像后个人资料图片立即更新。无需前往个人资料页面即可在我的详细信息页面中获取最新的个人资料图片。
API服务
Future<http.Response> uploadProfilePicture(File? file) async {
final List<int> imageBytes = file!.readAsBytesSync();
final base64Image = base64Encode(imageBytes);
final userLoginToken = await AppSharedPreferences.getUserLoginToken();
try {
final timeStamp = DateTimeHelper().apiRequiredTimeStamp();
final signature = await EncryptionService.apiSignature(
apiCodeName: 'UploadProfilePic',
timeStamp: timeStamp,
hasLoginToken: true);
var body = {
"Channel": "B",
"LoginToken": userLoginToken,
"ProfileImgFileContent": base64Image,
"ProfileImgFileName":
file.path.split('/').last, // Use the file name from the path
"ProfileImgFileSize": imageBytes.length.toString(),
"ProfileImgFileType":
'image/${file.path.split('.').last.toLowerCase()}', // Use the file extension for type
};
final response = await http.post(
Uri.parse('${AppConfig.development().apiBaseUrl}/UploadProfilePic'),
headers: {
"Content-Type": "application/json"
},
body: jsonEncode(body),
);
print('upload pp timestamp: $timeStamp');
print('upload pp signature: $signature');
print('upload pp response status code: ${response.statusCode}');
return response;
} catch (error) {
throw Exception('Failed to update profile picture.');
}
}
存储库
class ProfileContentRepository {
static final ProfileContentRepository _profileContentRepository =
ProfileContentRepository._internal();
factory ProfileContentRepository() {
return _profileContentRepository;
}
ProfileContentRepository._internal();
final apiService = ApiService();
Future<http.Response> uploadProfilePicture(File? file) async {
return apiService.uploadProfilePicture(file);
}
}
肘内
Future<void> uploadProfilePicture(File? image) async {
final apiResponse =
await profileContentRepository.uploadProfilePicture(image);
if (apiResponse.statusCode == 200) {
print('Profile picture uploaded successfully');
fetchProfileContent();
} else {
emit(ProfileErrorState(
error:
'Failed to upload profile picture: ${apiResponse.statusCode} - ${apiResponse.body}'));
}
}
UI部分
class ProfilePageContent extends StatelessWidget {
final MemberProfile memberProfile;
const ProfilePageContent({
Key? key,
required this.memberProfile,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return MyDetails(
memberProfile: memberProfile,
);
}
}
class MyDetails extends StatefulWidget {
const MyDetails({
required this.memberProfile,
Key? key,
}) : super(key: key);
final MemberProfile memberProfile;
@override
State<MyDetails> createState() => __MyDetailsState();
}
class __MyDetailsState extends State<MyDetails> {
final _formKey = GlobalKey<FormState>();
File? image;
bool isLoading = false;
late ProfileContentCubit profileContentCubit;
@override
void initState() {
profileContentCubit = BlocProvider.of<ProfileContentCubit>(context);
getMemberInfo();
super.initState();
}
getMemberInfo() async {
setState(() {
isLoading = true;
});
image = widget.memberProfile.memberInfo!.profileImage != ""
? File(widget.memberProfile.memberInfo!.profileImage!)
: null;
setState(() {
isLoading = false;
});
}
Future pickImage() async {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Choose Image Source'),
actions: [
TextButton(
onPressed: () async {
Navigator.of(context).pop();
await pickImageFromSource(ImageSource.gallery);
},
child: const Text('Gallery'),
),
TextButton(
onPressed: () async {
Navigator.of(context).pop();
await pickImageFromSource(ImageSource.camera);
},
child: const Text('Camera'),
),
],
);
},
);
}
Future<void> pickImageFromSource(ImageSource source) async {
try {
final pickImg = await ImagePicker().pickImage(source: source);
print('pickIMG $pickImg');
print('BEFOREEE $image');
if (pickImg != null) {
// Perform the asynchronous work outside of setState
final pickedImage = File(pickImg.path);
print('Picked Image Path: ${pickedImage.path}');
// Update the state synchronously
context.read<ProfileContentCubit>().uploadProfilePicture(pickedImage);
setState(() {
evictImage();
image = pickedImage;
print('AFTERR $image');
});
} else {
const Text('No photo was selected or taken');
}
} on PlatformException {
throw Exception('Cannot pick image!');
}
}
void evictImage() {
final NetworkImage provider =
NetworkImage(widget.memberProfile.memberInfo!.profileImage!);
provider.evict().then<void>((bool success) {
if (success) {
debugPrint('CACHEEEEEE removed image!');
}
});
}
@override
Widget build(BuildContext context) {
updateMemberInfo() {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
context
.read<ProfileContentCubit>()
.updateMemberInfo(widget.memberProfile.memberInfo!);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text(
'Profile Updated',
style: TextStyle(
color: Colors.black,
),
),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
);
}
}
return Scaffold(
body: isLoading
? const Center(
child: CircularProgressIndicator(),
)
: SingleChildScrollView(
child: Container(
padding:
const EdgeInsets.symmetric(vertical: 20, horizontal: 20),
child: Column(
children: [
Stack(
children: [
image != null
? SizedBox(
width: 120,
height: 120,
child: CircleAvatar(
radius: 100,
backgroundImage: NetworkImage(widget
.memberProfile.memberInfo!.profileImage!),
),
)
: SizedBox(
width: 120,
height: 120,
child: CircleAvatar(
backgroundColor: Colors.green.shade200,
radius: 100,
child: Text(
widget.memberProfile.memberInfo!.fullName
.trim()[0],
style: const TextStyle(
fontSize: 70,
),
),
),
),
Positioned(
bottom: 0,
right: 0,
child: InkWell(
onTap: pickImage,
child: Container(
width: 35,
height: 35,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100),
color: Theme.of(context)
.colorScheme
.inversePrimary),
child: const Icon(Icons.camera_alt_rounded,
color: Colors.black, size: 20),
),
),
),
],
),
你的肘节中的
fetchProfileContent()
会发出新的状态吗?它不是您发布的代码的一部分。
如果没有,那将是您的问题,因为您正在更新服务器上的图像,并且在 status == 200 时您不会将更新后的状态发送给应用程序。