Flutter自定义控件绘制内阴影
详细介绍视频地址:内阴影视频介绍
内阴影应用: Flutter Animation 3D仿真书本翻页动画效果
效果图:
主要代码
InnerShadowPage
import 'package:flutter/material.dart';
import 'package:flutter_demo/components/inner_shadow.dart';
class InnerShadowPage extends StatelessWidget {
const InnerShadowPage({Key? key}) : super(key: key);
Color _shadowColor() => const Color.fromRGBO(222, 234, 252, 1);
Widget _textWidget() =>
const Text('data', style: TextStyle(fontSize: 36, color: Colors.black));
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: const Text('InnerShadow'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
width: 200,
height: 200,
clipBehavior: Clip.hardEdge,
decoration:
BoxDecoration(borderRadius: BorderRadius.circular(20)),
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: _shadowColor(),
borderRadius: BorderRadius.circular(20),
),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: const [
BoxShadow(
color: Colors.white,
blurRadius: 10,
spreadRadius: 20,
),
],
),
child: _textWidget(),
),
),
),
InnerShadow(
blur: 6,
shadowColor: _shadowColor(),
offset: const Offset(2, 2),
child: Container(
width: 200,
height: 200,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: _textWidget(),
),
)
],
),
),
);
}
}
自定义InnerShadow控件
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
// https://pub.flutter-io.cn/packages/sums_inner
class InnerShadow extends SingleChildRenderObjectWidget {
const InnerShadow({
Key? key,
this.blur = 10,
this.shadowColor = Colors.black45,
this.offset = const Offset(10, 10),
required Widget child,
}) : super(key: key, child: child);
final double blur;
final Color shadowColor;
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
..shadowColor = shadowColor
..blur = blur
..dx = offset.dx
..dy = offset.dy;
}
}
class RenderInnerShadow extends RenderProxyBox {
late double blur;
late Color shadowColor;
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
..colorFilter = ColorFilter.mode(shadowColor, BlendMode.srcOut)
..imageFilter = ImageFilter.blur(sigmaX: blur, sigmaY: blur);
canvas
..saveLayer(rectOuter, shadowPaint)
..saveLayer(rectInner, Paint())
..translate(dx, dy);
context.paintChild(child!, offset);
context.canvas
..restore()
..restore()
..restore();
}
}
混合模式
List blendMode = [ BlendMode.srcATop, BlendMode.srcOut, BlendMode.clear, BlendMode.src, BlendMode.dst, BlendMode.srcOver, BlendMode.dstOver, BlendMode.srcIn, BlendMode.dstIn, BlendMode.dstOut, BlendMode.dstATop, BlendMode.xor, BlendMode.plus, BlendMode.modulate, BlendMode.screen, BlendMode.overlay, BlendMode.darken, BlendMode.lighten, BlendMode.colorDodge, BlendMode.colorBurn, BlendMode.hardLight, BlendMode.softLight, BlendMode.difference, BlendMode.exclusion, BlendMode.multiply, BlendMode.hue, BlendMode.saturation, BlendMode.color, BlendMode.luminosity, ];
内容涉及到的混合模式(先绘制的是目标图像,后绘制的是源图像)
BlendMode.srcOut
显示源图像,但仅显示两个图像不重叠的位置。目标图像不会渲染,仅将其视为蒙版。目标的颜色通道被忽略,只有不透明度有效
BlendMode.srcATop
源图和目标图相交处绘制原图,不相交绘制目标图
BlendMode.color
取源图像的色调和饱和度,然后取目标图像的亮度