无法在 Flutter 中更新用户个人资料

问题描述 投票:0回答:1

我是flutter开发的初学者,我想调用API来更新用户配置文件,在API中其中一个由'image'和'proof'字段组成,该字段可以留空,但问题是每当我将这些字段留空并单击“编辑”按钮,我总是收到来自服务器的此响应:


{
  "status": 500,
  "message": "The \"path\" argument must be of type string. Received an instance of Array"
}

我的目的是,如果我不选择任何图像和背景图像,我想清空“图像”和“证明”字段,但如果我选择它,我希望这些字段充满图像路径

我希望有人可以帮助我找到这个问题的解决方案,

这是我的代码:

// Auth Service
class AuthService {
  static String? token;
  static String? bioDateId;

  final TokenManager tokenManager = TokenManager();
  final BiodateIdManager biodateIdManager = BiodateIdManager();
// Update Bio
  Future<bool> updateBio({
    required String firstName,
    required String lastName,
    required String birthDate,
    required String gender,
    required String phone,
    required String address,
    required String province,
    required String regencies,
    required String institutionName,
    required String pupils,
    required String field,
    String? image,
    String? proof,
  }) async {
    String? token = await tokenManager.getToken();
    String? bioDateId = await biodateIdManager.getBiodateId();
    try {
      // Create a Map for form data
      Map<String, dynamic> formDataMap = {
        "firstName": firstName,
        "lastName": lastName,
        "birthDate": birthDate,
        "gender": gender,
        "phone": phone,
        "address": address,
        "province": province,
        "regencies": regencies,
        "institutionName": institutionName,
        "field": field,
        "pupils": pupils,
      };

      // Get MIME type for the files and add to formDataMap if not null
      if (proof != null && proof.isNotEmpty) {
        String? proofMimeType = lookupMimeType(proof);
        if (proofMimeType != null) {
          MultipartFile proofMultipartFile = await MultipartFile.fromFile(
            proof,
            filename: 'proof.png',
            contentType: MediaType.parse(proofMimeType),
          );
          formDataMap["proof"] = proofMultipartFile;
        } else {
          debugPrint('Invalid proof MIME type');
        }
      }

      if (image != null && image.isNotEmpty) {
        String? imageMimeType = lookupMimeType(image);
        if (imageMimeType != null) {
          MultipartFile imageMultipartFile = await MultipartFile.fromFile(
            image,
            filename: 'image.png',
            contentType: MediaType.parse(imageMimeType),
          );
          formDataMap["image"] = imageMultipartFile;
        } else {
          debugPrint('Invalid image MIME type');
        }
      }

      var response = await Dio().put(
        "http://192.168.1.5:3000/user/update-biodate?id=$bioDateId",
        data: FormData.fromMap(formDataMap),
        options: Options(
          headers: {
            "Content-Type": "multipart/form-data",
            "Authorization": "Bearer $token"
          },
        ),
      );

      if (response.statusCode == 200) {
        // Update successful
        return true;
      } else {
        throw Exception('Failed to Update Bio : ${response.data['message']}');
      }
    } on DioException catch (e) {
      if (e.response?.statusCode == 400) {
        debugPrint('Error 400: ${e.response?.data}');
        throw Exception('Update Bio failed: ${e.response?.data['message']}');
      } else {
        debugPrint('Error: ${e.toString()}');
        throw Exception('Failed to Update Bio');
      }
    }
  }
}



// UpdateUserController
class UpdateUserController {
  final AuthService _authService = AuthService();
  UpdateUserModel updateUserModel = UpdateUserModel();

  TextEditingController firstNameController = TextEditingController();
  TextEditingController lastNameController = TextEditingController();
  TextEditingController birthDateController = TextEditingController();
  TextEditingController genderController = TextEditingController();
  TextEditingController phoneController = TextEditingController();
  TextEditingController addressController = TextEditingController();
  TextEditingController provinceController = TextEditingController();
  TextEditingController regenciesController = TextEditingController();
  TextEditingController institutionNameController = TextEditingController();
  TextEditingController studyFieldController = TextEditingController();
  TextEditingController uniqueIdController = TextEditingController();
  TextEditingController imageController = TextEditingController();
  TextEditingController proofController = TextEditingController();

  String? _selectedImagePath;
  String? _selectedProofPath;

  void setImagePath(String filePath) {
    _selectedImagePath = filePath;
    debugPrint('Selected image path set: $_selectedImagePath');
  }

  void setProofPath(String filePath) {
    _selectedProofPath = filePath;
    debugPrint('Selected proof path set: $_selectedProofPath');
  }

  void reset() {
    _selectedImagePath = null;
    _selectedProofPath = null;
    firstNameController.clear();
    lastNameController.clear();
    birthDateController.clear();
    genderController.clear();
    phoneController.clear();
    addressController.clear();
    provinceController.clear();
    regenciesController.clear();
    institutionNameController.clear();
    studyFieldController.clear();
    uniqueIdController.clear();
  }

  void dispose() {
    firstNameController.dispose();
    lastNameController.dispose();
    birthDateController.dispose();
    genderController.dispose();
    phoneController.dispose();
    addressController.dispose();
    provinceController.dispose();
    regenciesController.dispose();
    institutionNameController.dispose();
    studyFieldController.dispose();
    uniqueIdController.dispose();
  }

  bool fileExists(String path) {
    return File(path).existsSync();
  }

  Future<void> updateBio(BuildContext context) async {
    DialogHelper.showLoading(context);

    String firstName = firstNameController.text.trim();
    String lastName = lastNameController.text.trim();
    String birthDate = birthDateController.text.trim();
    String gender = genderController.text.trim();
    String phone = phoneController.text.trim();
    String address = addressController.text.trim();
    String province = provinceController.text.trim();
    String regencies = regenciesController.text.trim();
    String institutionName = institutionNameController.text.trim();
    String studyField = studyFieldController.text.trim();
    String uniqueId = uniqueIdController.text.trim();
    // String? image = imageController.text.trim();
    // String? proof = proofController.text.trim();
    String? image = _selectedImagePath;
    String? proof = _selectedProofPath;

    try {
      bool bioUpdated = await _authService.updateBio(
        firstName: firstName,
        lastName: lastName,
        birthDate: birthDate,
        gender: gender,
        phone: phone,
        address: address,
        province: province,
        regencies: regencies,
        institutionName: institutionName,
        field: studyField,
        pupils: uniqueId,
        image: image,
        proof: proof,
      );
      DialogHelper.hideLoading(context);

      if (bioUpdated) {
        reset();
        Navigator.push(
            context,
            MaterialPageRoute(
                builder: (context) => const EduPassApp(
                      initialPageIndex: 4,
                    )));
        ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
          content: Text('UpdateBio berhasil'),
          duration: Duration(seconds: 1),
        ));
        debugPrint('UpdateBio berhasil');
      } else {
        ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
          content: Text('UpdateBio gagal'),
          duration: Duration(seconds: 2),
        ));
        debugPrint('UpdateBio gagal');
      }
    } catch (e) {
      DialogHelper.hideLoading(context);
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
        content: Text('Error: $e'),
        duration: const Duration(seconds: 2),
      ));
      debugPrint('Error saat UpdateBio: $e');
    }
  }
}

// ProfileUserDetail
class ProfileDetailUser extends StatefulWidget {
  const ProfileDetailUser({super.key});

  @override
  State<ProfileDetailUser> createState() => _ProfileDetailUserState();
}

class _ProfileDetailUserState extends State<ProfileDetailUser> {
  String? _selectedImagePath;
  String? _selectedBackgroundPath;
  final UpdateUserController updateController = UpdateUserController();

  void _handleImageSelected(String filePath) {
    setState(() {
      _selectedImagePath = filePath;
      updateController.setImagePath(filePath);
      // updateController.imageController.text = filePath;
    });
    debugPrint('File path: $filePath');
  }

  void _handleBackgroundSelected(String filePath) {
    setState(() {
      _selectedBackgroundPath = filePath;
      updateController.setProofPath(filePath);
      // updateController.proofController.text = filePath;
    });
    debugPrint('File path: $filePath');
  }


  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => ProfileUserController(),
      child: Consumer<ProfileUserController>(
        builder: (context, profileController, child) {
          if (profileController.isLoading) {
            return const Center(child: CircularProgressIndicator());
          }

          if (profileController.userData == null) {
            return ErrorScreen(onRetry: profileController.retryFetchUserData);
          }

          // Initialize UpdateUserController's text controllers with data from ProfileUserController
          updateController.firstNameController.text =
              profileController.userData!['biodate']['firstName'] ?? '';
          updateController.lastNameController.text =
              profileController.userData!['biodate']['lastName'] ?? '';
          String birthDate =
              profileController.userData!['biodate']['birthDate'] ?? '';
          if (birthDate.isNotEmpty) {
            updateController.birthDateController.text =
                birthDate.substring(0, 10); // Format date
          }

          updateController.genderController.text =
              profileController.userData!['biodate']['gender'] ?? '';
          updateController.phoneController.text =
              profileController.userData!['biodate']['phone'] ?? '';
          updateController.addressController.text =
              profileController.userData!['biodate']['address'] ?? '';
          updateController.provinceController.text =
              profileController.userData!['biodate']['province'] ?? '';
          updateController.regenciesController.text =
              profileController.userData!['biodate']['regencies'] ?? '';
          updateController.institutionNameController.text =
              profileController.userData!['biodate']['institutionName'] ?? '';
          updateController.studyFieldController.text =
              profileController.userData!['biodate']['field'] ?? '';
          updateController.uniqueIdController.text =
              profileController.userData!['biodate']['pupils'] ?? '';
          // updateController.imageController.text =
          //     profileController.userData!['biodate']['image'];
          // updateController.proofController.text =
          //     profileController.userData!['biodate']['proof'];
          // updateController.setImagePath(
          //     profileController.userData!['biodate']['image'] ?? '');
          // updateController.setProofPath(
          //     profileController.userData!['biodate']['proof'] ?? '');

          return Scaffold(
            appBar: AppBar(
              backgroundColor: Colors.white,
              elevation: 0,
              leading: IconButton(
                icon: const Icon(Icons.arrow_back, color: Colors.black),
                onPressed: () {
                  Navigator.pushReplacement(
                    context,
                    MaterialPageRoute(
                      builder: (context) =>
                          const EduPassApp(initialPageIndex: 4),
                    ),
                  );
                },
              ),
              title: Text(
                'Profile',
                style: GoogleFonts.poppins(
                  color: Colors.black,
                  fontSize: 20,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
            body: Container(
              color: Colors.grey.shade100,
              child: SingleChildScrollView(
                padding: const EdgeInsets.only(
                    left: 16, right: 16, top: 16, bottom: 30),
                child: Column(
                  children: [
                    UserAvatarField(onFileSelected: _handleImageSelected),
                    const SizedBox(height: 20),
                    if (_selectedImagePath != null) ...[
                      Text('Selected file: $_selectedImagePath'),
                      // Jika Anda ingin menampilkan gambar yang diunggah
                      Image.file(
                        File(_selectedImagePath!),
                        height: 200,
                      ),
                    ],
                    const SizedBox(height: 16),
                    ProfileTextField(
                      label: 'Nama Depan',
                      placeholder: 'John',
                      controller: updateController.firstNameController,
                    ),
                    const SizedBox(height: 16),
                    ProfileTextField(
                      label: 'Nama Belakang',
                      placeholder: 'Doe',
                      controller: updateController.lastNameController,
                    ),
                    const SizedBox(height: 16),
                    ProfileTextField(
                      label: 'Email',
                      placeholder: '[email protected]',
                      enabled: false,
                      controller: TextEditingController(
                        text: profileController.userData!['email'] ?? '',
                      ),
                    ),
                    const SizedBox(height: 16),
                    ProfileDateField(
                      label: 'Select Date',
                      placeholder: 'YYYY-MM-DD',
                      controller: updateController.birthDateController,
                    ),
                    const SizedBox(height: 16),
                    ElevatedButton(
                      onPressed: () {
                        debugPrint(
                            'Selected Date: ${updateController.birthDateController.text}');
                      },
                      child: const Text('Print Selected Date'),
                    ),
                    const SizedBox(height: 16),
                    DropdownField(
                      label: 'Jenis Kelamin',
                      items: const ['Pria', 'Wanita', ''],
                      controller: updateController.genderController,
                      onChanged: (value) {
                        debugPrint(
                            'Selected Gender: ${updateController.genderController.text}');
                      },
                    ),
                    const SizedBox(height: 16),
                    UploadImageField(onFileSelected: _handleBackgroundSelected),
                    const SizedBox(height: 20),
                    if (_selectedBackgroundPath != null) ...[
                      Text('Selected file: $_selectedBackgroundPath'),
                      // Jika Anda ingin menampilkan gambar yang diunggah
                      Image.file(
                        File(_selectedBackgroundPath!),
                        height: 200,
                      ),
                    ],
                    const SizedBox(height: 16),
                    ProfileTextField(
                      label: 'Provinsi',
                      placeholder: 'California',
                      controller: updateController.provinceController,
                    ),
                    const SizedBox(height: 16),
                    ProfileTextField(
                      label: 'Regencies',
                      placeholder: 'San Francisco',
                      controller: updateController.regenciesController,
                    ),
                    const SizedBox(height: 16),
                    ProfileTextField(
                      label: 'Nomor HP',
                      placeholder: '08123456789',
                      controller: updateController.phoneController,
                    ),
                    const SizedBox(height: 16),
                    ProfileTextField(
                      label: 'Alamat',
                      placeholder: 'Jalan Jendral Sudirman No 1',
                      controller: updateController.addressController,
                    ),
                    const SizedBox(height: 16),
                    ProfileTextField(
                      label: 'Nama Instansi',
                      placeholder: 'Harvard University',
                      controller: updateController.institutionNameController,
                    ),
                    const SizedBox(height: 16),
                    ProfileTextField(
                      label: 'Bidang Studi/Jurusan',
                      placeholder: 'Teknik Informatika',
                      controller: updateController.studyFieldController,
                    ),
                    const SizedBox(height: 16),
                    ProfileTextField(
                      label: 'NIM/NISN',
                      placeholder: '123123123',
                      controller: updateController.uniqueIdController,
                    ),
                    const SizedBox(height: 32),
                    ElevatedButton(
                      onPressed: () {
                        updateController.updateBio(context);
                        updateController.reset();

                        // String? image = _selectedImagePath;
                        // String? proof = _selectedBackgroundPath;
                        // updateController.updateBio(context, image, proof);
                      },
                      style: ElevatedButton.styleFrom(
                        backgroundColor: Colors.indigo,
                        padding: const EdgeInsets.symmetric(
                            horizontal: 40, vertical: 16),
                        shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(8),
                        ),
                      ),
                      child: Text(
                        'Edit',
                        style: GoogleFonts.poppins(
                          fontSize: 16,
                          fontWeight: FontWeight.bold,
                          color: Colors.white,
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),
          );
        },
      ),
    );
  }
}


flutter image file debugging
1个回答
0
投票

您的代码将应用程序状态管理与

ProfileDetailUser
小部件中的演示相结合。这不是构建可维护应用程序的好策略。

我的建议是查看 Flutter 的状态管理方法列表并选择适合您的方法。我已经成功地使用了 BloC 模式(与用于依赖注入的 GetIt 一起)。

如果做得正确,分离应用程序状态和演示将可以防止出现像您这样的问题。

© www.soinside.com 2019 - 2024. All rights reserved.