Creating an Interactive Rating and Review Widget in Flutter
User feedback is a cornerstone of modern application development, driving improvements, building trust, and fostering community engagement. A ubiquitous feature for gathering this feedback is the rating and review widget, commonly found in e-commerce, service, and content-based applications. This article will guide you through the process of building a highly customizable and interactive rating and review widget in Flutter, enabling users to easily provide their opinions and ratings.
Prerequisites
To follow along with this tutorial, you should have a basic understanding of Flutter development, including widgets, state management (StatefulWidget), and fundamental UI components.
Core Components of a Rating and Review Widget
A typical rating and review widget consists of several key elements:
- Star Rating System: An interactive component allowing users to select a rating, typically represented by stars.
- Review Text Field: An input area where users can write their textual feedback.
- Submission Button: A button to finalize and submit the rating and review.
- State Management: To hold the user's selected rating and entered review text.
Step-by-Step Implementation
1. Project Setup
Assuming you have a Flutter project set up, we will create a new Dart file for our widget, for instance, rating_review_widget.dart. You can then import and use this widget in your main.dart or any other screen.
2. Creating the RatingReviewWidget
Our widget will be a StatefulWidget because it needs to manage internal state—the currently selected rating and the text entered into the review field.
Create a new file named rating_review_widget.dart.
import 'package:flutter/material.dart';
class RatingReviewWidget extends StatefulWidget {
final Function(int rating, String review) onReviewSubmitted;
const RatingReviewWidget({Key? key, required this.onReviewSubmitted}) : super(key: key);
@override
_RatingReviewWidgetState createState() => _RatingReviewWidgetState();
}
class _RatingReviewWidgetState extends State {
int _currentRating = 0; // Stores the selected star rating
final TextEditingController _reviewController = TextEditingController(); // Controls the review text field
@override
void dispose() {
_reviewController.dispose(); // Dispose controller to prevent memory leaks
super.dispose();
}
// Method to build individual star icons
Widget _buildStar(int starIndex) {
return GestureDetector(
onTap: () {
setState(() {
_currentRating = starIndex + 1; // Update rating when star is tapped
});
},
child: Icon(
starIndex < _currentRating ? Icons.star : Icons.star_border,
color: Colors.amber, // Color for active stars
size: 36.0,
),
);
}
// Method to handle review submission
void _submitReview() {
if (_currentRating > 0) {
widget.onReviewSubmitted(_currentRating, _reviewController.text);
// Optionally, clear the form after submission
setState(() {
_currentRating = 0;
_reviewController.clear();
});
// Show a confirmation message (e.g., SnackBar)
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Thank you for your review!')),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Please select a star rating.')),
);
}
}
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.all(16.0),
elevation: 4.0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text(
'Rate this product:',
style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
const SizedBox(height: 10.0),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(5, (index) => _buildStar(index)), // Generate 5 stars
),
const SizedBox(height: 20.0),
const Text(
'Write a review:',
style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
const SizedBox(height: 10.0),
TextField(
controller: _reviewController,
maxLines: 4,
decoration: InputDecoration(
hintText: 'Share your thoughts...',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
),
contentPadding: const EdgeInsets.all(12.0),
),
keyboardType: TextInputType.multiline,
),
const SizedBox(height: 20.0),
ElevatedButton(
onPressed: _submitReview,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 12.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
),
child: const Text(
'Submit Review',
style: TextStyle(fontSize: 16.0),
),
),
],
),
),
);
}
}
Explanation of the Code:
RatingReviewWidgetis aStatefulWidgetthat takes anonReviewSubmittedcallback function. This function will be called when the user submits their review, passing the rating and review text to the parent widget._RatingReviewWidgetStateholds the mutable state:_currentRating: An integer from 0 to 5 representing the selected star rating._reviewController: ATextEditingControllerto manage the input of the review text field. It's crucial to dispose of this controller in thedisposemethod to prevent memory leaks.
_buildStar(int starIndex): This helper method creates an individual star icon. It uses aGestureDetectorto make each star interactive. Tapping a star updates_currentRating, andsetStateredraws the stars to reflect the new rating._submitReview(): This method is called when the "Submit Review" button is pressed. It checks if a rating has been selected, then invokes theonReviewSubmittedcallback with the collected data. It also includes basic feedback for the user via aSnackBar.build(BuildContext context): This method lays out the UI using aCardfor visual separation. Inside the card, aColumnarranges the elements vertically:- A title text.
- A
Rowof 5 stars, generated dynamically usingList.generateand the_buildStarmethod. - Another title for the review section.
- A
TextFieldfor the review text, configured withmaxLinesand a descriptive hint. - An
ElevatedButtonfor submitting the review, hooked up to the_submitReviewmethod.
3. Integrating the Widget into Your Application
Now, let's use the RatingReviewWidget in a sample page. For demonstration purposes, we'll integrate it into main.dart.
import 'package:flutter/material.dart';
import 'package:your_app_name/rating_review_widget.dart'; // Make sure this path is correct
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Rating Review Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
void _handleReviewSubmission(int rating, String review) {
// This is where you would typically send the data to a backend server,
// save it locally, or update your application's state.
print('Rating submitted: $rating stars');
print('Review text: "$review"');
// For a real app, you might show a loading indicator, then a success/error message.
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Product Rating & Review'),
),
body: Center(
child: RatingReviewWidget(
onReviewSubmitted: _handleReviewSubmission,
),
),
);
}
}
In the HomePage, we pass the _handleReviewSubmission method to the onReviewSubmitted callback of our RatingReviewWidget. This method will receive the rating and review text, which you can then process further (e.g., send to an API, store in a database, or display elsewhere).
Customization and Enhancements
This basic widget provides a solid foundation. Here are some ideas for further customization and enhancement:
- Styling: Easily change star colors, sizes, fonts, text field decorations, and button styles to match your application's theme.
- Validation: Add validation for the review text (e.g., minimum length, profanity filter).
- Loading State: Display a loading indicator on the submit button while the review is being processed (e.g., sent to a backend).
- Error Handling: Implement proper error handling and feedback for network requests or other submission failures.
- Existing Reviews Display: Extend the widget or create a separate component to display an average rating and a list of previously submitted reviews.
- Animation: Add subtle animations when stars are selected for a more engaging user experience.
- Accessibility: Ensure the widget is accessible, providing appropriate semantics for screen readers.
Conclusion
Building an interactive rating and review widget in Flutter is straightforward thanks to its rich set of UI components and flexible state management capabilities. By following the steps outlined in this article, you can create a functional and aesthetically pleasing widget that enhances user engagement and provides valuable feedback opportunities for your application. Remember to customize it to fit your specific design and functional requirements.