image

15 Dec 2025

9K

35K

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:

  • RatingReviewWidget is a StatefulWidget that takes an onReviewSubmitted callback function. This function will be called when the user submits their review, passing the rating and review text to the parent widget.
  • _RatingReviewWidgetState holds the mutable state:
    • _currentRating: An integer from 0 to 5 representing the selected star rating.
    • _reviewController: A TextEditingController to manage the input of the review text field. It's crucial to dispose of this controller in the dispose method to prevent memory leaks.
  • _buildStar(int starIndex): This helper method creates an individual star icon. It uses a GestureDetector to make each star interactive. Tapping a star updates _currentRating, and setState redraws 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 the onReviewSubmitted callback with the collected data. It also includes basic feedback for the user via a SnackBar.
  • build(BuildContext context): This method lays out the UI using a Card for visual separation. Inside the card, a Column arranges the elements vertically:
    • A title text.
    • A Row of 5 stars, generated dynamically using List.generate and the _buildStar method.
    • Another title for the review section.
    • A TextField for the review text, configured with maxLines and a descriptive hint.
    • An ElevatedButton for submitting the review, hooked up to the _submitReview method.

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.

Related Articles

May 14, 2026

Building a Multi-Event Countdown Timer Widget with Reminders, Notifications, Repeat, and Custom Labels in Flutter

Building a Multi-Event Countdown Timer Widget with Reminders, Notifications, Repeat, and Custom Labels in Flutter Countdown timers are essential in many applic

May 11, 2026

Unleashing Dynamic UIs: Flutter's Animation Prowess

Unleashing Dynamic UIs: Flutter's Animation Prowess for Slide & Scale Effects Flutter's declarative UI framework, combined with its powerful animation capabilit

May 11, 2026

Building a Product Detail Page Widget in Flutter with Related Items, Review Carousel, Promo Badges, and Quick Buy

Building a Product Detail Page Widget in Flutter with Related Items, Review Carousel, Promo Badges, and Quick Buy A well-designed Product Detail Page (PDP) is