Flutter Firebase数据更新失败问题求助(2021年)
Hey there! Let's break down why your Firebase update isn't working and fix it step by step—there are a couple of straightforward issues in your code that are blocking the update.
Key Problems in Your Current Code
1. You're passing TextEditingController objects instead of their text values
In your ElevatedButton's onPressed handler, you're trying to save controller instances (like TextEditingController(text: x['firstName'])) to Firebase instead of the actual text the user entered. Firebase expects string values, not controller objects, so this won't work at all.
2. Creating TextEditingControllers inside the StreamBuilder builder function
Every time the StreamBuilder rebuilds (which happens when Firebase data updates or the UI refreshes), you create brand new controllers. This means you lose any user input and can't reference the updated text when you try to save changes.
Fixed Code Implementation
First, convert your widget to a StatefulWidget so you can persist controllers across rebuilds. Here's the revised code with fixes:
class ProfileEditWidget extends StatefulWidget { const ProfileEditWidget({super.key}); @override State<ProfileEditWidget> createState() => _ProfileEditWidgetState(); } class _ProfileEditWidgetState extends State<ProfileEditWidget> { // Initialize controllers at the state level late TextEditingController _firstNameController; late TextEditingController _lastNameController; late TextEditingController _middleNameController; late TextEditingController _phoneController; String? _profileDocId; // Store document ID dynamically (no hardcoding!) @override void initState() { super.initState(); // Initialize empty controllers _firstNameController = TextEditingController(); _lastNameController = TextEditingController(); _middleNameController = TextEditingController(); _phoneController = TextEditingController(); } @override void dispose() { // Clean up controllers to avoid memory leaks _firstNameController.dispose(); _lastNameController.dispose(); _middleNameController.dispose(); _phoneController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return StreamBuilder<QuerySnapshot>( stream: FirebaseFirestore.instance.collection("personalDetail").snapshots(), builder: (context, snapshot) { if (snapshot.hasData) { // Get the first profile document final profileDoc = snapshot.data!.docs.first; _profileDocId = profileDoc.id; // Capture document ID dynamically // Update controller text only if it doesn't match the latest Firebase data if (_firstNameController.text != profileDoc['firstName']) { _firstNameController.text = profileDoc['firstName']; } if (_lastNameController.text != profileDoc['lastName']) { _lastNameController.text = profileDoc['lastName']; } if (_middleNameController.text != profileDoc['middleName']) { _middleNameController.text = profileDoc['middleName']; } if (_phoneController.text != profileDoc['phone']) { _phoneController.text = profileDoc['phone']; } return ListView.builder( itemCount: 1, itemBuilder: (context, index) { return Container( padding: EdgeInsets.all(16), child: Column( children: [ // Your avatar stack code remains the same Center( child: Stack( children: [ Container( width: 130, height: 130, decoration: BoxDecoration( border: Border.all(width: 4, color: Colors.white), boxShadow: [ BoxShadow( spreadRadius: 2, blurRadius: 10, color: Colors.black.withOpacity(0.1), ) ], shape: BoxShape.circle, image: DecorationImage( fit: BoxFit.cover, image: AssetImage('assets/images/Profile.jpg'), ), ), ), Positioned( bottom: 0, right: 0, child: Container( height: 40, width: 40, decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all(width: 4, color: Colors.white), color: Colors.green.shade400 ), child: Icon(Icons.camera_alt, color: Colors.white), ), ) ], ), ), SizedBox(height: 30), // Updated TextFormFields using persistent controllers TextFormField( maxLength: 20, style: TextStyle(color: Colors.black, fontSize: 15), controller: _firstNameController, decoration: InputDecoration( labelText: "First Name", labelStyle: TextStyle( fontWeight: FontWeight.w800, color: Colors.black, fontSize: 23), floatingLabelBehavior: FloatingLabelBehavior.always, ), ), TextFormField( maxLength: 20, style: TextStyle(color: Colors.black, fontSize: 15), controller: _lastNameController, decoration: InputDecoration( labelText: "Last Name", labelStyle: TextStyle( fontWeight: FontWeight.w800, color: Colors.black, fontSize: 23), floatingLabelBehavior: FloatingLabelBehavior.always, ), ), TextFormField( maxLength: 15, style: TextStyle(color: Colors.black, fontSize: 15), controller: _middleNameController, decoration: InputDecoration( labelText: "Middle Name", labelStyle: TextStyle( fontWeight: FontWeight.w800, color: Colors.black, fontSize: 23), floatingLabelBehavior: FloatingLabelBehavior.always, ), ), TextFormField( maxLength: 10, keyboardType: TextInputType.phone, inputFormatters: [FilteringTextInputFormatter.digitsOnly], style: TextStyle(color: Colors.black, fontSize: 15), controller: _phoneController, decoration: InputDecoration( labelText: "Phone Number", labelStyle: TextStyle( fontWeight: FontWeight.w800, color: Colors.black, fontSize: 23), floatingLabelBehavior: FloatingLabelBehavior.always, ), ), TextFormField( initialValue: profileDoc['email'], enabled: false, style: TextStyle(color: Colors.black, fontSize: 15), decoration: InputDecoration( labelText: "Email", labelStyle: TextStyle( fontWeight: FontWeight.w800, color: Colors.black, fontSize: 23), floatingLabelBehavior: FloatingLabelBehavior.always, ), ), SizedBox(height: 30), Align( child: ElevatedButton( onPressed: () async { if (_profileDocId == null) { Fluttertoast.showToast( msg: "No profile data found", toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, textColor: Colors.white, backgroundColor: Colors.red.shade400, ); return; } try { // Update Firebase with controller text values await FirebaseFirestore.instance .collection('personalDetail') .doc(_profileDocId) .update({ "firstName": _firstNameController.text, "lastName": _lastNameController.text, "middleName": _middleNameController.text, "phone": _phoneController.text, }); Fluttertoast.showToast( msg: "Profile updated Successfully", toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, textColor: Colors.black, backgroundColor: Colors.green.shade400, ); Navigator.pushNamed(context, "/profilePage"); } catch (e) { // Handle update errors (e.g., network issues) Fluttertoast.showToast( msg: "Update failed: ${e.toString()}", toastLength: Toast.LENGTH_LONG, gravity: ToastGravity.BOTTOM, textColor: Colors.white, backgroundColor: Colors.red.shade400, ); } }, child: Text( "Save Details", style: TextStyle( fontSize: 15, letterSpacing: 2, color: Colors.black), ), style: ElevatedButton.styleFrom( fixedSize: Size(250, 40), backgroundColor: Colors.green.shade500, padding: EdgeInsets.symmetric(horizontal: 50), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10))), ), ) ], ), ); }, ); } else if (snapshot.hasError) { return Center(child: Text("Error loading profile: ${snapshot.error}")); } else { return Center(child: CircularProgressIndicator()); } }, ); } }
Additional Improvements
- No hardcoded document ID: We now capture the document ID directly from the snapshot, so you don't have to update code if your document ID changes.
- Error handling: Added
try/catchto handle network errors or Firebase permissions issues, so users get clear feedback if something goes wrong. - Memory management: Controllers are properly disposed of in the
disposemethod to avoid memory leaks.
Give these changes a try—this should resolve your update issue completely. Let me know if you run into any other roadblocks!
内容的提问来源于stack exchange,提问作者user17112368

