Flutter技术实现:如何将反馈表单的用户反馈提交至Firestore数据库并在独立页面展示反馈报表
Hey there! Let's walk through connecting your feedback form to Firestore and building that report page—super straightforward once you break it down.
Before jumping into code, double-check these basics:
- You've added Firebase to your Flutter project (follow the Firebase console steps for Android/iOS if you haven't)
- Add the
cloud_firestoredependency to yourpubspec.yaml:dependencies: flutter: sdk: flutter cloud_firestore: ^4.15.0 # Use the latest version available - Initialize Firebase in your
main.dart:void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); runApp(MyApp()); }
We'll modify your handleSubmitFeedback method to send form data to Firestore instead of just printing it. Since Firestore operations are async, we'll make this method async and add error handling to keep things user-friendly.
Replace your existing handleSubmitFeedback with this:
Future<void> handleSubmitFeedback() async { bool complete = true; for (int i = 0; i < _feedbackValue.length; ++i) { if (_feedbackValue[i] == -1) { complete = false; _isFormFieldComplete[i] = true; } else { _isFormFieldComplete[i] = false; } } setState(() {}); if (!complete) return; try { // Send feedback to a Firestore collection named "user_feedback" await FirebaseFirestore.instance.collection('user_feedback').add({ 'feedback_values': _feedbackValue, // Scores for each question 'additional_comments': additionalComments ?? '', // Handle empty comments 'submitted_at': Timestamp.now(), // Add timestamp for sorting in reports 'questions': _questions, // Optional: Saves questions to avoid hardcoding later }); // Show success message and navigate ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Feedback submitted successfully!')), ); Navigator.pushReplacementNamed(context, SessionTwoScreen.routeName); } catch (e) { // Handle errors like no internet or permission issues ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Failed to submit: ${e.toString()}')), ); } }
A quick breakdown:
add()creates a new document in theuser_feedbackcollection (Firestore auto-generates a unique ID for each entry)submitted_athelps sort feedback by date in the report page- The
try/catchblock ensures we gracefully handle issues and let the user know what went wrong
Now let's create a new screen that reads all feedback from Firestore and displays it in real time (using StreamBuilder so new feedback shows up automatically without refreshing).
Create this new widget:
class FeedbackReportScreen extends StatelessWidget { static const String routeName = 'feedback_report_screen'; @override Widget build(BuildContext context) { return Scaffold( appBar: const AppBar(title: Text('Feedback Report')), body: StreamBuilder<QuerySnapshot>( // Listen to the user_feedback collection, sorted by most recent first stream: FirebaseFirestore.instance .collection('user_feedback') .orderBy('submitted_at', descending: true) .snapshots(), builder: (context, snapshot) { // Handle loading state if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } // Handle errors if (snapshot.hasError) { return Center(child: Text('Error loading feedback: ${snapshot.error}')); } // Handle empty state if (!snapshot.hasData || snapshot.data!.docs.isEmpty) { return const Center(child: Text('No feedback submitted yet.')); } // Display feedback list return ListView.builder( itemCount: snapshot.data!.docs.length, itemBuilder: (context, index) { final feedbackDoc = snapshot.data!.docs[index]; final feedbackData = feedbackDoc.data() as Map<String, dynamic>; // Parse data from Firestore final List<int> scores = List<int>.from(feedbackData['feedback_values']); final List<String> questions = List<String>.from(feedbackData['questions']); final String comments = feedbackData['additional_comments'] ?? 'No additional comments'; final DateTime submitDate = (feedbackData['submitted_at'] as Timestamp).toDate(); return Card( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Show submission date Text( 'Submitted on: ${submitDate.toString().substring(0, 19)}', style: TextStyle(color: Colors.grey[600], fontSize: 12), ), const SizedBox(height: 12), // Show each question and its corresponding score (as emoji) ...List.generate(questions.length, (qIndex) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(questions[qIndex]), Text(_getEmojiForScore(scores[qIndex])), ], ), ); }), const SizedBox(height: 12), // Show additional comments const Text('Additional Comments:'), const SizedBox(height: 4), Text(comments), ], ), ), ); }, ); }, ), ); } // Helper method to convert scores to emojis (match your form's labels) String _getEmojiForScore(int score) { switch (score) { case 0: return '😊'; // Tons emoji case 1: return '🙂'; // Alot emoji case 2: return '😐'; // So-so emoji case 3: return '😕'; // Not good emoji default: return '❓'; } } }
Don't forget to add this screen to your app's routes in MaterialApp:
routes: { // ... your existing routes FeedbackReportScreen.routeName: (context) => FeedbackReportScreen(), },
- Firestore Security Rules: For development, you can use temporary rules to allow read/write access, but lock them down for production:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /user_feedback/{document} { allow read, write: if request.auth != null; // Adjust based on your auth setup } } } - UI Tweaks: For the report page, you could use a
DataTableinstead of aListViewfor a more table-like layout, or add filters/sorting options - Validation: Add extra checks like limiting comment length if needed
That's it! Your form will now submit to Firestore, and the report page will display all feedback in real time.
内容的提问来源于stack exchange,提问作者peekaysjay

