Flutter开发中,常常需要给某些页面加水印,有的是加文字水印,有的是需要加logo水印,简单总结如下:
API:
Stack:层叠布局,让水印 "盖在" 页面内容上面
IgnorePointer:水印不拦截点击事件
Opacity:让水印半透明,不遮挡内容
Transform.rotate:让水印斜着显示
CustomPaint:底层画布绘制,防截图、铺满屏幕水印
栗子:
Dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyWaterMark01());
}
class MyWaterMark01 extends StatelessWidget {
const MyWaterMark01({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text("全屏文字水印")),
body: buildWatermark(
child: const Center(
child: Text(
"我是页面内容",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20),
),
),
watermarkText: "Flutter 内部资料",
),
),
);
}
Widget buildWatermark({required Widget child, required String watermarkText}) {
return Stack(
children: [
child,
IgnorePointer(
child: Opacity(
opacity: 0.15,
child: Transform.rotate(
angle: -0.35,
child: Center(
child: Text(
watermarkText,
style: const TextStyle(
fontSize: 32,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
),
),
),
],
);
}
}
Dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyWaterMark02());
}
class MyWaterMark02 extends StatelessWidget {
const MyWaterMark02({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text("对角铺满水印")),
body: FullScreenWatermark(
content: const Center(
child: Text("页面正常内容", style: TextStyle(fontSize: 24)),
),
text: "内部专用 请勿外传",
),
),
);
}
}
class FullScreenWatermark extends StatelessWidget {
final Widget content;
final String text;
const FullScreenWatermark({
super.key,
required this.content,
required this.text,
});
@override
Widget build(BuildContext context) {
return Stack(
children: [
content,
IgnorePointer(
child: Opacity(
opacity: 0.12,
child: GridView.count(
crossAxisCount: 3,
childAspectRatio: 1.2,
physics: const NeverScrollableScrollPhysics(),
children: List.generate(30, (index) {
return Center(
child: Transform.rotate(
angle: -0.4,
child: Text(
text,
style: const TextStyle(
fontSize: 18,
color: Colors.grey,
),
),
),
);
}),
),
),
),
],
);
}
}

Dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyWaterMark03());
}
class MyWaterMark03 extends StatelessWidget {
const MyWaterMark03({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text("图片水印(Logo)")),
body: ImageWatermark(
child: Container(
color: Colors.white,
child: const Center(
child: Text("主页面内容", style: TextStyle(fontSize: 24)),
),
),
),
),
);
}
}
class ImageWatermark extends StatelessWidget {
final Widget child;
const ImageWatermark({super.key, required this.child});
@override
Widget build(BuildContext context) {
return Stack(
children: [
child,
IgnorePointer(
child: Align(
alignment: Alignment.bottomRight,
child: Opacity(
opacity: 0.4,
child: Container(
width: 80,
height: 80,
margin: const EdgeInsets.all(20),
child: const FlutterLogo(),
),
),
),
),
],
);
}
}

Dart
import 'package:flutter/material.dart';
void main() => runApp(const MaterialApp(home: CustomPaintWatermark()));
class CustomPaintWatermark extends StatelessWidget {
const CustomPaintWatermark({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("CustomPaint 水印")),
body: Stack(
children: [
// 页面内容
const Center(
child: Text(
"页面内容可点击",
style: TextStyle(fontSize: 24),
),
),
IgnorePointer(
child: Container(
width: double.infinity,
height: double.infinity,
color: Colors.transparent,
child: CustomPaint(painter: WatermarkPainter()),
),
),
],
),
);
}
}
class WatermarkPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final textStyle = TextStyle(
color: Colors.grey.withOpacity(0.3),
fontSize: 24,
fontWeight: FontWeight.bold,
);
const text = "内部资料 请勿外传";
for (double x = 0; x < size.width; x += 220) {
for (double y = 0; y < size.height; y += 200) {
canvas.save();
canvas.translate(x, y);
canvas.rotate(-0.3);
final textSpan = TextSpan(text: text, style: textStyle);
final textPainter = TextPainter(
text: textSpan,
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(canvas, Offset.zero);
canvas.restore();
}
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
