I am a beginner in flutter development, I want to call the API to update the user profile, in the API one of them consist of 'image' & 'proof' field,that field can be left blank, but the problem is whenever I left those fields empty and click the 'edit' button, I always receive this response from the server :
{
"status": 500,
"message": "The \"path\" argument must be of type string. Received an instance of Array"
}
my purpose is I want to empty 'image' & 'proof' fields if I not choose any image & background image, but if I choose it I want those field is fulfil with the image path
I hope there is someone that can help me to find the solution of this problem,
here is my code :
1
2 class AuthService {
3 static String? token;
4 static String? bioDateId;
5
6 final TokenManager tokenManager = TokenManager();
7 final BiodateIdManager biodateIdManager = BiodateIdManager();
8
9 Future<bool> updateBio({
10 required String firstName,
11 required String lastName,
12 required String birthDate,
13 required String gender,
14 required String phone,
15 required String address,
16 required String province,
17 required String regencies,
18 required String institutionName,
19 required String pupils,
20 required String field,
21 String? image,
22 String? proof,
23 }) async {
24 String? token = await tokenManager.getToken();
25 String? bioDateId = await biodateIdManager.getBiodateId();
26 try {
27
28 Map<String, dynamic> formDataMap = {
29 "firstName": firstName,
30 "lastName": lastName,
31 "birthDate": birthDate,
32 "gender": gender,
33 "phone": phone,
34 "address": address,
35 "province": province,
36 "regencies": regencies,
37 "institutionName": institutionName,
38 "field": field,
39 "pupils": pupils,
40 };
41
42
43 if (proof != null && proof.isNotEmpty) {
44 String? proofMimeType = lookupMimeType(proof);
45 if (proofMimeType != null) {
46 MultipartFile proofMultipartFile = await MultipartFile.fromFile(
47 proof,
48 filename: 'proof.png',
49 contentType: MediaType.parse(proofMimeType),
50 );
51 formDataMap["proof"] = proofMultipartFile;
52 } else {
53 debugPrint('Invalid proof MIME type');
54 }
55 }
56
57 if (image != null && image.isNotEmpty) {
58 String? imageMimeType = lookupMimeType(image);
59 if (imageMimeType != null) {
60 MultipartFile imageMultipartFile = await MultipartFile.fromFile(
61 image,
62 filename: 'image.png',
63 contentType: MediaType.parse(imageMimeType),
64 );
65 formDataMap["image"] = imageMultipartFile;
66 } else {
67 debugPrint('Invalid image MIME type');
68 }
69 }
70
71 var response = await Dio().put(
72 "http://192.168.1.5:3000/user/update-biodate?id=$bioDateId",
73 data: FormData.fromMap(formDataMap),
74 options: Options(
75 headers: {
76 "Content-Type": "multipart/form-data",
77 "Authorization": "Bearer $token"
78 },
79 ),
80 );
81
82 if (response.statusCode == 200) {
83
84 return true;
85 } else {
86 throw Exception('Failed to Update Bio : ${response.data['message']}');
87 }
88 } on DioException catch (e) {
89 if (e.response?.statusCode == 400) {
90 debugPrint('Error 400: ${e.response?.data}');
91 throw Exception('Update Bio failed: ${e.response?.data['message']}');
92 } else {
93 debugPrint('Error: ${e.toString()}');
94 throw Exception('Failed to Update Bio');
95 }
96 }
97 }
98 }
1
2 class UpdateUserController {
3 final AuthService _authService = AuthService();
4 UpdateUserModel updateUserModel = UpdateUserModel();
5
6 TextEditingController firstNameController = TextEditingController();
7 TextEditingController lastNameController = TextEditingController();
8 TextEditingController birthDateController = TextEditingController();
9 TextEditingController genderController = TextEditingController();
10 TextEditingController phoneController = TextEditingController();
11 TextEditingController addressController = TextEditingController();
12 TextEditingController provinceController = TextEditingController();
13 TextEditingController regenciesController = TextEditingController();
14 TextEditingController institutionNameController = TextEditingController();
15 TextEditingController studyFieldController = TextEditingController();
16 TextEditingController uniqueIdController = TextEditingController();
17 TextEditingController imageController = TextEditingController();
18 TextEditingController proofController = TextEditingController();
19
20 String? _selectedImagePath;
21 String? _selectedProofPath;
22
23 void setImagePath(String filePath) {
24 _selectedImagePath = filePath;
25 debugPrint('Selected image path set: $_selectedImagePath');
26 }
27
28 void setProofPath(String filePath) {
29 _selectedProofPath = filePath;
30 debugPrint('Selected proof path set: $_selectedProofPath');
31 }
32
33 void reset() {
34 _selectedImagePath = null;
35 _selectedProofPath = null;
36 firstNameController.clear();
37 lastNameController.clear();
38 birthDateController.clear();
39 genderController.clear();
40 phoneController.clear();
41 addressController.clear();
42 provinceController.clear();
43 regenciesController.clear();
44 institutionNameController.clear();
45 studyFieldController.clear();
46 uniqueIdController.clear();
47 }
48
49 void dispose() {
50 firstNameController.dispose();
51 lastNameController.dispose();
52 birthDateController.dispose();
53 genderController.dispose();
54 phoneController.dispose();
55 addressController.dispose();
56 provinceController.dispose();
57 regenciesController.dispose();
58 institutionNameController.dispose();
59 studyFieldController.dispose();
60 uniqueIdController.dispose();
61 }
62
63 bool fileExists(String path) {
64 return File(path).existsSync();
65 }
66
67 Future<void> updateBio(BuildContext context) async {
68 DialogHelper.showLoading(context);
69
70 String firstName = firstNameController.text.trim();
71 String lastName = lastNameController.text.trim();
72 String birthDate = birthDateController.text.trim();
73 String gender = genderController.text.trim();
74 String phone = phoneController.text.trim();
75 String address = addressController.text.trim();
76 String province = provinceController.text.trim();
77 String regencies = regenciesController.text.trim();
78 String institutionName = institutionNameController.text.trim();
79 String studyField = studyFieldController.text.trim();
80 String uniqueId = uniqueIdController.text.trim();
81
82
83 String? image = _selectedImagePath;
84 String? proof = _selectedProofPath;
85
86 try {
87 bool bioUpdated = await _authService.updateBio(
88 firstName: firstName,
89 lastName: lastName,
90 birthDate: birthDate,
91 gender: gender,
92 phone: phone,
93 address: address,
94 province: province,
95 regencies: regencies,
96 institutionName: institutionName,
97 field: studyField,
98 pupils: uniqueId,
99 image: image,
100 proof: proof,
101 );
102 DialogHelper.hideLoading(context);
103
104 if (bioUpdated) {
105 reset();
106 Navigator.push(
107 context,
108 MaterialPageRoute(
109 builder: (context) => const EduPassApp(
110 initialPageIndex: 4,
111 )));
112 ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
113 content: Text('UpdateBio berhasil'),
114 duration: Duration(seconds: 1),
115 ));
116 debugPrint('UpdateBio berhasil');
117 } else {
118 ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
119 content: Text('UpdateBio gagal'),
120 duration: Duration(seconds: 2),
121 ));
122 debugPrint('UpdateBio gagal');
123 }
124 } catch (e) {
125 DialogHelper.hideLoading(context);
126 ScaffoldMessenger.of(context).showSnackBar(SnackBar(
127 content: Text('Error: $e'),
128 duration: const Duration(seconds: 2),
129 ));
130 debugPrint('Error saat UpdateBio: $e');
131 }
132 }
133 }
1
2 class ProfileDetailUser extends StatefulWidget {
3 const ProfileDetailUser({super.key});
4
5 @override
6 State<ProfileDetailUser> createState() => _ProfileDetailUserState();
7 }
8
9 class _ProfileDetailUserState extends State<ProfileDetailUser> {
10 String? _selectedImagePath;
11 String? _selectedBackgroundPath;
12 final UpdateUserController updateController = UpdateUserController();
13
14 void _handleImageSelected(String filePath) {
15 setState(() {
16 _selectedImagePath = filePath;
17 updateController.setImagePath(filePath);
18
19 });
20 debugPrint('File path: $filePath');
21 }
22
23 void _handleBackgroundSelected(String filePath) {
24 setState(() {
25 _selectedBackgroundPath = filePath;
26 updateController.setProofPath(filePath);
27
28 });
29 debugPrint('File path: $filePath');
30 }
31
32
33 @override
34 Widget build(BuildContext context) {
35 return ChangeNotifierProvider(
36 create: (context) => ProfileUserController(),
37 child: Consumer<ProfileUserController>(
38 builder: (context, profileController, child) {
39 if (profileController.isLoading) {
40 return const Center(child: CircularProgressIndicator());
41 }
42
43 if (profileController.userData == null) {
44 return ErrorScreen(onRetry: profileController.retryFetchUserData);
45 }
46
47
48 updateController.firstNameController.text =
49 profileController.userData!['biodate']['firstName'] ?? '';
50 updateController.lastNameController.text =
51 profileController.userData!['biodate']['lastName'] ?? '';
52 String birthDate =
53 profileController.userData!['biodate']['birthDate'] ?? '';
54 if (birthDate.isNotEmpty) {
55 updateController.birthDateController.text =
56 birthDate.substring(0, 10);
57 }
58
59 updateController.genderController.text =
60 profileController.userData!['biodate']['gender'] ?? '';
61 updateController.phoneController.text =
62 profileController.userData!['biodate']['phone'] ?? '';
63 updateController.addressController.text =
64 profileController.userData!['biodate']['address'] ?? '';
65 updateController.provinceController.text =
66 profileController.userData!['biodate']['province'] ?? '';
67 updateController.regenciesController.text =
68 profileController.userData!['biodate']['regencies'] ?? '';
69 updateController.institutionNameController.text =
70 profileController.userData!['biodate']['institutionName'] ?? '';
71 updateController.studyFieldController.text =
72 profileController.userData!['biodate']['field'] ?? '';
73 updateController.uniqueIdController.text =
74 profileController.userData!['biodate']['pupils'] ?? '';
75
76
77
78
79
80
81
82
83
84 return Scaffold(
85 appBar: AppBar(
86 backgroundColor: Colors.white,
87 elevation: 0,
88 leading: IconButton(
89 icon: const Icon(Icons.arrow_back, color: Colors.black),
90 onPressed: () {
91 Navigator.pushReplacement(
92 context,
93 MaterialPageRoute(
94 builder: (context) =>
95 const EduPassApp(initialPageIndex: 4),
96 ),
97 );
98 },
99 ),
100 title: Text(
101 'Profile',
102 style: GoogleFonts.poppins(
103 color: Colors.black,
104 fontSize: 20,
105 fontWeight: FontWeight.bold,
106 ),
107 ),
108 ),
109 body: Container(
110 color: Colors.grey.shade100,
111 child: SingleChildScrollView(
112 padding: const EdgeInsets.only(
113 left: 16, right: 16, top: 16, bottom: 30),
114 child: Column(
115 children: [
116 UserAvatarField(onFileSelected: _handleImageSelected),
117 const SizedBox(height: 20),
118 if (_selectedImagePath != null) ...[
119 Text('Selected file: $_selectedImagePath'),
120
121 Image.file(
122 File(_selectedImagePath!),
123 height: 200,
124 ),
125 ],
126 const SizedBox(height: 16),
127 ProfileTextField(
128 label: 'Nama Depan',
129 placeholder: 'John',
130 controller: updateController.firstNameController,
131 ),
132 const SizedBox(height: 16),
133 ProfileTextField(
134 label: 'Nama Belakang',
135 placeholder: 'Doe',
136 controller: updateController.lastNameController,
137 ),
138 const SizedBox(height: 16),
139 ProfileTextField(
140 label: 'Email',
141 placeholder: 'stevejobs@gmail.com',
142 enabled: false,
143 controller: TextEditingController(
144 text: profileController.userData!['email'] ?? '',
145 ),
146 ),
147 const SizedBox(height: 16),
148 ProfileDateField(
149 label: 'Select Date',
150 placeholder: 'YYYY-MM-DD',
151 controller: updateController.birthDateController,
152 ),
153 const SizedBox(height: 16),
154 ElevatedButton(
155 onPressed: () {
156 debugPrint(
157 'Selected Date: ${updateController.birthDateController.text}');
158 },
159 child: const Text('Print Selected Date'),
160 ),
161 const SizedBox(height: 16),
162 DropdownField(
163 label: 'Jenis Kelamin',
164 items: const ['Pria', 'Wanita', ''],
165 controller: updateController.genderController,
166 onChanged: (value) {
167 debugPrint(
168 'Selected Gender: ${updateController.genderController.text}');
169 },
170 ),
171 const SizedBox(height: 16),
172 UploadImageField(onFileSelected: _handleBackgroundSelected),
173 const SizedBox(height: 20),
174 if (_selectedBackgroundPath != null) ...[
175 Text('Selected file: $_selectedBackgroundPath'),
176
177 Image.file(
178 File(_selectedBackgroundPath!),
179 height: 200,
180 ),
181 ],
182 const SizedBox(height: 16),
183 ProfileTextField(
184 label: 'Provinsi',
185 placeholder: 'California',
186 controller: updateController.provinceController,
187 ),
188 const SizedBox(height: 16),
189 ProfileTextField(
190 label: 'Regencies',
191 placeholder: 'San Francisco',
192 controller: updateController.regenciesController,
193 ),
194 const SizedBox(height: 16),
195 ProfileTextField(
196 label: 'Nomor HP',
197 placeholder: '08123456789',
198 controller: updateController.phoneController,
199 ),
200 const SizedBox(height: 16),
201 ProfileTextField(
202 label: 'Alamat',
203 placeholder: 'Jalan Jendral Sudirman No 1',
204 controller: updateController.addressController,
205 ),
206 const SizedBox(height: 16),
207 ProfileTextField(
208 label: 'Nama Instansi',
209 placeholder: 'Harvard University',
210 controller: updateController.institutionNameController,
211 ),
212 const SizedBox(height: 16),
213 ProfileTextField(
214 label: 'Bidang Studi/Jurusan',
215 placeholder: 'Teknik Informatika',
216 controller: updateController.studyFieldController,
217 ),
218 const SizedBox(height: 16),
219 ProfileTextField(
220 label: 'NIM/NISN',
221 placeholder: '123123123',
222 controller: updateController.uniqueIdController,
223 ),
224 const SizedBox(height: 32),
225 ElevatedButton(
226 onPressed: () {
227 updateController.updateBio(context);
228 updateController.reset();
229
230
231
232
233 },
234 style: ElevatedButton.styleFrom(
235 backgroundColor: Colors.indigo,
236 padding: const EdgeInsets.symmetric(
237 horizontal: 40, vertical: 16),
238 shape: RoundedRectangleBorder(
239 borderRadius: BorderRadius.circular(8),
240 ),
241 ),
242 child: Text(
243 'Edit',
244 style: GoogleFonts.poppins(
245 fontSize: 16,
246 fontWeight: FontWeight.bold,
247 color: Colors.white,
248 ),
249 ),
250 ),
251 ],
252 ),
253 ),
254 ),
255 );
256 },
257 ),
258 );
259 }
260 }
What I have tried:
I have tried to delete the MIME image process but it's still not working