From 88b4f13185cda24284b279c5e746ffee9ad9395e Mon Sep 17 00:00:00 2001
From: engineer
Date: Tue, 14 Apr 2026 18:17:24 +0800
Subject: [PATCH 1/4] =?UTF-8?q?feat(watermark):=20=E6=B7=BB=E5=8A=A0?=
=?UTF-8?q?=E6=B0=B4=E5=8D=B0=E7=BB=84=E4=BB=B6=E5=8F=8A=E7=A4=BA=E4=BE=8B?=
=?UTF-8?q?=E9=A1=B5=E9=9D=A2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增 TWatermark 组件支持文本水印功能
- 实现单行、多行文本水印类型
- 提供水平、垂直、网格三种排列方式
- 支持自定义颜色、透明度、字体大小等样式
- 添加水印旋转和间距调整功能
- 集成水印组件到示例应用中
- 更新 iOS 最低版本要求至 13.0
- 添加完整的单元测试覆盖各种使用场景
---
.../ios/Flutter/AppFrameworkInfo.plist | 2 +-
tdesign-component/example/ios/Podfile | 2 +-
tdesign-component/example/ios/Podfile.lock | 13 +-
.../ios/Runner.xcodeproj/project.pbxproj | 6 +-
.../lib/component_test/watermark_demo.dart | 241 ++++++++++++++
tdesign-component/example/lib/config.dart | 5 +
.../example/lib/page/t_watermark_page.dart | 313 ++++++++++++++++++
.../src/components/watermark/t_watermark.dart | 307 +++++++++++++++++
tdesign-component/lib/tdesign_flutter.dart | 1 +
tdesign-component/test/t_watermark_test.dart | 166 ++++++++++
10 files changed, 1048 insertions(+), 8 deletions(-)
create mode 100644 tdesign-component/example/lib/component_test/watermark_demo.dart
create mode 100644 tdesign-component/example/lib/page/t_watermark_page.dart
create mode 100644 tdesign-component/lib/src/components/watermark/t_watermark.dart
create mode 100644 tdesign-component/test/t_watermark_test.dart
diff --git a/tdesign-component/example/ios/Flutter/AppFrameworkInfo.plist b/tdesign-component/example/ios/Flutter/AppFrameworkInfo.plist
index 7c5696400..1dc6cf765 100644
--- a/tdesign-component/example/ios/Flutter/AppFrameworkInfo.plist
+++ b/tdesign-component/example/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
CFBundleVersion
1.0
MinimumOSVersion
- 12.0
+ 13.0
diff --git a/tdesign-component/example/ios/Podfile b/tdesign-component/example/ios/Podfile
index b331c7b2b..5981b62b3 100644
--- a/tdesign-component/example/ios/Podfile
+++ b/tdesign-component/example/ios/Podfile
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
-platform :ios, '12.0'
+platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
diff --git a/tdesign-component/example/ios/Podfile.lock b/tdesign-component/example/ios/Podfile.lock
index 4c9913399..98199cfb8 100644
--- a/tdesign-component/example/ios/Podfile.lock
+++ b/tdesign-component/example/ios/Podfile.lock
@@ -2,21 +2,28 @@ PODS:
- Flutter (1.0.0)
- image_picker_ios (0.0.1):
- Flutter
+ - shared_preferences_foundation (0.0.1):
+ - Flutter
+ - FlutterMacOS
DEPENDENCIES:
- Flutter (from `Flutter`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
+ - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
EXTERNAL SOURCES:
Flutter:
:path: Flutter
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
+ shared_preferences_foundation:
+ :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
SPEC CHECKSUMS:
- Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
- image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
+ Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
+ image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
+ shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
-PODFILE CHECKSUM: cf0c950f7e9a456b4e325f5b8fc0f98906a3705a
+PODFILE CHECKSUM: 53026eaf67b63baa2e316e457f9701dbdf33f41a
COCOAPODS: 1.16.2
diff --git a/tdesign-component/example/ios/Runner.xcodeproj/project.pbxproj b/tdesign-component/example/ios/Runner.xcodeproj/project.pbxproj
index a00363849..79f9c833d 100644
--- a/tdesign-component/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/tdesign-component/example/ios/Runner.xcodeproj/project.pbxproj
@@ -357,7 +357,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -438,7 +438,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -487,7 +487,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
diff --git a/tdesign-component/example/lib/component_test/watermark_demo.dart b/tdesign-component/example/lib/component_test/watermark_demo.dart
new file mode 100644
index 000000000..bf6bf9ec9
--- /dev/null
+++ b/tdesign-component/example/lib/component_test/watermark_demo.dart
@@ -0,0 +1,241 @@
+import 'package:flutter/material.dart';
+import 'package:tdesign_flutter/tdesign_flutter.dart';
+
+/// TWatermark 水印组件使用示例
+class WatermarkDemo extends StatelessWidget {
+ const WatermarkDemo({Key? key}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('TWatermark 水印组件演示'),
+ ),
+ body: SingleChildScrollView(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // 示例1: 基础水印
+ _buildSection(
+ context,
+ '基础水印',
+ Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: 'TDesign Flutter',
+ ),
+ ),
+ ),
+
+ const SizedBox(height: 24),
+
+ // 示例2: 不同布局
+ _buildSection(
+ context,
+ '水平布局',
+ Container(
+ height: 150,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '内部资料',
+ layout: TWatermarkLayout.horizontal,
+ gapX: 150,
+ ),
+ ),
+ ),
+
+ const SizedBox(height: 24),
+
+ // 示例3: 自定义样式
+ _buildSection(
+ context,
+ '自定义颜色和透明度',
+ Container(
+ height: 150,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '保密文档',
+ textColor: TTheme.of(context).errorNormalColor,
+ opacity: 0.2,
+ textSize: 18,
+ ),
+ ),
+ ),
+
+ const SizedBox(height: 24),
+
+ // 示例4: 图片上的水印
+ _buildSection(
+ context,
+ '图片水印',
+ Container(
+ height: 250,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '仅供查看',
+ opacity: 0.15,
+ rotate: -30,
+ child: Center(
+ child: Container(
+ width: 200,
+ height: 200,
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ colors: [
+ TTheme.of(context).brandLightColor,
+ TTheme.of(context).brandFocusColor,
+ ],
+ begin: Alignment.topLeft,
+ end: Alignment.bottomRight,
+ ),
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: Icon(
+ TIcons.image,
+ size: 80,
+ color: TTheme.of(context).brandNormalColor,
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+
+ const SizedBox(height: 24),
+
+ // 示例5: 列表上的水印
+ _buildSection(
+ context,
+ '列表现水印',
+ Container(
+ height: 300,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '内部数据',
+ opacity: 0.08,
+ child: ListView.builder(
+ itemCount: 8,
+ itemBuilder: (context, index) {
+ return Container(
+ padding: const EdgeInsets.all(12),
+ decoration: BoxDecoration(
+ border: Border(
+ bottom: BorderSide(
+ color: TTheme.of(context).componentStrokeColor,
+ width: 0.5,
+ ),
+ ),
+ ),
+ child: Row(
+ children: [
+ CircleAvatar(
+ backgroundColor: TTheme.of(context).brandLightColor,
+ child: Text('${index + 1}'),
+ ),
+ const SizedBox(width: 12),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ TText(
+ '数据项 ${index + 1}',
+ font: TTheme.of(context).fontBodyMedium,
+ ),
+ const SizedBox(height: 4),
+ TText(
+ '这是第 ${index + 1} 条数据',
+ font: TTheme.of(context).fontBodySmall,
+ textColor: TTheme.of(context).textColorSecondary,
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ },
+ ),
+ ),
+ ),
+ ),
+
+ const SizedBox(height: 24),
+
+ // 示例6: 表单草稿状态
+ _buildSection(
+ context,
+ '表单草稿标识',
+ Container(
+ height: 300,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '草稿',
+ opacity: 0.08,
+ textSize: 48,
+ fontWeight: FontWeight.bold,
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ children: [
+ TInput(
+ leftLabel: '标题',
+ hintText: '请输入标题',
+ ),
+ const SizedBox(height: 12),
+ TInput(
+ leftLabel: '内容',
+ hintText: '请输入内容',
+ ),
+ const SizedBox(height: 12),
+ TButton(
+ text: '保存草稿',
+ theme: TButtonTheme.primary,
+ onTap: () {},
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ Widget _buildSection(BuildContext context, String title, Widget content) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ TText(
+ title,
+ font: TTheme.of(context).fontTitleMedium,
+ textColor: TTheme.of(context).textColorPrimary,
+ ),
+ const SizedBox(height: 12),
+ content,
+ ],
+ );
+ }
+}
diff --git a/tdesign-component/example/lib/config.dart b/tdesign-component/example/lib/config.dart
index 137502e39..d31812202 100644
--- a/tdesign-component/example/lib/config.dart
+++ b/tdesign-component/example/lib/config.dart
@@ -68,6 +68,7 @@ import 'page/t_time_counter_page.dart';
import 'page/t_toast_page.dart';
import 'page/t_tree_select_page.dart';
import 'page/t_upload_page.dart';
+import 'page/t_watermark_page.dart';
import 'page/todo_page.dart';
PageBuilder _wrapInheritedTheme(WidgetBuilder builder) {
@@ -287,6 +288,10 @@ Map> exampleMap = {
text: 'Tag 标签',
name: 'tag',
pageBuilder: _wrapInheritedTheme((context) => const TTagPage())),
+ ExamplePageModel(
+ text: 'Watermark 水印',
+ name: 'watermark',
+ pageBuilder: _wrapInheritedTheme((context) => const TWatermarkPage())),
],
'反馈': [
ExamplePageModel(
diff --git a/tdesign-component/example/lib/page/t_watermark_page.dart b/tdesign-component/example/lib/page/t_watermark_page.dart
new file mode 100644
index 000000000..d409d14d7
--- /dev/null
+++ b/tdesign-component/example/lib/page/t_watermark_page.dart
@@ -0,0 +1,313 @@
+import 'package:flutter/material.dart';
+import 'package:tdesign_flutter/tdesign_flutter.dart';
+
+import '../../annotation/demo.dart';
+import '../../base/example_widget.dart';
+
+class TWatermarkPage extends StatelessWidget {
+ const TWatermarkPage({Key? key}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return ExamplePage(
+ title: '水印 Watermark',
+ exampleCodeGroup: 'watermark',
+ children: [
+ ExampleModule(title: '基础用法', children: [
+ ExampleItem(desc: '单行文本水印:', builder: _buildSingleLine),
+ ExampleItem(desc: '多行文本水印:', builder: _buildMultiLine),
+ ]),
+ ExampleModule(title: '排列方式', children: [
+ ExampleItem(desc: '水平排列:', builder: _buildHorizontalLayout),
+ ExampleItem(desc: '垂直排列:', builder: _buildVerticalLayout),
+ ExampleItem(desc: '网格排列:', builder: _buildGridLayout),
+ ]),
+ ExampleModule(title: '自定义样式', children: [
+ ExampleItem(desc: '自定义颜色和透明度:', builder: _buildCustomColor),
+ ExampleItem(desc: '自定义字体大小:', builder: _buildCustomSize),
+ ExampleItem(desc: '自定义旋转角度:', builder: _buildCustomRotate),
+ ExampleItem(desc: '自定义间距:', builder: _buildCustomGap),
+ ]),
+ ExampleModule(title: '带内容的水印', children: [
+ ExampleItem(desc: '图片上的水印:', builder: _buildImageWatermark),
+ ExampleItem(desc: '列表上的水印:', builder: _buildListWatermark),
+ ExampleItem(desc: '表单上的水印:', builder: _buildFormWatermark),
+ ]),
+ ],
+ );
+ }
+
+ @Demo(group: 'watermark')
+ Widget _buildSingleLine(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: 'TDesign Flutter',
+ type: TWatermarkType.singleLine,
+ ),
+ );
+ }
+
+ @Demo(group: 'watermark')
+ Widget _buildMultiLine(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: 'TDesign\nFlutter',
+ type: TWatermarkType.multiLine,
+ ),
+ );
+ }
+
+ @Demo(group: 'watermark')
+ Widget _buildHorizontalLayout(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '内部资料',
+ layout: TWatermarkLayout.horizontal,
+ gapX: 150,
+ ),
+ );
+ }
+
+ @Demo(group: 'watermark')
+ Widget _buildVerticalLayout(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '机密',
+ layout: TWatermarkLayout.vertical,
+ gapY: 80,
+ ),
+ );
+ }
+
+ @Demo(group: 'watermark')
+ Widget _buildGridLayout(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: 'TDesign',
+ layout: TWatermarkLayout.grid,
+ gapX: 120,
+ gapY: 80,
+ ),
+ );
+ }
+
+ @Demo(group: 'watermark')
+ Widget _buildCustomColor(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '保密文档',
+ textColor: TTheme.of(context).errorNormalColor,
+ opacity: 0.2,
+ ),
+ );
+ }
+
+ @Demo(group: 'watermark')
+ Widget _buildCustomSize(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '大字体水印',
+ textSize: 24,
+ fontWeight: FontWeight.bold,
+ ),
+ );
+ }
+
+ @Demo(group: 'watermark')
+ Widget _buildCustomRotate(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '旋转45度',
+ rotate: -45,
+ gapX: 150,
+ gapY: 100,
+ ),
+ );
+ }
+
+ @Demo(group: 'watermark')
+ Widget _buildCustomGap(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '密集',
+ gapX: 60,
+ gapY: 40,
+ textSize: 12,
+ ),
+ );
+ }
+
+ @Demo(group: 'watermark')
+ Widget _buildImageWatermark(BuildContext context) {
+ return Container(
+ height: 300,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '仅供查看',
+ child: Center(
+ child: Container(
+ width: 200,
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).brandFocusColor,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: Icon(
+ TIcons.image,
+ size: 80,
+ color: TTheme.of(context).brandNormalColor,
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ @Demo(group: 'watermark')
+ Widget _buildListWatermark(BuildContext context) {
+ return Container(
+ height: 300,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '内部数据',
+ opacity: 0.1,
+ child: ListView.builder(
+ itemCount: 10,
+ itemBuilder: (context, index) {
+ return Container(
+ padding: const EdgeInsets.all(16),
+ decoration: BoxDecoration(
+ border: Border(
+ bottom: BorderSide(
+ color: TTheme.of(context).componentStrokeColor,
+ width: 0.5,
+ ),
+ ),
+ ),
+ child: Row(
+ children: [
+ CircleAvatar(
+ backgroundColor: TTheme.of(context).brandLightColor,
+ child: Text('${index + 1}'),
+ ),
+ const SizedBox(width: 12),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ TText(
+ '列表项 ${index + 1}',
+ font: TTheme.of(context).fontBodyMedium,
+ ),
+ const SizedBox(height: 4),
+ TText(
+ '这是第 ${index + 1} 条数据的描述信息',
+ font: TTheme.of(context).fontBodySmall,
+ textColor: TTheme.of(context).textColorSecondary,
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ },
+ ),
+ ),
+ );
+ }
+
+ @Demo(group: 'watermark')
+ Widget _buildFormWatermark(BuildContext context) {
+ return Container(
+ height: 350,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '草稿',
+ opacity: 0.08,
+ textSize: 48,
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ children: [
+ TInput(
+ leftLabel: '姓名',
+ hintText: '请输入姓名',
+ ),
+ const SizedBox(height: 16),
+ TInput(
+ leftLabel: '邮箱',
+ hintText: '请输入邮箱',
+ ),
+ const SizedBox(height: 16),
+ TInput(
+ leftLabel: '电话',
+ hintText: '请输入电话号码',
+ ),
+ const SizedBox(height: 16),
+ TButton(
+ text: '提交',
+ theme: TButtonTheme.primary,
+ onTap: () {},
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/tdesign-component/lib/src/components/watermark/t_watermark.dart b/tdesign-component/lib/src/components/watermark/t_watermark.dart
new file mode 100644
index 000000000..ccd794109
--- /dev/null
+++ b/tdesign-component/lib/src/components/watermark/t_watermark.dart
@@ -0,0 +1,307 @@
+import 'package:flutter/material.dart';
+import '../../../tdesign_flutter.dart';
+
+/// 水印类型
+enum TWatermarkType {
+ /// 单行文本
+ singleLine,
+
+ /// 多行文本
+ multiLine,
+}
+
+/// 水印排列方式
+enum TWatermarkLayout {
+ /// 水平排列
+ horizontal,
+
+ /// 垂直排列
+ vertical,
+
+ /// 网格排列
+ grid,
+}
+
+/// 水印组件
+class TWatermark extends StatefulWidget {
+ const TWatermark({
+ Key? key,
+ required this.text,
+ this.type = TWatermarkType.multiLine,
+ this.layout = TWatermarkLayout.grid,
+ this.textColor,
+ this.textSize = 14,
+ this.fontWeight = FontWeight.normal,
+ this.opacity = 0.15,
+ this.rotate = -20,
+ this.gapX = 100,
+ this.gapY = 100,
+ this.offsetX = 0,
+ this.offsetY = 0,
+ this.zIndex = 1,
+ this.width,
+ this.height,
+ this.child,
+ }) : super(key: key);
+
+ /// 水印文本内容
+ final String text;
+
+ /// 水印类型
+ final TWatermarkType type;
+
+ /// 水印排列方式
+ final TWatermarkLayout layout;
+
+ /// 水印文字颜色
+ final Color? textColor;
+
+ /// 水印文字大小
+ final double textSize;
+
+ /// 水印文字粗细
+ final FontWeight fontWeight;
+
+ /// 水印透明度 (0.0 - 1.0)
+ final double opacity;
+
+ /// 水印旋转角度(度)
+ final double rotate;
+
+ /// 水平间距
+ final double gapX;
+
+ /// 垂直间距
+ final double gapY;
+
+ /// 水平偏移量
+ final double offsetX;
+
+ /// 垂直偏移量
+ final double offsetY;
+
+ /// z-index层级
+ final int zIndex;
+
+ /// 水印区域宽度
+ final double? width;
+
+ /// 水印区域高度
+ final double? height;
+
+ /// 子组件(水印将覆盖在此组件上方)
+ final Widget? child;
+
+ @override
+ State createState() => _TWatermarkState();
+}
+
+class _TWatermarkState extends State {
+ @override
+ Widget build(BuildContext context) {
+ final theme = TTheme.of(context);
+ final textColor = widget.textColor ?? theme.textColorPlaceholder;
+
+ return Stack(
+ children: [
+ // 子组件
+ if (widget.child != null) widget.child!,
+
+ // 水印层
+ Positioned.fill(
+ child: IgnorePointer(
+ ignoring: true,
+ child: CustomPaint(
+ painter: _WatermarkPainter(
+ text: widget.text,
+ type: widget.type,
+ layout: widget.layout,
+ textColor: textColor.withOpacity(widget.opacity),
+ textSize: widget.textSize,
+ fontWeight: widget.fontWeight,
+ rotate: widget.rotate,
+ gapX: widget.gapX,
+ gapY: widget.gapY,
+ offsetX: widget.offsetX,
+ offsetY: widget.offsetY,
+ ),
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+}
+
+/// 水印绘制器
+class _WatermarkPainter extends CustomPainter {
+ final String text;
+ final TWatermarkType type;
+ final TWatermarkLayout layout;
+ final Color textColor;
+ final double textSize;
+ final FontWeight fontWeight;
+ final double rotate;
+ final double gapX;
+ final double gapY;
+ final double offsetX;
+ final double offsetY;
+
+ _WatermarkPainter({
+ required this.text,
+ required this.type,
+ required this.layout,
+ required this.textColor,
+ required this.textSize,
+ required this.fontWeight,
+ required this.rotate,
+ required this.gapX,
+ required this.gapY,
+ required this.offsetX,
+ required this.offsetY,
+ });
+
+ @override
+ void paint(Canvas canvas, Size size) {
+ final paint = Paint()
+ ..color = textColor
+ ..isAntiAlias = true;
+
+ final textPainter = TextPainter(
+ text: TextSpan(
+ text: text,
+ style: TextStyle(
+ color: textColor,
+ fontSize: textSize,
+ fontWeight: fontWeight,
+ ),
+ ),
+ textDirection: TextDirection.ltr,
+ );
+
+ textPainter.layout();
+
+ final textWidth = textPainter.width;
+ final textHeight = textPainter.height;
+
+ // 根据布局方式绘制水印
+ switch (layout) {
+ case TWatermarkLayout.horizontal:
+ _drawHorizontal(canvas, textPainter, size, textWidth, textHeight);
+ break;
+ case TWatermarkLayout.vertical:
+ _drawVertical(canvas, textPainter, size, textWidth, textHeight);
+ break;
+ case TWatermarkLayout.grid:
+ _drawGrid(canvas, textPainter, size, textWidth, textHeight);
+ break;
+ }
+ }
+
+ /// 水平排列
+ void _drawHorizontal(
+ Canvas canvas,
+ TextPainter textPainter,
+ Size size,
+ double textWidth,
+ double textHeight,
+ ) {
+ final centerY = size.height / 2 + offsetY;
+ var currentX = offsetX;
+
+ while (currentX < size.width) {
+ _drawTextWithRotation(
+ canvas,
+ textPainter,
+ Offset(currentX, centerY - textHeight / 2),
+ );
+ currentX += textWidth + gapX;
+ }
+ }
+
+ /// 垂直排列
+ void _drawVertical(
+ Canvas canvas,
+ TextPainter textPainter,
+ Size size,
+ double textWidth,
+ double textHeight,
+ ) {
+ final centerX = size.width / 2 + offsetX;
+ var currentY = offsetY;
+
+ while (currentY < size.height) {
+ _drawTextWithRotation(
+ canvas,
+ textPainter,
+ Offset(centerX - textWidth / 2, currentY),
+ );
+ currentY += textHeight + gapY;
+ }
+ }
+
+ /// 网格排列
+ void _drawGrid(
+ Canvas canvas,
+ TextPainter textPainter,
+ Size size,
+ double textWidth,
+ double textHeight,
+ ) {
+ var currentY = -textHeight + offsetY;
+
+ while (currentY < size.height + textHeight) {
+ var currentX = -textWidth + offsetX;
+
+ while (currentX < size.width + textWidth) {
+ _drawTextWithRotation(
+ canvas,
+ textPainter,
+ Offset(currentX, currentY),
+ );
+ currentX += textWidth + gapX;
+ }
+ currentY += textHeight + gapY;
+ }
+ }
+
+ /// 绘制带旋转的文本
+ void _drawTextWithRotation(
+ Canvas canvas,
+ TextPainter textPainter,
+ Offset position,
+ ) {
+ canvas.save();
+
+ // 移动到文本中心点
+ final center = Offset(
+ position.dx + textPainter.width / 2,
+ position.dy + textPainter.height / 2,
+ );
+
+ canvas.translate(center.dx, center.dy);
+ // 旋转
+ canvas.rotate(rotate * 3.1415926535897932 / 180);
+ canvas.translate(-center.dx, -center.dy);
+
+ // 绘制文本
+ textPainter.paint(canvas, position);
+
+ canvas.restore();
+ }
+
+ @override
+ bool shouldRepaint(covariant _WatermarkPainter oldDelegate) {
+ return oldDelegate.text != text ||
+ oldDelegate.type != type ||
+ oldDelegate.layout != layout ||
+ oldDelegate.textColor != textColor ||
+ oldDelegate.textSize != textSize ||
+ oldDelegate.fontWeight != fontWeight ||
+ oldDelegate.rotate != rotate ||
+ oldDelegate.gapX != gapX ||
+ oldDelegate.gapY != gapY ||
+ oldDelegate.offsetX != offsetX ||
+ oldDelegate.offsetY != offsetY;
+ }
+}
diff --git a/tdesign-component/lib/tdesign_flutter.dart b/tdesign-component/lib/tdesign_flutter.dart
index 675a6500d..cf1cc5bb0 100644
--- a/tdesign-component/lib/tdesign_flutter.dart
+++ b/tdesign-component/lib/tdesign_flutter.dart
@@ -94,6 +94,7 @@ export 'src/components/time_counter/t_time_counter_style.dart';
export 'src/components/toast/t_toast.dart';
export 'src/components/tree/t_tree_select.dart';
export 'src/components/upload/t_upload.dart';
+export 'src/components/watermark/t_watermark.dart';
export 'src/theme/basic.dart';
export 'src/theme/resource_delegate.dart';
export 'src/theme/t_colors.dart';
diff --git a/tdesign-component/test/t_watermark_test.dart b/tdesign-component/test/t_watermark_test.dart
new file mode 100644
index 000000000..87f3ad9b5
--- /dev/null
+++ b/tdesign-component/test/t_watermark_test.dart
@@ -0,0 +1,166 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:tdesign_flutter/tdesign_flutter.dart';
+
+void main() {
+ group('TWatermark', () {
+ testWidgets('水印组件基本渲染测试', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Scaffold(
+ body: TWatermark(
+ text: '测试水印',
+ ),
+ ),
+ ),
+ );
+
+ expect(find.byType(TWatermark), findsOneWidget);
+ });
+
+ testWidgets('水印组件带子组件测试', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Scaffold(
+ body: TWatermark(
+ text: '测试水印',
+ child: const Text('子组件内容'),
+ ),
+ ),
+ ),
+ );
+
+ expect(find.byType(TWatermark), findsOneWidget);
+ expect(find.text('子组件内容'), findsOneWidget);
+ });
+
+ testWidgets('水印组件单行类型测试', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Scaffold(
+ body: TWatermark(
+ text: '单行水印',
+ type: TWatermarkType.singleLine,
+ ),
+ ),
+ ),
+ );
+
+ expect(find.byType(TWatermark), findsOneWidget);
+ });
+
+ testWidgets('水印组件多行类型测试', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Scaffold(
+ body: TWatermark(
+ text: '多行\n水印',
+ type: TWatermarkType.multiLine,
+ ),
+ ),
+ ),
+ );
+
+ expect(find.byType(TWatermark), findsOneWidget);
+ });
+
+ testWidgets('水印组件水平布局测试', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Scaffold(
+ body: SizedBox(
+ height: 200,
+ child: TWatermark(
+ text: '水平',
+ layout: TWatermarkLayout.horizontal,
+ ),
+ ),
+ ),
+ ),
+ );
+
+ expect(find.byType(TWatermark), findsOneWidget);
+ });
+
+ testWidgets('水印组件垂直布局测试', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Scaffold(
+ body: SizedBox(
+ height: 200,
+ child: TWatermark(
+ text: '垂直',
+ layout: TWatermarkLayout.vertical,
+ ),
+ ),
+ ),
+ ),
+ );
+
+ expect(find.byType(TWatermark), findsOneWidget);
+ });
+
+ testWidgets('水印组件网格布局测试', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Scaffold(
+ body: SizedBox(
+ height: 200,
+ child: TWatermark(
+ text: '网格',
+ layout: TWatermarkLayout.grid,
+ ),
+ ),
+ ),
+ ),
+ );
+
+ expect(find.byType(TWatermark), findsOneWidget);
+ });
+
+ testWidgets('水印组件自定义样式测试', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Scaffold(
+ body: TWatermark(
+ text: '自定义',
+ textColor: Colors.red,
+ textSize: 20,
+ opacity: 0.5,
+ rotate: -45,
+ gapX: 100,
+ gapY: 80,
+ ),
+ ),
+ ),
+ );
+
+ expect(find.byType(TWatermark), findsOneWidget);
+ });
+
+ testWidgets('水印组件忽略指针测试', (WidgetTester tester) async {
+ var buttonClicked = false;
+
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Scaffold(
+ body: TWatermark(
+ text: '测试',
+ child: ElevatedButton(
+ onPressed: () {
+ buttonClicked = true;
+ },
+ child: const Text('点击我'),
+ ),
+ ),
+ ),
+ ),
+ );
+
+ await tester.tap(find.text('点击我'));
+ await tester.pump();
+
+ expect(buttonClicked, true);
+ });
+ });
+}
From 06e18e937c48487b75575f1221de0fdb66eaf426 Mon Sep 17 00:00:00 2001
From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com>
Date: Tue, 14 Apr 2026 10:34:19 +0000
Subject: [PATCH 2/4] [autofix.ci] apply automated fixes
---
.../code/watermark._buildCustomColor.txt | 15 +
.../assets/code/watermark._buildCustomGap.txt | 16 +
.../code/watermark._buildCustomRotate.txt | 16 +
.../code/watermark._buildCustomSize.txt | 15 +
.../code/watermark._buildFormWatermark.txt | 42 ++
.../code/watermark._buildGridLayout.txt | 16 +
.../code/watermark._buildHorizontalLayout.txt | 15 +
.../code/watermark._buildImageWatermark.txt | 28 ++
.../code/watermark._buildListWatermark.txt | 56 +++
.../assets/code/watermark._buildMultiLine.txt | 14 +
.../code/watermark._buildSingleLine.txt | 14 +
.../code/watermark._buildVerticalLayout.txt | 15 +
tdesign-site/src/watermark/README.md | 390 ++++++++++++++++++
13 files changed, 652 insertions(+)
create mode 100644 tdesign-component/example/assets/code/watermark._buildCustomColor.txt
create mode 100644 tdesign-component/example/assets/code/watermark._buildCustomGap.txt
create mode 100644 tdesign-component/example/assets/code/watermark._buildCustomRotate.txt
create mode 100644 tdesign-component/example/assets/code/watermark._buildCustomSize.txt
create mode 100644 tdesign-component/example/assets/code/watermark._buildFormWatermark.txt
create mode 100644 tdesign-component/example/assets/code/watermark._buildGridLayout.txt
create mode 100644 tdesign-component/example/assets/code/watermark._buildHorizontalLayout.txt
create mode 100644 tdesign-component/example/assets/code/watermark._buildImageWatermark.txt
create mode 100644 tdesign-component/example/assets/code/watermark._buildListWatermark.txt
create mode 100644 tdesign-component/example/assets/code/watermark._buildMultiLine.txt
create mode 100644 tdesign-component/example/assets/code/watermark._buildSingleLine.txt
create mode 100644 tdesign-component/example/assets/code/watermark._buildVerticalLayout.txt
create mode 100644 tdesign-site/src/watermark/README.md
diff --git a/tdesign-component/example/assets/code/watermark._buildCustomColor.txt b/tdesign-component/example/assets/code/watermark._buildCustomColor.txt
new file mode 100644
index 000000000..4c5d21928
--- /dev/null
+++ b/tdesign-component/example/assets/code/watermark._buildCustomColor.txt
@@ -0,0 +1,15 @@
+
+ Widget _buildCustomColor(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '保密文档',
+ textColor: TTheme.of(context).errorNormalColor,
+ opacity: 0.2,
+ ),
+ );
+ }
\ No newline at end of file
diff --git a/tdesign-component/example/assets/code/watermark._buildCustomGap.txt b/tdesign-component/example/assets/code/watermark._buildCustomGap.txt
new file mode 100644
index 000000000..c691d99ab
--- /dev/null
+++ b/tdesign-component/example/assets/code/watermark._buildCustomGap.txt
@@ -0,0 +1,16 @@
+
+ Widget _buildCustomGap(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '密集',
+ gapX: 60,
+ gapY: 40,
+ textSize: 12,
+ ),
+ );
+ }
\ No newline at end of file
diff --git a/tdesign-component/example/assets/code/watermark._buildCustomRotate.txt b/tdesign-component/example/assets/code/watermark._buildCustomRotate.txt
new file mode 100644
index 000000000..7495053be
--- /dev/null
+++ b/tdesign-component/example/assets/code/watermark._buildCustomRotate.txt
@@ -0,0 +1,16 @@
+
+ Widget _buildCustomRotate(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '旋转45度',
+ rotate: -45,
+ gapX: 150,
+ gapY: 100,
+ ),
+ );
+ }
\ No newline at end of file
diff --git a/tdesign-component/example/assets/code/watermark._buildCustomSize.txt b/tdesign-component/example/assets/code/watermark._buildCustomSize.txt
new file mode 100644
index 000000000..ea90ae0b2
--- /dev/null
+++ b/tdesign-component/example/assets/code/watermark._buildCustomSize.txt
@@ -0,0 +1,15 @@
+
+ Widget _buildCustomSize(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '大字体水印',
+ textSize: 24,
+ fontWeight: FontWeight.bold,
+ ),
+ );
+ }
\ No newline at end of file
diff --git a/tdesign-component/example/assets/code/watermark._buildFormWatermark.txt b/tdesign-component/example/assets/code/watermark._buildFormWatermark.txt
new file mode 100644
index 000000000..671c6b878
--- /dev/null
+++ b/tdesign-component/example/assets/code/watermark._buildFormWatermark.txt
@@ -0,0 +1,42 @@
+
+ Widget _buildFormWatermark(BuildContext context) {
+ return Container(
+ height: 350,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '草稿',
+ opacity: 0.08,
+ textSize: 48,
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ children: [
+ TInput(
+ leftLabel: '姓名',
+ hintText: '请输入姓名',
+ ),
+ const SizedBox(height: 16),
+ TInput(
+ leftLabel: '邮箱',
+ hintText: '请输入邮箱',
+ ),
+ const SizedBox(height: 16),
+ TInput(
+ leftLabel: '电话',
+ hintText: '请输入电话号码',
+ ),
+ const SizedBox(height: 16),
+ TButton(
+ text: '提交',
+ theme: TButtonTheme.primary,
+ onTap: () {},
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
\ No newline at end of file
diff --git a/tdesign-component/example/assets/code/watermark._buildGridLayout.txt b/tdesign-component/example/assets/code/watermark._buildGridLayout.txt
new file mode 100644
index 000000000..29d73f993
--- /dev/null
+++ b/tdesign-component/example/assets/code/watermark._buildGridLayout.txt
@@ -0,0 +1,16 @@
+
+ Widget _buildGridLayout(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: 'TDesign',
+ layout: TWatermarkLayout.grid,
+ gapX: 120,
+ gapY: 80,
+ ),
+ );
+ }
\ No newline at end of file
diff --git a/tdesign-component/example/assets/code/watermark._buildHorizontalLayout.txt b/tdesign-component/example/assets/code/watermark._buildHorizontalLayout.txt
new file mode 100644
index 000000000..3590882de
--- /dev/null
+++ b/tdesign-component/example/assets/code/watermark._buildHorizontalLayout.txt
@@ -0,0 +1,15 @@
+
+ Widget _buildHorizontalLayout(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '内部资料',
+ layout: TWatermarkLayout.horizontal,
+ gapX: 150,
+ ),
+ );
+ }
\ No newline at end of file
diff --git a/tdesign-component/example/assets/code/watermark._buildImageWatermark.txt b/tdesign-component/example/assets/code/watermark._buildImageWatermark.txt
new file mode 100644
index 000000000..3bc7983b4
--- /dev/null
+++ b/tdesign-component/example/assets/code/watermark._buildImageWatermark.txt
@@ -0,0 +1,28 @@
+
+ Widget _buildImageWatermark(BuildContext context) {
+ return Container(
+ height: 300,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '仅供查看',
+ child: Center(
+ child: Container(
+ width: 200,
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).brandFocusColor,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: Icon(
+ TIcons.image,
+ size: 80,
+ color: TTheme.of(context).brandNormalColor,
+ ),
+ ),
+ ),
+ ),
+ );
+ }
\ No newline at end of file
diff --git a/tdesign-component/example/assets/code/watermark._buildListWatermark.txt b/tdesign-component/example/assets/code/watermark._buildListWatermark.txt
new file mode 100644
index 000000000..eb9bd3c01
--- /dev/null
+++ b/tdesign-component/example/assets/code/watermark._buildListWatermark.txt
@@ -0,0 +1,56 @@
+
+ Widget _buildListWatermark(BuildContext context) {
+ return Container(
+ height: 300,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '内部数据',
+ opacity: 0.1,
+ child: ListView.builder(
+ itemCount: 10,
+ itemBuilder: (context, index) {
+ return Container(
+ padding: const EdgeInsets.all(16),
+ decoration: BoxDecoration(
+ border: Border(
+ bottom: BorderSide(
+ color: TTheme.of(context).componentStrokeColor,
+ width: 0.5,
+ ),
+ ),
+ ),
+ child: Row(
+ children: [
+ CircleAvatar(
+ backgroundColor: TTheme.of(context).brandLightColor,
+ child: Text('${index + 1}'),
+ ),
+ const SizedBox(width: 12),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ TText(
+ '列表项 ${index + 1}',
+ font: TTheme.of(context).fontBodyMedium,
+ ),
+ const SizedBox(height: 4),
+ TText(
+ '这是第 ${index + 1} 条数据的描述信息',
+ font: TTheme.of(context).fontBodySmall,
+ textColor: TTheme.of(context).textColorSecondary,
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ },
+ ),
+ ),
+ );
+ }
\ No newline at end of file
diff --git a/tdesign-component/example/assets/code/watermark._buildMultiLine.txt b/tdesign-component/example/assets/code/watermark._buildMultiLine.txt
new file mode 100644
index 000000000..3cb8b6418
--- /dev/null
+++ b/tdesign-component/example/assets/code/watermark._buildMultiLine.txt
@@ -0,0 +1,14 @@
+
+ Widget _buildMultiLine(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: 'TDesign\nFlutter',
+ type: TWatermarkType.multiLine,
+ ),
+ );
+ }
\ No newline at end of file
diff --git a/tdesign-component/example/assets/code/watermark._buildSingleLine.txt b/tdesign-component/example/assets/code/watermark._buildSingleLine.txt
new file mode 100644
index 000000000..8f35774f9
--- /dev/null
+++ b/tdesign-component/example/assets/code/watermark._buildSingleLine.txt
@@ -0,0 +1,14 @@
+
+ Widget _buildSingleLine(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: 'TDesign Flutter',
+ type: TWatermarkType.singleLine,
+ ),
+ );
+ }
\ No newline at end of file
diff --git a/tdesign-component/example/assets/code/watermark._buildVerticalLayout.txt b/tdesign-component/example/assets/code/watermark._buildVerticalLayout.txt
new file mode 100644
index 000000000..12b8962ad
--- /dev/null
+++ b/tdesign-component/example/assets/code/watermark._buildVerticalLayout.txt
@@ -0,0 +1,15 @@
+
+ Widget _buildVerticalLayout(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '机密',
+ layout: TWatermarkLayout.vertical,
+ gapY: 80,
+ ),
+ );
+ }
\ No newline at end of file
diff --git a/tdesign-site/src/watermark/README.md b/tdesign-site/src/watermark/README.md
new file mode 100644
index 000000000..be16828f3
--- /dev/null
+++ b/tdesign-site/src/watermark/README.md
@@ -0,0 +1,390 @@
+---
+title: Watermark 水印
+description:
+spline: base
+isComponent: true
+---
+
+


+## 引入
+
+在tdesign_flutter/tdesign_flutter.dart中有所有组件的路径。
+
+```dart
+import 'package:tdesign_flutter/tdesign_flutter.dart';
+```
+
+## 代码演示
+
+[td_watermark_page.dart](https://github.com/Tencent/tdesign-flutter/blob/main/tdesign-component/example/lib/page/td_watermark_page.dart)
+
+### 1 基础用法
+
+单行文本水印:
+
+
+
+
+ Widget _buildSingleLine(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: 'TDesign Flutter',
+ type: TWatermarkType.singleLine,
+ ),
+ );
+ }
+
+
+
+
+多行文本水印:
+
+
+
+
+ Widget _buildMultiLine(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: 'TDesign\nFlutter',
+ type: TWatermarkType.multiLine,
+ ),
+ );
+ }
+
+
+
+### 1 排列方式
+
+水平排列:
+
+
+
+
+ Widget _buildHorizontalLayout(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '内部资料',
+ layout: TWatermarkLayout.horizontal,
+ gapX: 150,
+ ),
+ );
+ }
+
+
+
+
+垂直排列:
+
+
+
+
+ Widget _buildVerticalLayout(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '机密',
+ layout: TWatermarkLayout.vertical,
+ gapY: 80,
+ ),
+ );
+ }
+
+
+
+
+网格排列:
+
+
+
+
+ Widget _buildGridLayout(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: 'TDesign',
+ layout: TWatermarkLayout.grid,
+ gapX: 120,
+ gapY: 80,
+ ),
+ );
+ }
+
+
+
+### 1 自定义样式
+
+自定义颜色和透明度:
+
+
+
+
+ Widget _buildCustomColor(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '保密文档',
+ textColor: TTheme.of(context).errorNormalColor,
+ opacity: 0.2,
+ ),
+ );
+ }
+
+
+
+
+自定义字体大小:
+
+
+
+
+ Widget _buildCustomSize(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '大字体水印',
+ textSize: 24,
+ fontWeight: FontWeight.bold,
+ ),
+ );
+ }
+
+
+
+
+自定义旋转角度:
+
+
+
+
+ Widget _buildCustomRotate(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '旋转45度',
+ rotate: -45,
+ gapX: 150,
+ gapY: 100,
+ ),
+ );
+ }
+
+
+
+
+自定义间距:
+
+
+
+
+ Widget _buildCustomGap(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '密集',
+ gapX: 60,
+ gapY: 40,
+ textSize: 12,
+ ),
+ );
+ }
+
+
+
+### 1 带内容的水印
+
+图片上的水印:
+
+
+
+
+ Widget _buildImageWatermark(BuildContext context) {
+ return Container(
+ height: 300,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '仅供查看',
+ child: Center(
+ child: Container(
+ width: 200,
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).brandFocusColor,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: Icon(
+ TIcons.image,
+ size: 80,
+ color: TTheme.of(context).brandNormalColor,
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+
+
+
+列表上的水印:
+
+
+
+
+ Widget _buildListWatermark(BuildContext context) {
+ return Container(
+ height: 300,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '内部数据',
+ opacity: 0.1,
+ child: ListView.builder(
+ itemCount: 10,
+ itemBuilder: (context, index) {
+ return Container(
+ padding: const EdgeInsets.all(16),
+ decoration: BoxDecoration(
+ border: Border(
+ bottom: BorderSide(
+ color: TTheme.of(context).componentStrokeColor,
+ width: 0.5,
+ ),
+ ),
+ ),
+ child: Row(
+ children: [
+ CircleAvatar(
+ backgroundColor: TTheme.of(context).brandLightColor,
+ child: Text('${index + 1}'),
+ ),
+ const SizedBox(width: 12),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ TText(
+ '列表项 ${index + 1}',
+ font: TTheme.of(context).fontBodyMedium,
+ ),
+ const SizedBox(height: 4),
+ TText(
+ '这是第 ${index + 1} 条数据的描述信息',
+ font: TTheme.of(context).fontBodySmall,
+ textColor: TTheme.of(context).textColorSecondary,
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ },
+ ),
+ ),
+ );
+ }
+
+
+
+
+表单上的水印:
+
+
+
+
+ Widget _buildFormWatermark(BuildContext context) {
+ return Container(
+ height: 350,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '草稿',
+ opacity: 0.08,
+ textSize: 48,
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ children: [
+ TInput(
+ leftLabel: '姓名',
+ hintText: '请输入姓名',
+ ),
+ const SizedBox(height: 16),
+ TInput(
+ leftLabel: '邮箱',
+ hintText: '请输入邮箱',
+ ),
+ const SizedBox(height: 16),
+ TInput(
+ leftLabel: '电话',
+ hintText: '请输入电话号码',
+ ),
+ const SizedBox(height: 16),
+ TButton(
+ text: '提交',
+ theme: TButtonTheme.primary,
+ onTap: () {},
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+
+
+
+
+
+## API
+
+暂无对应api
+
+
+
\ No newline at end of file
From 7393f16f463461ef016b83fe03a4bbfb1c45f8fa Mon Sep 17 00:00:00 2001
From: engineer
Date: Tue, 14 Apr 2026 21:33:59 +0800
Subject: [PATCH 3/4] =?UTF-8?q?feat(watermark):=20=E4=BC=98=E5=8C=96?=
=?UTF-8?q?=E6=B0=B4=E5=8D=B0=E7=BB=84=E4=BB=B6=EF=BC=8C=E6=B7=BB=E5=8A=A0?=
=?UTF-8?q?=E6=B0=B4=E5=8D=B0=E7=BB=84=E4=BB=B6=E6=96=87=E6=A1=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/components/watermark/t_watermark.dart | 23 ++-
tdesign-site/site/docs/getting-started.md | 1 +
tdesign-site/site/site.config.mjs | 7 +
tdesign-site/src/watermark/README.md | 193 ++++++++++++++++++
4 files changed, 221 insertions(+), 3 deletions(-)
create mode 100644 tdesign-site/src/watermark/README.md
diff --git a/tdesign-component/lib/src/components/watermark/t_watermark.dart b/tdesign-component/lib/src/components/watermark/t_watermark.dart
index ccd794109..ee804ad57 100644
--- a/tdesign-component/lib/src/components/watermark/t_watermark.dart
+++ b/tdesign-component/lib/src/components/watermark/t_watermark.dart
@@ -167,6 +167,9 @@ class _WatermarkPainter extends CustomPainter {
..color = textColor
..isAntiAlias = true;
+ // 裁剪到容器边界,确保水印不超出容器
+ canvas.clipRect(Rect.fromLTWH(0, 0, size.width, size.height));
+
final textPainter = TextPainter(
text: TextSpan(
text: text,
@@ -209,6 +212,11 @@ class _WatermarkPainter extends CustomPainter {
final centerY = size.height / 2 + offsetY;
var currentX = offsetX;
+ // 向左延伸绘制,确保旋转后能覆盖左边缘
+ while (currentX > -textWidth) {
+ currentX -= textWidth + gapX;
+ }
+
while (currentX < size.width) {
_drawTextWithRotation(
canvas,
@@ -230,6 +238,11 @@ class _WatermarkPainter extends CustomPainter {
final centerX = size.width / 2 + offsetX;
var currentY = offsetY;
+ // 向上延伸绘制,确保旋转后能覆盖上边缘
+ while (currentY > -textHeight) {
+ currentY -= textHeight + gapY;
+ }
+
while (currentY < size.height) {
_drawTextWithRotation(
canvas,
@@ -248,11 +261,15 @@ class _WatermarkPainter extends CustomPainter {
double textWidth,
double textHeight,
) {
- var currentY = -textHeight + offsetY;
+ // 从原点开始绘制,配合clipRect确保水印在容器内
+ var startY = offsetY;
+ var startX = offsetX;
+ // 绘制网格,向右下扩展到容器边界
+ // 扩展一些以确保旋转后的水印也能覆盖边缘
+ var currentY = startY;
while (currentY < size.height + textHeight) {
- var currentX = -textWidth + offsetX;
-
+ var currentX = startX;
while (currentX < size.width + textWidth) {
_drawTextWithRotation(
canvas,
diff --git a/tdesign-site/site/docs/getting-started.md b/tdesign-site/site/docs/getting-started.md
index 53de7e554..563519751 100644
--- a/tdesign-site/site/docs/getting-started.md
+++ b/tdesign-site/site/docs/getting-started.md
@@ -19,6 +19,7 @@
+[English](./README.md) | 简体中文
**TDesign Flutter** 是基于腾讯设计体系的跨平台 UI 组件库,使用 Flutter 框架开发,可快速构建美观、一致的移动端/Web 应用,提供丰富的预制组件和主题定制能力,支持 iOS、Android、Web 多端运行。
diff --git a/tdesign-site/site/site.config.mjs b/tdesign-site/site/site.config.mjs
index 75d15c202..6bb1b3b29 100644
--- a/tdesign-site/site/site.config.mjs
+++ b/tdesign-site/site/site.config.mjs
@@ -426,6 +426,13 @@ export default {
path: '/flutter/components/tag',
component: () => import('@/tag/README.md'),
},
+ {
+ title: 'Watermark 水印',
+ name: 'watermark',
+ meta: { docType: 'data' },
+ path: '/flutter/components/watermark',
+ component: () => import('@/watermark/README.md'),
+ },
],
},
{
diff --git a/tdesign-site/src/watermark/README.md b/tdesign-site/src/watermark/README.md
new file mode 100644
index 000000000..fd27323fa
--- /dev/null
+++ b/tdesign-site/src/watermark/README.md
@@ -0,0 +1,193 @@
+---
+title: Watermark 水印
+description: 给页面的某个区域加上水印,常用于防止信息截图泄露。
+spline: base
+isComponent: true
+---
+
+## 引入
+
+在tdesign_flutter/tdesign_flutter.dart中有所有组件的路径。
+
+```dart
+import 'package:tdesign_flutter/tdesign_flutter.dart';
+```
+
+## 代码演示
+
+### 基础水印
+
+默认使用多行文本和网格排列的水印。
+
+
+
+
+
+ Widget _buildBasicWatermark() {
+ return const TWatermark(
+ text: 'TDesign Watermark',
+ );
+ }
+
+
+
+
+### 单行水印
+
+使用单行文本类型的水印。
+
+
+
+
+
+ Widget _buildSingleLineWatermark() {
+ return const TWatermark(
+ text: 'TDesign Watermark',
+ type: TWatermarkType.singleLine,
+ );
+ }
+
+
+
+
+### 自定义样式
+
+自定义水印的颜色、大小、透明度和旋转角度。
+
+
+
+
+
+ Widget _buildCustomStyleWatermark(BuildContext context) {
+ return TWatermark(
+ text: '机密信息',
+ textColor: Colors.red,
+ textSize: 16,
+ opacity: 0.2,
+ rotate: -30,
+ fontWeight: FontWeight.bold,
+ );
+ }
+
+
+
+
+### 自定义间距
+
+调整水印之间的水平和垂直间距。
+
+
+
+
+
+ Widget _buildCustomGapWatermark() {
+ return const TWatermark(
+ text: 'TDesign Watermark',
+ gapX: 150,
+ gapY: 150,
+ );
+ }
+
+
+
+
+### 带内容的水印
+
+在子组件上方添加水印。
+
+
+
+
+
+ Widget _buildWatermarkWithContent(BuildContext context) {
+ return TWatermark(
+ text: '内部资料',
+ child: Container(
+ padding: const EdgeInsets.all(16),
+ color: TTheme.of(context).bgColorContainer,
+ child: Column(
+ children: [
+ Text('这是一段重要内容', style: TTheme.of(context).fontTitleLarge),
+ const SizedBox(height: 8),
+ Text('水印会覆盖在这段内容上方', style: TTheme.of(context).fontBodyMedium),
+ ],
+ ),
+ ),
+ );
+ }
+
+
+
+
+### 不同排列方式
+
+使用水平、垂直和网格排列。
+
+
+
+
+
+ Widget _buildHorizontalWatermark() {
+ return const TWatermark(
+ text: 'TDesign Watermark',
+ layout: TWatermarkLayout.horizontal,
+ );
+ }
+
+ Widget _buildVerticalWatermark() {
+ return const TWatermark(
+ text: 'TDesign Watermark',
+ layout: TWatermarkLayout.vertical,
+ );
+ }
+
+ Widget _buildGridWatermark() {
+ return const TWatermark(
+ text: 'TDesign Watermark',
+ layout: TWatermarkLayout.grid,
+ );
+ }
+
+
+
+
+## API
+### TWatermark
+#### 默认构造方法
+
+| 参数 | 类型 | 默认值 | 说明 |
+| --- | --- | --- | --- |
+| text | String | required | 水印文本内容 |
+| type | TWatermarkType | TWatermarkType.multiLine | 水印类型(单行/多行) |
+| layout | TWatermarkLayout | TWatermarkLayout.grid | 水印排列方式(水平/垂直/网格) |
+| textColor | Color? | theme.textColorPlaceholder | 水印文字颜色 |
+| textSize | double | 14 | 水印文字大小 |
+| fontWeight | FontWeight | FontWeight.normal | 水印文字粗细 |
+| opacity | double | 0.15 | 水印透明度 (0.0 - 1.0) |
+| rotate | double | -20 | 水印旋转角度(度) |
+| gapX | double | 100 | 水平间距 |
+| gapY | double | 100 | 垂直间距 |
+| offsetX | double | 0 | 水平偏移量 |
+| offsetY | double | 0 | 垂直偏移量 |
+| zIndex | int | 1 | z-index层级 |
+| width | double? | null | 水印区域宽度 |
+| height | double? | null | 水印区域高度 |
+| child | Widget? | null | 子组件(水印将覆盖在此组件上方) |
+
+### TWatermarkType
+水印类型枚举
+
+| 值 | 说明 |
+| --- | --- |
+| singleLine | 单行文本 |
+| multiLine | 多行文本 |
+
+### TWatermarkLayout
+水印排列方式枚举
+
+| 值 | 说明 |
+| --- | --- |
+| horizontal | 水平排列 |
+| vertical | 垂直排列 |
+| grid | 网格排列 |
+
From c626d5886d44ba015e1c4c8dcc69103ecd4d50cb Mon Sep 17 00:00:00 2001
From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com>
Date: Tue, 14 Apr 2026 13:44:36 +0000
Subject: [PATCH 4/4] [autofix.ci] apply automated fixes
---
tdesign-site/site/docs/getting-started.md | 1 -
tdesign-site/src/watermark/README.md | 417 ++++++++++++++++------
2 files changed, 307 insertions(+), 111 deletions(-)
diff --git a/tdesign-site/site/docs/getting-started.md b/tdesign-site/site/docs/getting-started.md
index 563519751..53de7e554 100644
--- a/tdesign-site/site/docs/getting-started.md
+++ b/tdesign-site/site/docs/getting-started.md
@@ -19,7 +19,6 @@
-[English](./README.md) | 简体中文
**TDesign Flutter** 是基于腾讯设计体系的跨平台 UI 组件库,使用 Flutter 框架开发,可快速构建美观、一致的移动端/Web 应用,提供丰富的预制组件和主题定制能力,支持 iOS、Android、Web 多端运行。
diff --git a/tdesign-site/src/watermark/README.md b/tdesign-site/src/watermark/README.md
index fd27323fa..be16828f3 100644
--- a/tdesign-site/src/watermark/README.md
+++ b/tdesign-site/src/watermark/README.md
@@ -1,10 +1,11 @@
---
title: Watermark 水印
-description: 给页面的某个区域加上水印,常用于防止信息截图泄露。
+description:
spline: base
isComponent: true
---
+


## 引入
在tdesign_flutter/tdesign_flutter.dart中有所有组件的路径。
@@ -15,179 +16,375 @@ import 'package:tdesign_flutter/tdesign_flutter.dart';
## 代码演示
-### 基础水印
+[td_watermark_page.dart](https://github.com/Tencent/tdesign-flutter/blob/main/tdesign-component/example/lib/page/td_watermark_page.dart)
-默认使用多行文本和网格排列的水印。
+### 1 基础用法
-
+单行文本水印:
+
- Widget _buildBasicWatermark() {
- return const TWatermark(
- text: 'TDesign Watermark',
+ Widget _buildSingleLine(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: 'TDesign Flutter',
+ type: TWatermarkType.singleLine,
+ ),
);
}
-
+
+
+多行文本水印:
+
+
-### 单行水印
+
+ Widget _buildMultiLine(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: 'TDesign\nFlutter',
+ type: TWatermarkType.multiLine,
+ ),
+ );
+ }
-使用单行文本类型的水印。
+
+
+### 1 排列方式
-
+水平排列:
+
- Widget _buildSingleLineWatermark() {
- return const TWatermark(
- text: 'TDesign Watermark',
- type: TWatermarkType.singleLine,
+ Widget _buildHorizontalLayout(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '内部资料',
+ layout: TWatermarkLayout.horizontal,
+ gapX: 150,
+ ),
);
}
-
+
-### 自定义样式
+垂直排列:
+
+
-自定义水印的颜色、大小、透明度和旋转角度。
+
+ Widget _buildVerticalLayout(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '机密',
+ layout: TWatermarkLayout.vertical,
+ gapY: 80,
+ ),
+ );
+ }
-
+
+
+
+网格排列:
+
- Widget _buildCustomStyleWatermark(BuildContext context) {
- return TWatermark(
- text: '机密信息',
- textColor: Colors.red,
- textSize: 16,
- opacity: 0.2,
- rotate: -30,
- fontWeight: FontWeight.bold,
+ Widget _buildGridLayout(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: 'TDesign',
+ layout: TWatermarkLayout.grid,
+ gapX: 120,
+ gapY: 80,
+ ),
);
}
-
+
+### 1 自定义样式
-### 自定义间距
+自定义颜色和透明度:
+
+
-调整水印之间的水平和垂直间距。
+
+ Widget _buildCustomColor(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '保密文档',
+ textColor: TTheme.of(context).errorNormalColor,
+ opacity: 0.2,
+ ),
+ );
+ }
-
+
+
+
+自定义字体大小:
+
- Widget _buildCustomGapWatermark() {
- return const TWatermark(
- text: 'TDesign Watermark',
- gapX: 150,
- gapY: 150,
+ Widget _buildCustomSize(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '大字体水印',
+ textSize: 24,
+ fontWeight: FontWeight.bold,
+ ),
);
}
-
+
-### 带内容的水印
+自定义旋转角度:
+
+
+
+
+ Widget _buildCustomRotate(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '旋转45度',
+ rotate: -45,
+ gapX: 150,
+ gapY: 100,
+ ),
+ );
+ }
-在子组件上方添加水印。
+
+
-
+自定义间距:
+
- Widget _buildWatermarkWithContent(BuildContext context) {
- return TWatermark(
- text: '内部资料',
- child: Container(
- padding: const EdgeInsets.all(16),
+ Widget _buildCustomGap(BuildContext context) {
+ return Container(
+ height: 200,
+ decoration: BoxDecoration(
color: TTheme.of(context).bgColorContainer,
- child: Column(
- children: [
- Text('这是一段重要内容', style: TTheme.of(context).fontTitleLarge),
- const SizedBox(height: 8),
- Text('水印会覆盖在这段内容上方', style: TTheme.of(context).fontBodyMedium),
- ],
- ),
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '密集',
+ gapX: 60,
+ gapY: 40,
+ textSize: 12,
),
);
}
-
+
+### 1 带内容的水印
+
+图片上的水印:
+
+
-### 不同排列方式
+
+ Widget _buildImageWatermark(BuildContext context) {
+ return Container(
+ height: 300,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '仅供查看',
+ child: Center(
+ child: Container(
+ width: 200,
+ height: 200,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).brandFocusColor,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: Icon(
+ TIcons.image,
+ size: 80,
+ color: TTheme.of(context).brandNormalColor,
+ ),
+ ),
+ ),
+ ),
+ );
+ }
-使用水平、垂直和网格排列。
+
+
-
+列表上的水印:
+
- Widget _buildHorizontalWatermark() {
- return const TWatermark(
- text: 'TDesign Watermark',
- layout: TWatermarkLayout.horizontal,
+ Widget _buildListWatermark(BuildContext context) {
+ return Container(
+ height: 300,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '内部数据',
+ opacity: 0.1,
+ child: ListView.builder(
+ itemCount: 10,
+ itemBuilder: (context, index) {
+ return Container(
+ padding: const EdgeInsets.all(16),
+ decoration: BoxDecoration(
+ border: Border(
+ bottom: BorderSide(
+ color: TTheme.of(context).componentStrokeColor,
+ width: 0.5,
+ ),
+ ),
+ ),
+ child: Row(
+ children: [
+ CircleAvatar(
+ backgroundColor: TTheme.of(context).brandLightColor,
+ child: Text('${index + 1}'),
+ ),
+ const SizedBox(width: 12),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ TText(
+ '列表项 ${index + 1}',
+ font: TTheme.of(context).fontBodyMedium,
+ ),
+ const SizedBox(height: 4),
+ TText(
+ '这是第 ${index + 1} 条数据的描述信息',
+ font: TTheme.of(context).fontBodySmall,
+ textColor: TTheme.of(context).textColorSecondary,
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ },
+ ),
+ ),
);
- }
+ }
- Widget _buildVerticalWatermark() {
- return const TWatermark(
- text: 'TDesign Watermark',
- layout: TWatermarkLayout.vertical,
- );
- }
+
+
- Widget _buildGridWatermark() {
- return const TWatermark(
- text: 'TDesign Watermark',
- layout: TWatermarkLayout.grid,
+表单上的水印:
+
+
+
+
+ Widget _buildFormWatermark(BuildContext context) {
+ return Container(
+ height: 350,
+ decoration: BoxDecoration(
+ color: TTheme.of(context).bgColorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TWatermark(
+ text: '草稿',
+ opacity: 0.08,
+ textSize: 48,
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ children: [
+ TInput(
+ leftLabel: '姓名',
+ hintText: '请输入姓名',
+ ),
+ const SizedBox(height: 16),
+ TInput(
+ leftLabel: '邮箱',
+ hintText: '请输入邮箱',
+ ),
+ const SizedBox(height: 16),
+ TInput(
+ leftLabel: '电话',
+ hintText: '请输入电话号码',
+ ),
+ const SizedBox(height: 16),
+ TButton(
+ text: '提交',
+ theme: TButtonTheme.primary,
+ onTap: () {},
+ ),
+ ],
+ ),
+ ),
+ ),
);
}
-
+
+
## API
-### TWatermark
-#### 默认构造方法
-
-| 参数 | 类型 | 默认值 | 说明 |
-| --- | --- | --- | --- |
-| text | String | required | 水印文本内容 |
-| type | TWatermarkType | TWatermarkType.multiLine | 水印类型(单行/多行) |
-| layout | TWatermarkLayout | TWatermarkLayout.grid | 水印排列方式(水平/垂直/网格) |
-| textColor | Color? | theme.textColorPlaceholder | 水印文字颜色 |
-| textSize | double | 14 | 水印文字大小 |
-| fontWeight | FontWeight | FontWeight.normal | 水印文字粗细 |
-| opacity | double | 0.15 | 水印透明度 (0.0 - 1.0) |
-| rotate | double | -20 | 水印旋转角度(度) |
-| gapX | double | 100 | 水平间距 |
-| gapY | double | 100 | 垂直间距 |
-| offsetX | double | 0 | 水平偏移量 |
-| offsetY | double | 0 | 垂直偏移量 |
-| zIndex | int | 1 | z-index层级 |
-| width | double? | null | 水印区域宽度 |
-| height | double? | null | 水印区域高度 |
-| child | Widget? | null | 子组件(水印将覆盖在此组件上方) |
-
-### TWatermarkType
-水印类型枚举
-
-| 值 | 说明 |
-| --- | --- |
-| singleLine | 单行文本 |
-| multiLine | 多行文本 |
-
-### TWatermarkLayout
-水印排列方式枚举
-
-| 值 | 说明 |
-| --- | --- |
-| horizontal | 水平排列 |
-| vertical | 垂直排列 |
-| grid | 网格排列 |
+暂无对应api
+
+
+
\ No newline at end of file