Flutter – How can I clip a fixed size gradient and animate it if possible?

Issue

I’m trying to create a visual gauge for audio input. The visual is basic: a linear gradient ranging from green to yellow to red. Problem is, I’m trying to clip the gradient according to the current audio input value. I’m relatively new to Flutter and I tried different widgets to achieve it and found no solution yet.

1

I tried to wrap the decorated Container in an OverflowBox, the parent would be a SizedBox with the appropriate size, according to the value. My last attempt is like this:

Widget build(BuildContext context) {
    return BlocBuilder(
      bloc: _bloc,
      builder: (BuildContext context, MicrophoneSpeakingBaseState state) {
        if (state is MicrophoneSpeakingActiveState && state.speakingValue > 0) {
          return FractionallySizedBox(
            alignment: Alignment.topLeft,
            widthFactor: state.speakingValue,
            child: Stack(
              alignment: Alignment.topLeft,
              overflow: Overflow.clip,
              children: [
                Container(
                  width: size.width,
                  height: size.height,
                  constraints: BoxConstraints(minWidth: size.width),
                  decoration: BoxDecoration(
                    borderRadius: borderRadius,
                    gradient: LinearGradient(
                      begin: Alignment.centerLeft,
                      end: Alignment.centerRight,
                      colors: [Colors.green, Colors.green, Colors.yellow, Colors.red],
                    ),
                  ),
                ),
              ],
            ),
          );
        }
      },
    );
  }

I tough the output would work but actually I get the gradient with the reduced size (according the the current input value) and the gradient itself is scaled down, not keeping the full size. The border radius is also applied as if the bar was smaller.

Solution

Wrap the container in a ClipPath widget like so:

new ClipPath(
          clipper: BoxClipper(clipX: 100.0),
          clipBehavior: Clip.hardEdge,
          child: Container(
              width: 400.0,
              height: 100.0,
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(100.0),
                gradient: LinearGradient(
                  begin: Alignment.centerLeft,
                  end: Alignment.centerRight,
                  colors: [Colors.green, Colors.green, Colors.yellow, Colors.red],
                ),
              ),
            ),
        ),

The ClipPath widget requires you to define a CustomClipper widget. The custom clipper below takes in a paramter clipX, which controls the width of the clipped container. The larger the value of clipX, the smaller the width of the clipped rectangle will be.

class BoxClipper extends CustomClipper<Path> {
  final double clipX;
  BoxClipper({this.clipX});
  Path getClip(Size size) {
    var path = Path();
    path.lineTo(0.0, size.height);
    path.lineTo(size.width - clipX, size.height);
    path.lineTo(size.width - clipX, 0.0);
    path.close();
    return path;
  }
  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}

Answered By – Error404

Answer Checked By – Katrina (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *