【问题标题】:Inner shadow effect in flutterFlutter 中的内阴影效果
【发布时间】:2019-06-01 09:13:44
【问题描述】:

根据github issue,ShadowBox 中还没有 inset 属性。是否有任何解决方法如何在颤振中模拟内部阴影。

我喜欢实现如下图所示的内阴影效果

【问题讨论】:

标签: flutter flutter-layout shadow


【解决方案1】:
decoration: BoxDecoration(
        boxShadow: [
          const BoxShadow(
            color: your_shadow_color,
          ),
          const BoxShadow(
            color: your_bg_color,
            spreadRadius: -12.0,
            blurRadius: 12.0,
          ),
        ],
      ),

【讨论】:

  • 好答案。虽然Colors.transparentOffset(0, 0)是默认值,所以这3行可以删除。
  • 我不明白它如何带来所需的内部阴影。我在装饰内部添加了阴影,但看不到任何内部阴影效果。
  • @ZahidTekbaş 容器必须为空且不能设置color
  • 不错,但我正在寻找像 CRED 应用程序这样的卡片样式。 -Offset(value,value) 对我有用。
  • 在 Flutter 2.10 版本中,blurRadius 的负值会引发错误。
【解决方案2】:

这是我的工作:

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

class InnerShadow extends SingleChildRenderObjectWidget {
  const InnerShadow({
    Key key,
    this.blur = 10,
    this.color = Colors.black38,
    this.offset = const Offset(10, 10),
    Widget child,
  }) : super(key: key, child: child);

  final double blur;
  final Color color;
  final Offset offset;

  @override
  RenderObject createRenderObject(BuildContext context) {
    final _RenderInnerShadow renderObject = _RenderInnerShadow();
    updateRenderObject(context, renderObject);
    return renderObject;
  }

  @override
  void updateRenderObject(
      BuildContext context, _RenderInnerShadow renderObject) {
    renderObject
      ..color = color
      ..blur = blur
      ..dx = offset.dx
      ..dy = offset.dy;
  }
}

class _RenderInnerShadow extends RenderProxyBox {
  double blur;
  Color color;
  double dx;
  double dy;

  @override
  void paint(PaintingContext context, Offset offset) {
    if (child == null) return;

    final Rect rectOuter = offset & size;
    final Rect rectInner = Rect.fromLTWH(
      offset.dx,
      offset.dy,
      size.width - dx,
      size.height - dy,
    );
    final Canvas canvas = context.canvas..saveLayer(rectOuter, Paint());
    context.paintChild(child, offset);
    final Paint shadowPaint = Paint()
      ..blendMode = BlendMode.srcATop
      ..imageFilter = ImageFilter.blur(sigmaX: blur, sigmaY: blur)
      ..colorFilter = ColorFilter.mode(color, BlendMode.srcOut);

    canvas
      ..saveLayer(rectOuter, shadowPaint)
      ..saveLayer(rectInner, Paint())
      ..translate(dx, dy);
    context.paintChild(child, offset);
    context.canvas..restore()..restore()..restore();
  }
}

然后在某处使用它:

InnerShadow(
  blur: 5,
  color: const Color(0xFF477C70),
  offset: const Offset(5, 5),
  child: Container(
    decoration: const BoxDecoration(
      borderRadius: BorderRadius.all(Radius.circular(8)),
      color: Color(0xFFE9EFEC),
    ),
    height: 100,
  ),
)

结果:

【讨论】:

  • 此代码在 Pub 上以 sums_inner 的形式发布,顺便说一句。 GitHub 存储库中的代码看起来非常熟悉!
  • 不幸的是,我没有;我要试一试的第一件事是写信给 SO,因为据我了解,这违反了CC BY-SA 4.0 的许可(因为没有归属)。也许他们可以给你更多的信息。作为旁注:不确定这里是否接受称其他人为“渣滓”(不管他们做了什么):)
  • 感谢您的建议。你是对的,我的错。我无法编辑我的评论,所以只是在这里复制它:是的,他甚至没有费心改变一行并输入他自己的版权。你知道在哪里写声明吗?
  • @AlexandrPriezzhev 我真的很喜欢上面提供的答案。你能看看这个问题stackoverflow.com/questions/66514745/…。类似的类型
  • @SarveshDalvi,您可以使用我在此处给出的答案。它适用于任何自定义形状。
【解决方案3】:

我接受了@AlexandrPriezzhev 的回答并改进了它以使用标准的Shadow 类(包括其offset 字段的语义),添加了对多个阴影的支持,并削减了saveLayer() 调用应该让它更有效率:

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

class InnerShadow extends SingleChildRenderObjectWidget {
  const InnerShadow({
    Key? key,
    this.shadows = const <Shadow>[],
    Widget? child,
  }) : super(key: key, child: child);

  final List<Shadow> shadows;

  @override
  RenderObject createRenderObject(BuildContext context) {
    final renderObject = _RenderInnerShadow();
    updateRenderObject(context, renderObject);
    return renderObject;
  }

  @override
  void updateRenderObject(
      BuildContext context, _RenderInnerShadow renderObject) {
    renderObject.shadows = shadows;
  }
}

class _RenderInnerShadow extends RenderProxyBox {
  late List<Shadow> shadows;

  @override
  void paint(PaintingContext context, Offset offset) {
    if (child == null) return;
    final bounds = offset & size;

    context.canvas.saveLayer(bounds, Paint());
    context.paintChild(child!, offset);

    for (final shadow in shadows) {
      final shadowRect = bounds.inflate(shadow.blurSigma);
      final shadowPaint = Paint()
        ..blendMode = BlendMode.srcATop
        ..colorFilter = ColorFilter.mode(shadow.color, BlendMode.srcOut)
        ..imageFilter = ImageFilter.blur(
            sigmaX: shadow.blurSigma, sigmaY: shadow.blurSigma);
      context.canvas
        ..saveLayer(shadowRect, shadowPaint)
        ..translate(shadow.offset.dx, shadow.offset.dy);
      context.paintChild(child!, offset);
      context.canvas.restore();
    }

    context.canvas.restore();
  }
}

【讨论】:

  • 嗨,托马斯!谢谢你的版本。效果很好。我得到了一个错误(也在亚历山大版本中)``` ════════ 渲染库捕获的异常═══════════════════════ ══════════ 对象已被处置。相关的导致错误的小部件是 InnerShadow ``` 这可能是由saveLayerpaintChild 结合引起的。我还没有找到解决办法。另请参阅github.com/luigi-rosso/flutter-inner-shadow/issues/1 有没有人解决这个问题?
  • @Richard 感谢您的注意!几天前有人提出了一个编辑,对这篇文章进行了类似的修改,但没有说明原因,所以被拒绝了。我现在在这里做了同样的改变。
  • 我得到方法 'paintChild' 没有为类型 'Canvas' 定义。
  • @user3808307 已编辑;这是上下文而不是画布上的方法。
【解决方案4】:

James 的回答在外部创建了一个阴影,使形状变形。 Mohammed 的答案是渐变而不是适当的阴影,但 Alexandr 的解决方案对我来说非常适合我的小修复! rectInner 必须更新。

final Rect rectInner = Rect.fromLTWH(
    offset.dx,
    offset.dy,
    size.width,
    size.height,
);

【讨论】:

  • 像这样更改rectInner 会移除底部和右侧的阴影。 Alexandr's solution 对我来说效果很好。
【解决方案5】:

您可以使用多个 BoxShadow 进行操作,其中第一个(下方)较暗,顶部较亮,并在其顶部加上灰色边框以使容器弹出。

代码:

Container(
                        width: 100,
                        height: 100,
                        alignment: Alignment.center,
                        decoration: BoxDecoration(
                            border: Border.fromBorderSide(
                              BorderSide(color: Colors.black12),
                            ),
                            shape: BoxShape.circle,
                            boxShadow: [
                              BoxShadow(color: Colors.black, blurRadius: 1, spreadRadius: 0),
                              BoxShadow(color: Colors.white, blurRadius: 10, spreadRadius: 5),
                            ]),
                        child: Icon(Icons.pause, size: 70, color: Colors.black54),
                      ),

结果:

【讨论】:

    【解决方案6】:

    或者,您可以通过使用普通的盒子阴影和两个容器来解决这个问题 - 外部一个用作背景,内部一个提供阴影阴影。

    代码:

          Container(
              padding: EdgeInsets.only(top: 30, left: 10, right: 10),
              child: Container(
                  padding: EdgeInsets.all(10),
                  child: Container(
                      decoration: BoxDecoration(
                          color: Colors.grey,
                          borderRadius: BorderRadius.all(Radius.circular(12))),
                      padding: EdgeInsets.all(10),
                      child: Container(
                        padding: EdgeInsets.zero,
                        decoration: BoxDecoration(
                            color: Colors.white,
                            borderRadius: BorderRadius.all(Radius.circular(8)),
                            boxShadow: [
                              BoxShadow(
                                  color: Colors.white,
                                  blurRadius: 10,
                                  spreadRadius: 10)
                            ]),
                        width: double.infinity,
                        height: 272,
                        child: Center(
                          child: Text("Content goes here"),
                        ),
                      )))),
    

    产生:

    【讨论】:

      【解决方案7】:

      詹姆斯的回答对我没有用。

      所以我只是通过在我的容器/图像上方放置一个内部 渐变 层来实现它,因此,我得到了我想要的那种内部阴影(在我的情况下仅从底部)。

      Stack(children: <Widget>[
            Container(
              decoration: BoxDecoration(
                image: DecorationImage(
                  fit: BoxFit.cover,
                  image: AssetImage('images/bg.jpg') /*NetworkImage(imageUrl)*/,
                ),
              ),
              height: 350.0,
            ),
            Container(
              decoration: BoxDecoration(
                gradient: LinearGradient(
                  begin: FractionalOffset.topCenter,
                  end: FractionalOffset.bottomCenter,
                  colors: [
                    Colors.grey.withOpacity(0.0),
                    Colors.black54,
                  ],
                  stops: [0.95, 1.0],
                ),
              ),
            )
          ],
        )
      

      【讨论】:

        【解决方案8】:

        这是内阴影的代码

        首先创建一个此类

        class InnerShadow extends SingleChildRenderObjectWidget {
          const InnerShadow({
            this.blur = 10,
            this.color = Colors.black38,
            this.offset = const Offset(10, 10),
            required Widget child,
          }) : super(child: child);
        
          final double blur;
          final Color color;
          final Offset offset;
        
          @override
          RenderObject createRenderObject(BuildContext context) {
            final _RenderInnerShadow renderObject = _RenderInnerShadow();
            updateRenderObject(context, renderObject);
            return renderObject;
          }
        
          @override
          void updateRenderObject(
              BuildContext context, _RenderInnerShadow renderObject) {
            renderObject
              ..color = color
              ..blur = blur
              ..dx = offset.dx
              ..dy = offset.dy;
          }
        }
        
        class _RenderInnerShadow extends RenderProxyBox {
          late double blur;
          late Color color;
          late double dx;
          late double dy;
        
          @override
          void paint(PaintingContext context, Offset offset) {
            if (child == null) return;
        
            final Rect rectOuter = offset & size;
            final Rect rectInner = Rect.fromLTWH(
              offset.dx,
              offset.dy,
              size.width - dx,
              size.height - dy,
            );
            final Canvas canvas = context.canvas..saveLayer(rectOuter, Paint());
            context.paintChild(child!, offset);
            final Paint shadowPaint = Paint()
              ..blendMode = BlendMode.srcATop
              ..imageFilter = ImageFilter.blur(sigmaX: blur, sigmaY: blur)
              ..colorFilter = ColorFilter.mode(color, BlendMode.srcOut);
        
            canvas
              ..saveLayer(rectOuter, shadowPaint)
              ..saveLayer(rectInner, Paint())
              ..translate(dx, dy);
            context.paintChild(child!, offset);
            context.canvas
              ..restore()
              ..restore()
              ..restore();
          }
        }
        

        像这样使用InnerShadow类后

        InnerShadow(
              blur: 2,
              offset: const Offset(-3, -8),
              color: Colors.black12.withOpacity(0.2),
              child: Container(
                child: ClipRRect(
                  borderRadius: BorderRadius.circular(30.0),
                  child: Image.asset(
                    'assets/icons/abc.png',
                    fit: BoxFit.fill,
                  ),
                ),
                width: 30.h,
                height: 30.h,
                decoration: BoxDecoration(
                  color: Colors.white.withOpacity(0.01),
                  borderRadius: const BorderRadius.all(Radius.circular(30)),
                ),
              ),
            )
        

        这是我的输出

        【讨论】:

          猜你喜欢
          • 2015-06-13
          • 1970-01-01
          • 2011-05-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-07-15
          • 2010-11-10
          相关资源
          最近更新 更多