Search code examples
flutterflutter-layoutflutter-dependencies

Flutter rating bar package selected and unselected containers


What I have:

have

What I want:

want

I am using flutter_rating_bar package and I want it to show the unselected containers as is instead of the default grey color and only want to change the color for the selected container.

As shown in the above pictures, instead of the grey boxes I want to display the remaining unselected containers preceding the selected ones as shown in the second picture.

Code:

RatingBar.builder(
  initialRating: initialRatings,
  itemCount: 5,
  itemSize: 50,
  itemPadding: EdgeInsets.symmetric(horizontal: containerWidth * 0.0077),
  itemBuilder: (context, index) {
    switch (index) {
      case 0:
        return Container(
          width: ratingContainerWidth,
          height: ratingContainerHeight,
          decoration: BoxDecoration(
            borderRadius: const BorderRadius.only(
              topLeft: Radius.circular(8),
              topRight: Radius.circular(8),
              bottomLeft: Radius.circular(8),
              bottomRight: Radius.circular(8),
            ),
            color: knowledgeRating == 1
                ? const Color.fromRGBO(109, 44, 237, 1)
                : const Color.fromRGBO(226, 243, 255, 1),
          ),
          padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
          child: Text(
            '1',
            textAlign: TextAlign.left,
            style: TextStyle(
              color: knowledgeRating == 1
                  ? const Color.fromRGBO(255, 255, 255, 1)
                  : const Color.fromRGBO(0, 0, 0, 1),
              fontFamily: 'Inter',
              fontSize: 16,
              letterSpacing: 0.20000000298023224,
              fontWeight: FontWeight.bold,
            ),
          ),
        );
      case 1:
        return Container(
          width: ratingContainerWidth,
          height: ratingContainerHeight,
          decoration: BoxDecoration(
            borderRadius: const BorderRadius.only(
              topLeft: Radius.circular(8),
              topRight: Radius.circular(8),
              bottomLeft: Radius.circular(8),
              bottomRight: Radius.circular(8),
            ),
            color: knowledgeRating == 2
                ? const Color.fromRGBO(109, 44, 237, 1)
                : const Color.fromRGBO(226, 243, 255, 1),
          ),
          padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
          child: Text(
            '2',
            textAlign: TextAlign.left,
            style: TextStyle(
              color: knowledgeRating == 2
                  ? const Color.fromRGBO(255, 255, 255, 1)
                  : const Color.fromRGBO(0, 0, 0, 1),
              fontFamily: 'Inter',
              fontSize: 16,
              letterSpacing: 0.20000000298023224,
              fontWeight: FontWeight.bold,
            ),
          ),
        );
      case 2:
        return Container(
          width: ratingContainerWidth,
          height: ratingContainerHeight,
          decoration: BoxDecoration(
            borderRadius: const BorderRadius.only(
              topLeft: Radius.circular(8),
              topRight: Radius.circular(8),
              bottomLeft: Radius.circular(8),
              bottomRight: Radius.circular(8),
            ),
            color: knowledgeRating == 3
                ? const Color.fromRGBO(109, 44, 237, 1)
                : const Color.fromRGBO(226, 243, 255, 1),
          ),
          padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
          child: Text(
            '3',
            textAlign: TextAlign.left,
            style: TextStyle(
              color: knowledgeRating == 3
                  ? const Color.fromRGBO(255, 255, 255, 1)
                  : const Color.fromRGBO(0, 0, 0, 1),
              fontFamily: 'Inter',
              fontSize: 16,
              letterSpacing: 0.20000000298023224,
              fontWeight: FontWeight.bold,
            ),
          ),
        );
      case 3:
        return Container(
          width: ratingContainerWidth,
          height: ratingContainerHeight,
          decoration: BoxDecoration(
            borderRadius: const BorderRadius.only(
              topLeft: Radius.circular(8),
              topRight: Radius.circular(8),
              bottomLeft: Radius.circular(8),
              bottomRight: Radius.circular(8),
            ),
            color: knowledgeRating == 4
                ? const Color.fromRGBO(109, 44, 237, 1)
                : const Color.fromRGBO(226, 243, 255, 1),
          ),
          padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
          child: Text(
            '4',
            textAlign: TextAlign.left,
            style: TextStyle(
              color: knowledgeRating == 4
                  ? const Color.fromRGBO(255, 255, 255, 1)
                  : const Color.fromRGBO(0, 0, 0, 1),
              fontFamily: 'Inter',
              fontSize: 16,
              letterSpacing: 0.20000000298023224,
              fontWeight: FontWeight.bold,
            ),
          ),
        );
      case 4:
        return Container(
          width: ratingContainerWidth,
          height: ratingContainerHeight,
          decoration: BoxDecoration(
            borderRadius: const BorderRadius.only(
              topLeft: Radius.circular(8),
              topRight: Radius.circular(8),
              bottomLeft: Radius.circular(8),
              bottomRight: Radius.circular(8),
            ),
            color: knowledgeRating == 5
                ? const Color.fromRGBO(109, 44, 237, 1)
                : const Color.fromRGBO(226, 243, 255, 1),
          ),
          padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
          child: Text(
            '5',
            textAlign: TextAlign.left,
            style: TextStyle(
              color: knowledgeRating == 5
                  ? const Color.fromRGBO(255, 255, 255, 1)
                  : const Color.fromRGBO(0, 0, 0, 1),
              fontFamily: 'Inter',
              fontSize: 16,
              letterSpacing: 0.20000000298023224,
              fontWeight: FontWeight.bold,
            ),
          ),
        );
      default:
        return Container();
    }
  },
  onRatingUpdate: (rating) => setState(() {
    knowledgeRating = rating;
  }),
),

Solution

  • Unfortunately, this is not possible to do with flutter_rating_bar. Checking the sources, if the itemBuilder is being used there is a color filter applied to the child that blends the unratedColor with the child widget. This unratedColor is the source and it always wins because of the filter that is set to BlendMode.srcIn.

    This filter is not applied only if the itemBuilder is null. But the only way to make it null is to use the default constructor and set the ratingWidget that repeats the same for all ratings.

    Fortunately, the source code is available and it's just a matter of forking it, removing the filter and pointing the fork in pubspec.yaml to get the desired result like below:

    enter image description here

    pubspec.yaml

    # ...
    dependencies:
      flutter_rating_bar:
        git:
          url: git://github.com/your_git_repo/flutter_rating_bar.git
          ref: master
    

    The changed widget without the color filter.

    class _NoRatingWidget extends StatelessWidget {
      _NoRatingWidget({
        required this.size,
        required this.child,
        required this.enableMask,
        required this.unratedColor,
      });
    
      final double size;
      final Widget child;
      final bool enableMask;
      final Color unratedColor;
    
      @override
      Widget build(BuildContext context) {
        return SizedBox(
          height: size,
          width: size,
          child: FittedBox(
            fit: BoxFit.contain,
            child: child,
          ),
        );
      }
    }