How Can I do a text cut into a box container that leaves the content under it seen?
As you can see the immage is seen under the text. How could I do this?
You have to use CustomPainter
, TextPainter
, BlendMode
and saveLayer
:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Playground',
home: TestPage(),
);
}
}
class TestPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
image: DecorationImage(image: AssetImage('assets/earth.jpg'), fit: BoxFit.cover),
),
child: Center(
child: CustomPaint(
painter: CutOutTextPainter(text: 'YOUR NAME'),
),
),
);
}
}
class CutOutTextPainter extends CustomPainter {
CutOutTextPainter({this.text}) {
_textPainter = TextPainter(
text: TextSpan(
text: text,
style: TextStyle(
fontSize: 40.0,
fontWeight: FontWeight.w600,
),
),
textDirection: TextDirection.ltr,
);
_textPainter.layout();
}
final String text;
TextPainter _textPainter;
@override
void paint(Canvas canvas, Size size) {
// Draw the text in the middle of the canvas
final textOffset = size.center(Offset.zero) - _textPainter.size.center(Offset.zero);
final textRect = textOffset & _textPainter.size;
// The box surrounding the text should be 10 pixels larger, with 4 pixels corner radius
final boxRect = RRect.fromRectAndRadius(textRect.inflate(10.0), Radius.circular(4.0));
final boxPaint = Paint()..color = Colors.white..blendMode=BlendMode.srcOut;
canvas.saveLayer(boxRect.outerRect, Paint());
_textPainter.paint(canvas, textOffset);
canvas.drawRRect(boxRect, boxPaint);
canvas.restore();
}
@override
bool shouldRepaint(CutOutTextPainter oldDelegate) {
return text != oldDelegate.text;
}
}
Answer:
You could use a ShaderMask for that, which allows you to apply a shader to a widget, taking a Blend Mode into account. The Blend Mode is what we’re interested in, so the Shader will be a simple color:
class Cutout extends StatelessWidget {
const Cutout({
Key key,
@required this.color,
@required this.child,
}) : super(key: key);
final Color color;
final Widget child;
@override
Widget build(BuildContext context) {
return ShaderMask(
blendMode: BlendMode.srcOut,
shaderCallback: (bounds) => LinearGradient(colors: [color], stops: [0.0]).createShader(bounds),
child: child,
);
}
}
For your exact example image, the child should be a Text
widget and you should also include this in a ClipRRect
for the rounded corners (or you can figure out more optimal solutions using BoxDecoration
if the performance impact of ClipRRect
is an issue)
The advantage of this solution is that it works with any widget as a child and that it’s a composable widget that you can pop in your layout.
Tags: androidandroid, text