Flutter - 如何剪辑固定大小的渐变并在可能的情况下对其进行动画处理?

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

我正在尝试为音频输入创建一个视觉仪表。视觉是基本的:从绿色到黄色再到红色的线性渐变。问题是,我试图根据当前的音频输入值来剪辑渐变。我对 Flutter 比较陌生,我尝试了不同的小部件来实现它,但还没有找到解决方案。

我尝试将装饰后的 Container 包裹在 OverflowBox 中,根据值,父项将是具有适当大小的 SizedBox。我最后的尝试是这样的:

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],
                    ),
                  ),
                ),
              ],
            ),
          );
        }
      },
    );
  }

我认为输出会起作用,但实际上我得到了尺寸减小的渐变(根据当前输入值)并且渐变本身按比例缩小,而不是保持完整尺寸。还应用了边框半径,就好像条形图更小一样。

像这样将容器包装在 ClipPath 小部件中:

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],
                ),
              ),
            ),
        ),

ClipPath 小部件需要您定义 CustomClipper 小部件。下面的自定义裁剪器接受一个参数 clipX,它控制裁剪容器的宽度。 clipX的值越大,裁剪后的矩形宽度越小

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;
}

感谢@Error4on4,我扩展了他的答案,使其也能够同时剪裁左侧和右侧:

class FractionalRangeClipper extends CustomClipper<Path> {
  final double? begin;
  final double? end;
  FractionalRangeClipper({required this.begin, required this.end});
  Path getClip(Size size) {
    final path = Path();
    final absoluteBegin = size.width * (begin ?? 0);
    final absoluteEnd = size.width * (end ?? 1);
    path.lineTo(absoluteBegin, 0);
    path.lineTo(absoluteBegin, size.height);
    path.lineTo(absoluteEnd, size.height);
    path.lineTo(absoluteEnd, 0);
    path.close();
    return path;
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}

用法:

ClipPath(
   clipper: FractionalRangeClipper(begin: 0.2, end: 0.7),
   clipBehavior: Clip.hardEdge,
   child: YourWidget(),
),