Flutter自定义控件绘制内阴影
详细介绍视频地址链接:Flutter自定义控件-RenderObject-Canvas绘制内阴影(InnerShadow)|flutter动画(准备)
Flutter3D书本翻页视频地址链接: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));
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;
RenderObject createRenderObject(BuildContext context) {
final RenderInnerShadow renderObject = RenderInnerShadow();
updateRenderObject(context, renderObject);
return renderObject;
}
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;
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
取源图像的色调和饱和度,然后取目标图像的亮度