进阶实战 Flutter for OpenHarmony:ShaderMask 着色器系统 - 高级视觉效果实现

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net


一、ShaderMask 系统架构深度解析

在现代移动应用中,视觉效果是提升用户体验的关键因素。ShaderMask 作为 Flutter 中强大的着色工具,能够为任意组件添加渐变、纹理和混合效果,创造出丰富多彩的视觉呈现。

📱 1.1 ShaderMask 核心概念

ShaderMask 是一个能够使用着色器(Shader)来修改子组件绘制效果的 Widget。它通过将着色器应用到子组件的像素上,实现各种视觉效果。

ShaderMask 工作原理:

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                    ShaderMask 工作流程                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐         │
│  │  子组件绘制  │ -> │  着色器应用  │ -> │  混合模式   │         │
│  │  (Child)    │    │  (Shader)   │    │  (BlendMode)│         │
│  └─────────────┘    └─────────────┘    └─────────────┘         │
│         │                  │                  │                 │
│         ▼                  ▼                  ▼                 │
│  ┌─────────────────────────────────────────────────────┐       │
│  │              原始像素 + 着色器颜色 = 最终效果         │       │
│  └─────────────────────────────────────────────────────┘       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

核心属性详解:

属性 类型 说明 应用场景
shaderCallback ShaderCallback 着色器回调函数 定义渐变、纹理等着色效果
blendMode BlendMode 混合模式 控制着色器与原图的混合方式
child Widget 子组件 需要应用着色效果的组件

🔬 1.2 着色器类型详解

Flutter 提供了多种着色器类型,每种都有其独特的视觉效果:

1. LinearGradient(线性渐变)

线性渐变沿直线方向进行颜色过渡,是最常用的渐变类型。

dart 复制代码
LinearGradient(
  begin: Alignment.topLeft,      // 起始位置
  end: Alignment.bottomRight,    // 结束位置
  colors: [Colors.red, Colors.blue],  // 颜色列表
  stops: [0.0, 1.0],             // 颜色停止点
  tileMode: TileMode.clamp,      // 平铺模式
)

2. RadialGradient(径向渐变)

径向渐变从中心点向外辐射,产生圆形或椭圆形的颜色过渡效果。

dart 复制代码
RadialGradient(
  center: Alignment.center,      // 中心点
  radius: 0.5,                   // 半径
  colors: [Colors.yellow, Colors.orange, Colors.red],
  stops: [0.0, 0.5, 1.0],
)

3. SweepGradient(扫描渐变)

扫描渐变沿圆周方向进行颜色过渡,常用于创建进度环、指南针等效果。

dart 复制代码
SweepGradient(
  center: Alignment.center,      // 中心点
  startAngle: 0.0,               // 起始角度
  endAngle: math.pi * 2,         // 结束角度
  colors: [Colors.red, Colors.yellow, Colors.green, Colors.blue],
)

🎯 1.3 混合模式详解

混合模式决定了着色器颜色如何与原始像素进行混合。Flutter 提供了丰富的混合模式选项:

常用混合模式分类:

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                       混合模式分类                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  基础混合模式:                                                  │
│  ├── srcIn        - 仅显示着色器颜色(遮罩效果)                  │
│  ├── srcATop      - 着色器覆盖在原图上方                         │
│  ├── dstIn        - 仅显示原图(受着色器形状限制)               │
│  └── dstATop      - 原图覆盖在着色器上方                         │
│                                                                 │
│  颜色混合模式:                                                  │
│  ├── modulate     - 颜色相乘(变暗效果)                         │
│  ├── screen       - 屏幕混合(变亮效果)                         │
│  ├── overlay      - 叠加混合(增强对比)                         │
│  └── hardlight    - 强光效果                                    │
│                                                                 │
│  特殊效果模式:                                                  │
│  ├── difference   - 差值混合(反色效果)                         │
│  ├── exclusion    - 排除混合                                    │
│  ├── hue          - 色相混合                                    │
│  └── saturation   - 饱和度混合                                  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

二、基础着色效果实现

👆 2.1 文字渐变效果

文字渐变是最常见的着色效果之一,通过 ShaderMask 可以为文字添加丰富的渐变色彩。

文字渐变实现原理:

复制代码
原始文字(纯色)          ShaderMask应用后(渐变)
    ┌─────────┐              ┌─────────┐
    │  Hello  │    ──────>   │  Hello  │
    │  World  │              │  World  │
    └─────────┘              └─────────┘
    单一颜色                  红→橙→黄→绿→蓝
dart 复制代码
/// 渐变文字组件
class GradientText extends StatelessWidget {
  final String text;
  final TextStyle? style;
  final List<Color> colors;
  final Alignment begin;
  final Alignment end;

  const GradientText({
    super.key,
    required this.text,
    this.style,
    required this.colors,
    this.begin = Alignment.topLeft,
    this.end = Alignment.bottomRight,
  });

  @override
  Widget build(BuildContext context) {
    return ShaderMask(
      shaderCallback: (bounds) => LinearGradient(
        begin: begin,
        end: end,
        colors: colors,
      ).createShader(bounds),
      blendMode: BlendMode.srcIn,
      child: Text(
        text,
        style: (style ?? const TextStyle()).copyWith(color: Colors.white),
      ),
    );
  }
}

/// 使用示例
class GradientTextDemo extends StatelessWidget {
  const GradientTextDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('渐变文字效果')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            GradientText(
              text: 'Flutter',
              style: const TextStyle(fontSize: 48, fontWeight: FontWeight.bold),
              colors: [Colors.purple, Colors.blue, Colors.cyan],
            ),
            const SizedBox(height: 20),
            GradientText(
              text: 'OpenHarmony',
              style: const TextStyle(fontSize: 36, fontWeight: FontWeight.w300),
              colors: [Colors.orange, Colors.red, Colors.pink],
              begin: Alignment.centerLeft,
              end: Alignment.centerRight,
            ),
            const SizedBox(height: 20),
            GradientText(
              text: '跨平台开发',
              style: const TextStyle(fontSize: 32),
              colors: [Colors.green, Colors.teal, Colors.blue],
              begin: Alignment.topCenter,
              end: Alignment.bottomCenter,
            ),
          ],
        ),
      ),
    );
  }
}

🔧 2.2 图标渐变效果

图标渐变可以为应用图标、按钮图标等添加视觉吸引力。

dart 复制代码
/// 渐变图标组件
class GradientIcon extends StatelessWidget {
  final IconData icon;
  final double size;
  final List<Color> colors;

  const GradientIcon({
    super.key,
    required this.icon,
    this.size = 24,
    required this.colors,
  });

  @override
  Widget build(BuildContext context) {
    return ShaderMask(
      shaderCallback: (bounds) => LinearGradient(
        colors: colors,
      ).createShader(bounds),
      blendMode: BlendMode.srcIn,
      child: Icon(icon, size: size, color: Colors.white),
    );
  }
}

/// 图标渐变演示
class GradientIconDemo extends StatelessWidget {
  const GradientIconDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('渐变图标效果')),
      body: GridView.count(
        padding: const EdgeInsets.all(16),
        crossAxisCount: 4,
        mainAxisSpacing: 20,
        crossAxisSpacing: 20,
        children: [
          _buildIconCard(Icons.home, [Colors.blue, Colors.purple]),
          _buildIconCard(Icons.favorite, [Colors.pink, Colors.red]),
          _buildIconCard(Icons.star, [Colors.orange, Colors.yellow]),
          _buildIconCard(Icons.settings, [Colors.teal, Colors.green]),
          _buildIconCard(Icons.person, [Colors.indigo, Colors.blue]),
          _buildIconCard(Icons.shopping_cart, [Colors.amber, Colors.orange]),
          _buildIconCard(Icons.notifications, [Colors.red, Colors.pink]),
          _buildIconCard(Icons.search, [Colors.cyan, Colors.blue]),
        ],
      ),
    );
  }

  Widget _buildIconCard(IconData icon, List<Color> colors) {
    return Container(
      decoration: BoxDecoration(
        color: Colors.grey.shade100,
        borderRadius: BorderRadius.circular(16),
      ),
      child: Center(
        child: GradientIcon(icon: icon, size: 40, colors: colors),
      ),
    );
  }
}

🎨 2.3 图片着色效果

ShaderMask 可以为图片添加各种滤镜效果,如渐变遮罩、颜色叠加等。

dart 复制代码
/// 图片着色效果演示
class ImageShaderDemo extends StatelessWidget {
  const ImageShaderDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('图片着色效果')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          _buildShaderImage(
            '原图',
            null,
            BlendMode.clear,
          ),
          _buildShaderImage(
            '渐变叠加',
            LinearGradient(
              colors: [Colors.purple.withOpacity(0.5), Colors.blue.withOpacity(0.5)],
            ),
            BlendMode.srcATop,
          ),
          _buildShaderImage(
            '径向渐变',
            RadialGradient(
              colors: [Colors.transparent, Colors.black.withOpacity(0.7)],
            ),
            BlendMode.srcOver,
          ),
          _buildShaderImage(
            '颜色滤镜',
            const LinearGradient(
              colors: [Colors.orange, Colors.deepOrange],
            ),
            BlendMode.modulate,
          ),
        ],
      ),
    );
  }

  Widget _buildShaderImage(String title, Shader? shader, BlendMode blendMode) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
        const SizedBox(height: 8),
        ClipRRect(
          borderRadius: BorderRadius.circular(12),
          child: ShaderMask(
            shaderCallback: shader != null 
                ? (bounds) => shader.createShader(bounds)
                : (bounds) => Paint().shader!,
            blendMode: blendMode,
            child: Container(
              height: 150,
              decoration: BoxDecoration(
                gradient: LinearGradient(
                  colors: [Colors.blue.shade300, Colors.purple.shade300],
                ),
              ),
              child: const Center(
                child: Icon(Icons.image, size: 60, color: Colors.white54),
              ),
            ),
          ),
        ),
        const SizedBox(height: 16),
      ],
    );
  }
}

三、高级着色效果实现

✨ 3.1 动态渐变效果

动态渐变通过动画控制器驱动渐变颜色的变化,产生流动的视觉效果。

动态渐变实现原理:

复制代码
时间轴动画:
┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│  t=0.0     t=0.25    t=0.5     t=0.75    t=1.0                  │
│    │         │         │         │         │                    │
│    ▼         ▼         ▼         ▼         ▼                    │
│  ┌───┐    ┌───┐    ┌───┐    ┌───┐    ┌───┐                    │
│  │红 │ -> │橙 │ -> │黄 │ -> │绿 │ -> │蓝 │                    │
│  │   │    │   │    │   │    │   │    │   │                    │
│  │蓝 │    │紫 │    │红 │    │橙 │    │绿 │                    │
│  └───┘    └───┘    └───┘    └───┘    └───┘                    │
│                                                                 │
│  颜色随时间平滑过渡,产生流动效果                                 │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
dart 复制代码
/// 动态渐变文字
class AnimatedGradientText extends StatefulWidget {
  final String text;
  final TextStyle? style;
  final List<List<Color>> colorSets;
  final Duration duration;

  const AnimatedGradientText({
    super.key,
    required this.text,
    this.style,
    required this.colorSets,
    this.duration = const Duration(seconds: 3),
  });

  @override
  State<AnimatedGradientText> createState() => _AnimatedGradientTextState();
}

class _AnimatedGradientTextState extends State<AnimatedGradientText>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: widget.duration,
      vsync: this,
    )..repeat();
    _animation = Tween<double>(begin: 0, end: 1).animate(_controller);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  List<Color> _lerpColors(List<Color> start, List<Color> end, double t) {
    return List.generate(
      start.length,
      (i) => Color.lerp(start[i], end[i], t) ?? start[i],
    );
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _animation,
      builder: (context, child) {
        final progress = _animation.value * (widget.colorSets.length - 1);
        final index = progress.floor();
        final nextIndex = (index + 1) % widget.colorSets.length;
        final t = progress - index;

        final colors = _lerpColors(
          widget.colorSets[index],
          widget.colorSets[nextIndex],
          t,
        );

        return ShaderMask(
          shaderCallback: (bounds) => LinearGradient(
            begin: Alignment.topLeft,
            end: Alignment.bottomRight,
            colors: colors,
          ).createShader(bounds),
          blendMode: BlendMode.srcIn,
          child: Text(
            widget.text,
            style: (widget.style ?? const TextStyle()).copyWith(color: Colors.white),
          ),
        );
      },
    );
  }
}

/// 动态渐变演示页面
class AnimatedGradientDemo extends StatelessWidget {
  const AnimatedGradientDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('动态渐变效果')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            AnimatedGradientText(
              text: 'Flutter',
              style: const TextStyle(fontSize: 48, fontWeight: FontWeight.bold),
              colorSets: [
                [Colors.purple, Colors.blue],
                [Colors.blue, Colors.cyan],
                [Colors.cyan, Colors.green],
                [Colors.green, Colors.yellow],
                [Colors.yellow, Colors.orange],
                [Colors.orange, Colors.red],
                [Colors.red, Colors.purple],
              ],
            ),
            const SizedBox(height: 40),
            AnimatedGradientText(
              text: 'OpenHarmony',
              style: const TextStyle(fontSize: 32),
              colorSets: [
                [Colors.pink, Colors.red],
                [Colors.red, Colors.orange],
                [Colors.orange, Colors.amber],
                [Colors.amber, Colors.pink],
              ],
              duration: const Duration(seconds: 2),
            ),
          ],
        ),
      ),
    );
  }
}

🌊 3.2 扫描渐变进度环

扫描渐变非常适合用于创建进度指示器、仪表盘等圆形视觉效果。

dart 复制代码
/// 扫描渐变进度环
class SweepGradientProgress extends StatefulWidget {
  final double progress;
  final double size;
  final double strokeWidth;
  final List<Color> colors;

  const SweepGradientProgress({
    super.key,
    required this.progress,
    this.size = 200,
    this.strokeWidth = 15,
    this.colors = const [Colors.blue, Colors.purple, Colors.pink, Colors.red],
  });

  @override
  State<SweepGradientProgress> createState() => _SweepGradientProgressState();
}

class _SweepGradientProgressState extends State<SweepGradientProgress>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 3),
      vsync: this,
    )..repeat();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (context, child) {
        return Transform.rotate(
          angle: _controller.value * 2 * math.pi,
          child: SizedBox(
            width: widget.size,
            height: widget.size,
            child: CustomPaint(
              painter: SweepProgressPainter(
                progress: widget.progress,
                strokeWidth: widget.strokeWidth,
                colors: widget.colors,
              ),
            ),
          ),
        );
      },
    );
  }
}

/// 扫描进度绘制器
class SweepProgressPainter extends CustomPainter {
  final double progress;
  final double strokeWidth;
  final List<Color> colors;

  SweepProgressPainter({
    required this.progress,
    required this.strokeWidth,
    required this.colors,
  });

  @override
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    final radius = (size.width - strokeWidth) / 2;

    // 绘制背景圆环
    final bgPaint = Paint()
      ..color = Colors.grey.shade200
      ..style = PaintingStyle.stroke
      ..strokeWidth = strokeWidth
      ..strokeCap = StrokeCap.round;
    canvas.drawCircle(center, radius, bgPaint);

    // 绘制渐变进度弧
    final rect = Rect.fromCircle(center: center, radius: radius);
    final gradient = SweepGradient(
      center: Alignment.center,
      colors: colors,
      stops: List.generate(colors.length, (i) => i / (colors.length - 1)),
    );

    final progressPaint = Paint()
      ..shader = gradient.createShader(rect)
      ..style = PaintingStyle.stroke
      ..strokeWidth = strokeWidth
      ..strokeCap = StrokeCap.round;

    final sweepAngle = 2 * math.pi * progress;
    canvas.drawArc(
      rect,
      -math.pi / 2,
      sweepAngle,
      false,
      progressPaint,
    );

    // 绘制中心文字
    final textPainter = TextPainter(
      text: TextSpan(
        text: '${(progress * 100).toInt()}%',
        style: TextStyle(
          fontSize: strokeWidth * 2,
          fontWeight: FontWeight.bold,
          color: colors.first,
        ),
      ),
      textDirection: TextDirection.ltr,
    );
    textPainter.layout();
    textPainter.paint(
      canvas,
      Offset(
        center.dx - textPainter.width / 2,
        center.dy - textPainter.height / 2,
      ),
    );
  }

  @override
  bool shouldRepaint(SweepProgressPainter oldDelegate) {
    return progress != oldDelegate.progress;
  }
}

/// 扫描渐变演示
class SweepGradientDemo extends StatefulWidget {
  const SweepGradientDemo({super.key});

  @override
  State<SweepGradientDemo> createState() => _SweepGradientDemoState();
}

class _SweepGradientDemoState extends State<SweepGradientDemo> {
  double _progress = 0.75;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('扫描渐变进度环')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            SweepGradientProgress(progress: _progress),
            const SizedBox(height: 40),
            Slider(
              value: _progress,
              onChanged: (value) => setState(() => _progress = value),
            ),
          ],
        ),
      ),
    );
  }
}

四、混合模式高级应用

🎪 4.1 混合模式效果展示

不同的混合模式会产生截然不同的视觉效果,理解每种模式的特点对于创建高级视觉效果至关重要。

dart 复制代码
/// 混合模式展示
class BlendModeShowcase extends StatelessWidget {
  const BlendModeShowcase({super.key});

  static const List<Map<String, dynamic>> blendModes = [
    {'name': 'srcIn', 'mode': BlendMode.srcIn, 'desc': '遮罩效果'},
    {'name': 'modulate', 'mode': BlendMode.modulate, 'desc': '颜色相乘'},
    {'name': 'screen', 'mode': BlendMode.screen, 'desc': '屏幕混合'},
    {'name': 'overlay', 'mode': BlendMode.overlay, 'desc': '叠加效果'},
    {'name': 'difference', 'mode': BlendMode.difference, 'desc': '差值混合'},
    {'name': 'hardLight', 'mode': BlendMode.hardLight, 'desc': '强光效果'},
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('混合模式展示')),
      body: GridView.builder(
        padding: const EdgeInsets.all(16),
        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
          mainAxisSpacing: 16,
          crossAxisSpacing: 16,
          childAspectRatio: 0.85,
        ),
        itemCount: blendModes.length,
        itemBuilder: (context, index) {
          final item = blendModes[index];
          return _buildBlendModeCard(
            item['name'],
            item['mode'],
            item['desc'],
          );
        },
      ),
    );
  }

  Widget _buildBlendModeCard(String name, BlendMode mode, String desc) {
    return Card(
      clipBehavior: Clip.antiAlias,
      child: Column(
        children: [
          Expanded(
            child: ShaderMask(
              shaderCallback: (bounds) => const LinearGradient(
                colors: [Colors.purple, Colors.pink, Colors.orange],
              ).createShader(bounds),
              blendMode: mode,
              child: Container(
                width: double.infinity,
                color: Colors.blue,
                child: const Center(
                  child: Text(
                    'Flutter',
                    style: TextStyle(
                      fontSize: 28,
                      fontWeight: FontWeight.bold,
                      color: Colors.white,
                    ),
                  ),
                ),
              ),
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8),
            child: Column(
              children: [
                Text(name, style: const TextStyle(fontWeight: FontWeight.bold)),
                Text(desc, style: TextStyle(fontSize: 12, color: Colors.grey[600])),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

🎭 4.2 文字遮罩效果

使用 BlendMode.srcIn 可以创建文字遮罩效果,让图片或渐变透过文字显示。

dart 复制代码
/// 文字遮罩效果
class TextMaskEffect extends StatelessWidget {
  const TextMaskEffect({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('文字遮罩效果')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ShaderMask(
              shaderCallback: (bounds) => ImageShader(
                NetworkImage('https://picsum.photos/400/200'),
                tileMode: TileMode.mirror,
              ).createShader(bounds),
              blendMode: BlendMode.srcIn,
              child: const Text(
                'FLUTTER',
                style: TextStyle(
                  fontSize: 64,
                  fontWeight: FontWeight.bold,
                  color: Colors.white,
                  letterSpacing: 8,
                ),
              ),
            ),
            const SizedBox(height: 40),
            ShaderMask(
              shaderCallback: (bounds) => const LinearGradient(
                colors: [Colors.red, Colors.orange, Colors.yellow, Colors.green, Colors.blue, Colors.purple],
              ).createShader(bounds),
              blendMode: BlendMode.srcIn,
              child: const Text(
                '彩虹文字',
                style: TextStyle(
                  fontSize: 48,
                  fontWeight: FontWeight.bold,
                  color: Colors.white,
                ),
              ),
            ),
            const SizedBox(height: 40),
            ShaderMask(
              shaderCallback: (bounds) => RadialGradient(
                center: Alignment.center,
                colors: [Colors.white, Colors.black],
              ).createShader(bounds),
              blendMode: BlendMode.srcIn,
              child: const Text(
                '径向渐变',
                style: TextStyle(
                  fontSize: 48,
                  fontWeight: FontWeight.bold,
                  color: Colors.white,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

五、综合应用案例

🎨 5.1 渐变按钮组件

结合 ShaderMask 创建具有渐变效果的按钮组件。

dart 复制代码
/// 渐变按钮组件
class GradientButton extends StatefulWidget {
  final String text;
  final VoidCallback onPressed;
  final List<Color> colors;
  final double borderRadius;

  const GradientButton({
    super.key,
    required this.text,
    required this.onPressed,
    required this.colors,
    this.borderRadius = 25,
  });

  @override
  State<GradientButton> createState() => _GradientButtonState();
}

class _GradientButtonState extends State<GradientButton>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;
  bool _isPressed = false;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(milliseconds: 1500),
      vsync: this,
    )..repeat();
    _animation = Tween<double>(begin: 0, end: 1).animate(_controller);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTapDown: (_) => setState(() => _isPressed = true),
      onTapUp: (_) {
        setState(() => _isPressed = false);
        widget.onPressed();
      },
      onTapCancel: () => setState(() => _isPressed = false),
      child: AnimatedScale(
        scale: _isPressed ? 0.95 : 1,
        duration: const Duration(milliseconds: 100),
        child: AnimatedBuilder(
          animation: _animation,
          builder: (context, child) {
            return Container(
              padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
              decoration: BoxDecoration(
                gradient: LinearGradient(
                  begin: Alignment(-1 + _animation.value * 2, 0),
                  end: Alignment(1 - _animation.value * 2, 0),
                  colors: widget.colors,
                ),
                borderRadius: BorderRadius.circular(widget.borderRadius),
                boxShadow: [
                  BoxShadow(
                    color: widget.colors.first.withOpacity(0.4),
                    blurRadius: 15,
                    offset: const Offset(0, 5),
                  ),
                ],
              ),
              child: Text(
                widget.text,
                style: const TextStyle(
                  color: Colors.white,
                  fontSize: 16,
                  fontWeight: FontWeight.bold,
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}

/// 渐变按钮演示
class GradientButtonDemo extends StatelessWidget {
  const GradientButtonDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('渐变按钮')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            GradientButton(
              text: '开始体验',
              onPressed: () => ScaffoldMessenger.of(context).showSnackBar(
                const SnackBar(content: Text('按钮被点击')),
              ),
              colors: [Colors.purple, Colors.blue],
            ),
            const SizedBox(height: 20),
            GradientButton(
              text: '立即登录',
              onPressed: () {},
              colors: [Colors.orange, Colors.red],
            ),
            const SizedBox(height: 20),
            GradientButton(
              text: '了解更多',
              onPressed: () {},
              colors: [Colors.teal, Colors.green],
            ),
          ],
        ),
      ),
    );
  }
}

⭐ 5.2 闪光效果组件

创建类似闪光扫过的视觉效果,常用于强调重要内容。

dart 复制代码
/// 闪光效果组件
class ShimmerEffect extends StatefulWidget {
  final Widget child;
  final Duration duration;
  final Color baseColor;
  final Color highlightColor;

  const ShimmerEffect({
    super.key,
    required this.child,
    this.duration = const Duration(milliseconds: 1500),
    this.baseColor = const Color(0xFFE0E0E0),
    this.highlightColor = const Color(0xFFF5F5F5),
  });

  @override
  State<ShimmerEffect> createState() => _ShimmerEffectState();
}

class _ShimmerEffectState extends State<ShimmerEffect>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: widget.duration,
      vsync: this,
    )..repeat();
    _animation = Tween<double>(begin: -2, end: 2).animate(
      CurvedAnimation(parent: _controller, curve: Curves.easeInOutSine),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _animation,
      builder: (context, child) {
        return ShaderMask(
          shaderCallback: (bounds) => LinearGradient(
            begin: Alignment(_animation.value - 1, 0),
            end: Alignment(_animation.value + 1, 0),
            colors: [
              widget.baseColor,
              widget.highlightColor,
              widget.baseColor,
            ],
            stops: const [0.0, 0.5, 1.0],
          ).createShader(bounds),
          blendMode: BlendMode.srcATop,
          child: widget.child,
        );
      },
    );
  }
}

/// 闪光效果演示
class ShimmerDemo extends StatelessWidget {
  const ShimmerDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('闪光效果')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          _buildShimmerCard(),
          const SizedBox(height: 16),
          _buildShimmerCard(),
          const SizedBox(height: 16),
          _buildShimmerCard(),
        ],
      ),
    );
  }

  Widget _buildShimmerCard() {
    return ShimmerEffect(
      baseColor: Colors.grey.shade300,
      highlightColor: Colors.grey.shade100,
      child: Container(
        height: 100,
        decoration: BoxDecoration(
          color: Colors.grey.shade300,
          borderRadius: BorderRadius.circular(12),
        ),
      ),
    );
  }
}

六、完整代码示例

下面是一个整合了所有 ShaderMask 效果的完整示例:

dart 复制代码
import 'dart:math' as math;
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.purple),
        useMaterial3: true,
      ),
      home: const ShaderMaskHomePage(),
    );
  }
}

/// ShaderMask 主页
class ShaderMaskHomePage extends StatelessWidget {
  const ShaderMaskHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('✨ ShaderMask 着色器系统'),
      ),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          _buildSectionCard(
            context,
            title: '渐变文字效果',
            description: '文字渐变色彩展示',
            icon: Icons.text_fields,
            color: Colors.purple,
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const GradientTextDemo()),
            ),
          ),
          _buildSectionCard(
            context,
            title: '渐变图标效果',
            description: '图标渐变着色',
            icon: Icons.gradient,
            color: Colors.blue,
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const GradientIconDemo()),
            ),
          ),
          _buildSectionCard(
            context,
            title: '动态渐变效果',
            description: '动画驱动的渐变',
            icon: Icons.animation,
            color: Colors.teal,
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const AnimatedGradientDemo()),
            ),
          ),
          _buildSectionCard(
            context,
            title: '扫描渐变进度环',
            description: '圆形进度指示器',
            icon: Icons.donut_large,
            color: Colors.orange,
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const SweepGradientDemo()),
            ),
          ),
          _buildSectionCard(
            context,
            title: '混合模式展示',
            description: '不同混合模式效果',
            icon: Icons.layers,
            color: Colors.green,
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const BlendModeShowcase()),
            ),
          ),
          _buildSectionCard(
            context,
            title: '渐变按钮',
            description: '动态渐变按钮组件',
            icon: Icons.smart_button,
            color: Colors.pink,
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const GradientButtonDemo()),
            ),
          ),
          _buildSectionCard(
            context,
            title: '闪光效果',
            description: '加载占位闪光',
            icon: Icons.auto_awesome,
            color: Colors.amber,
            onTap: () => Navigator.push(
              context,
              MaterialPageRoute(builder: (_) => const ShimmerDemo()),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildSectionCard(
    BuildContext context, {
    required String title,
    required String description,
    required IconData icon,
    required Color color,
    required VoidCallback onTap,
  }) {
    return Card(
      margin: const EdgeInsets.only(bottom: 12),
      child: InkWell(
        onTap: onTap,
        borderRadius: BorderRadius.circular(12),
        child: Padding(
          padding: const EdgeInsets.all(16),
          child: Row(
            children: [
              Container(
                width: 56,
                height: 56,
                decoration: BoxDecoration(
                  color: color.withOpacity(0.1),
                  borderRadius: BorderRadius.circular(12),
                ),
                child: ShaderMask(
                  shaderCallback: (bounds) => LinearGradient(
                    colors: [color, color.withOpacity(0.5)],
                  ).createShader(bounds),
                  blendMode: BlendMode.srcIn,
                  child: Icon(icon, color: Colors.white, size: 28),
                ),
              ),
              const SizedBox(width: 16),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      title,
                      style: const TextStyle(
                        fontSize: 16,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(height: 4),
                    Text(
                      description,
                      style: TextStyle(
                        fontSize: 13,
                        color: Colors.grey[600],
                      ),
                    ),
                  ],
                ),
              ),
              Icon(Icons.chevron_right, color: Colors.grey[400]),
            ],
          ),
        ),
      ),
    );
  }
}

/// 渐变文字组件
class GradientText extends StatelessWidget {
  final String text;
  final TextStyle? style;
  final List<Color> colors;
  final Alignment begin;
  final Alignment end;

  const GradientText({
    super.key,
    required this.text,
    this.style,
    required this.colors,
    this.begin = Alignment.topLeft,
    this.end = Alignment.bottomRight,
  });

  @override
  Widget build(BuildContext context) {
    return ShaderMask(
      shaderCallback: (bounds) => LinearGradient(
        begin: begin,
        end: end,
        colors: colors,
      ).createShader(bounds),
      blendMode: BlendMode.srcIn,
      child: Text(
        text,
        style: (style ?? const TextStyle()).copyWith(color: Colors.white),
      ),
    );
  }
}

/// 渐变文字演示
class GradientTextDemo extends StatelessWidget {
  const GradientTextDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('渐变文字效果')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            GradientText(
              text: 'Flutter',
              style: const TextStyle(fontSize: 48, fontWeight: FontWeight.bold),
              colors: [Colors.purple, Colors.blue, Colors.cyan],
            ),
            const SizedBox(height: 20),
            GradientText(
              text: 'OpenHarmony',
              style: const TextStyle(fontSize: 36, fontWeight: FontWeight.w300),
              colors: [Colors.orange, Colors.red, Colors.pink],
              begin: Alignment.centerLeft,
              end: Alignment.centerRight,
            ),
            const SizedBox(height: 20),
            GradientText(
              text: '跨平台开发',
              style: const TextStyle(fontSize: 32),
              colors: [Colors.green, Colors.teal, Colors.blue],
              begin: Alignment.topCenter,
              end: Alignment.bottomCenter,
            ),
          ],
        ),
      ),
    );
  }
}

/// 渐变图标组件
class GradientIcon extends StatelessWidget {
  final IconData icon;
  final double size;
  final List<Color> colors;

  const GradientIcon({
    super.key,
    required this.icon,
    this.size = 24,
    required this.colors,
  });

  @override
  Widget build(BuildContext context) {
    return ShaderMask(
      shaderCallback: (bounds) => LinearGradient(
        colors: colors,
      ).createShader(bounds),
      blendMode: BlendMode.srcIn,
      child: Icon(icon, size: size, color: Colors.white),
    );
  }
}

/// 图标渐变演示
class GradientIconDemo extends StatelessWidget {
  const GradientIconDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('渐变图标效果')),
      body: GridView.count(
        padding: const EdgeInsets.all(16),
        crossAxisCount: 4,
        mainAxisSpacing: 20,
        crossAxisSpacing: 20,
        children: [
          _buildIconCard(Icons.home, [Colors.blue, Colors.purple]),
          _buildIconCard(Icons.favorite, [Colors.pink, Colors.red]),
          _buildIconCard(Icons.star, [Colors.orange, Colors.yellow]),
          _buildIconCard(Icons.settings, [Colors.teal, Colors.green]),
          _buildIconCard(Icons.person, [Colors.indigo, Colors.blue]),
          _buildIconCard(Icons.shopping_cart, [Colors.amber, Colors.orange]),
          _buildIconCard(Icons.notifications, [Colors.red, Colors.pink]),
          _buildIconCard(Icons.search, [Colors.cyan, Colors.blue]),
        ],
      ),
    );
  }

  Widget _buildIconCard(IconData icon, List<Color> colors) {
    return Container(
      decoration: BoxDecoration(
        color: Colors.grey.shade100,
        borderRadius: BorderRadius.circular(16),
      ),
      child: Center(
        child: GradientIcon(icon: icon, size: 40, colors: colors),
      ),
    );
  }
}

/// 动态渐变文字
class AnimatedGradientText extends StatefulWidget {
  final String text;
  final TextStyle? style;
  final List<List<Color>> colorSets;
  final Duration duration;

  const AnimatedGradientText({
    super.key,
    required this.text,
    this.style,
    required this.colorSets,
    this.duration = const Duration(seconds: 3),
  });

  @override
  State<AnimatedGradientText> createState() => _AnimatedGradientTextState();
}

class _AnimatedGradientTextState extends State<AnimatedGradientText>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: widget.duration,
      vsync: this,
    )..repeat();
    _animation = Tween<double>(begin: 0, end: 1).animate(_controller);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  List<Color> _lerpColors(List<Color> start, List<Color> end, double t) {
    return List.generate(
      start.length,
      (i) => Color.lerp(start[i], end[i], t) ?? start[i],
    );
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _animation,
      builder: (context, child) {
        final progress = _animation.value * (widget.colorSets.length - 1);
        final index = progress.floor();
        final nextIndex = (index + 1) % widget.colorSets.length;
        final t = progress - index;

        final colors = _lerpColors(
          widget.colorSets[index],
          widget.colorSets[nextIndex],
          t,
        );

        return ShaderMask(
          shaderCallback: (bounds) => LinearGradient(
            begin: Alignment.topLeft,
            end: Alignment.bottomRight,
            colors: colors,
          ).createShader(bounds),
          blendMode: BlendMode.srcIn,
          child: Text(
            widget.text,
            style: (widget.style ?? const TextStyle()).copyWith(color: Colors.white),
          ),
        );
      },
    );
  }
}

/// 动态渐变演示页面
class AnimatedGradientDemo extends StatelessWidget {
  const AnimatedGradientDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('动态渐变效果')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            AnimatedGradientText(
              text: 'Flutter',
              style: const TextStyle(fontSize: 48, fontWeight: FontWeight.bold),
              colorSets: [
                [Colors.purple, Colors.blue],
                [Colors.blue, Colors.cyan],
                [Colors.cyan, Colors.green],
                [Colors.green, Colors.yellow],
                [Colors.yellow, Colors.orange],
                [Colors.orange, Colors.red],
                [Colors.red, Colors.purple],
              ],
            ),
            const SizedBox(height: 40),
            AnimatedGradientText(
              text: 'OpenHarmony',
              style: const TextStyle(fontSize: 32),
              colorSets: [
                [Colors.pink, Colors.red],
                [Colors.red, Colors.orange],
                [Colors.orange, Colors.amber],
                [Colors.amber, Colors.pink],
              ],
              duration: const Duration(seconds: 2),
            ),
          ],
        ),
      ),
    );
  }
}

/// 扫描渐变进度环
class SweepGradientProgress extends StatefulWidget {
  final double progress;
  final double size;
  final double strokeWidth;
  final List<Color> colors;

  const SweepGradientProgress({
    super.key,
    required this.progress,
    this.size = 200,
    this.strokeWidth = 15,
    this.colors = const [Colors.blue, Colors.purple, Colors.pink, Colors.red],
  });

  @override
  State<SweepGradientProgress> createState() => _SweepGradientProgressState();
}

class _SweepGradientProgressState extends State<SweepGradientProgress>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 3),
      vsync: this,
    )..repeat();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (context, child) {
        return Transform.rotate(
          angle: _controller.value * 2 * math.pi,
          child: SizedBox(
            width: widget.size,
            height: widget.size,
            child: CustomPaint(
              painter: SweepProgressPainter(
                progress: widget.progress,
                strokeWidth: widget.strokeWidth,
                colors: widget.colors,
              ),
            ),
          ),
        );
      },
    );
  }
}

/// 扫描进度绘制器
class SweepProgressPainter extends CustomPainter {
  final double progress;
  final double strokeWidth;
  final List<Color> colors;

  SweepProgressPainter({
    required this.progress,
    required this.strokeWidth,
    required this.colors,
  });

  @override
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    final radius = (size.width - strokeWidth) / 2;

    final bgPaint = Paint()
      ..color = Colors.grey.shade200
      ..style = PaintingStyle.stroke
      ..strokeWidth = strokeWidth
      ..strokeCap = StrokeCap.round;
    canvas.drawCircle(center, radius, bgPaint);

    final rect = Rect.fromCircle(center: center, radius: radius);
    final gradient = SweepGradient(
      center: Alignment.center,
      colors: colors,
      stops: List.generate(colors.length, (i) => i / (colors.length - 1)),
    );

    final progressPaint = Paint()
      ..shader = gradient.createShader(rect)
      ..style = PaintingStyle.stroke
      ..strokeWidth = strokeWidth
      ..strokeCap = StrokeCap.round;

    final sweepAngle = 2 * math.pi * progress;
    canvas.drawArc(rect, -math.pi / 2, sweepAngle, false, progressPaint);

    final textPainter = TextPainter(
      text: TextSpan(
        text: '${(progress * 100).toInt()}%',
        style: TextStyle(
          fontSize: strokeWidth * 2,
          fontWeight: FontWeight.bold,
          color: colors.first,
        ),
      ),
      textDirection: TextDirection.ltr,
    );
    textPainter.layout();
    textPainter.paint(
      canvas,
      Offset(
        center.dx - textPainter.width / 2,
        center.dy - textPainter.height / 2,
      ),
    );
  }

  @override
  bool shouldRepaint(SweepProgressPainter oldDelegate) {
    return progress != oldDelegate.progress;
  }
}

/// 扫描渐变演示
class SweepGradientDemo extends StatefulWidget {
  const SweepGradientDemo({super.key});

  @override
  State<SweepGradientDemo> createState() => _SweepGradientDemoState();
}

class _SweepGradientDemoState extends State<SweepGradientDemo> {
  double _progress = 0.75;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('扫描渐变进度环')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            SweepGradientProgress(progress: _progress),
            const SizedBox(height: 40),
            Slider(
              value: _progress,
              onChanged: (value) => setState(() => _progress = value),
            ),
          ],
        ),
      ),
    );
  }
}

/// 混合模式展示
class BlendModeShowcase extends StatelessWidget {
  const BlendModeShowcase({super.key});

  static const List<Map<String, dynamic>> blendModes = [
    {'name': 'srcIn', 'mode': BlendMode.srcIn, 'desc': '遮罩效果'},
    {'name': 'modulate', 'mode': BlendMode.modulate, 'desc': '颜色相乘'},
    {'name': 'screen', 'mode': BlendMode.screen, 'desc': '屏幕混合'},
    {'name': 'overlay', 'mode': BlendMode.overlay, 'desc': '叠加效果'},
    {'name': 'difference', 'mode': BlendMode.difference, 'desc': '差值混合'},
    {'name': 'hardLight', 'mode': BlendMode.hardLight, 'desc': '强光效果'},
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('混合模式展示')),
      body: GridView.builder(
        padding: const EdgeInsets.all(16),
        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
          mainAxisSpacing: 16,
          crossAxisSpacing: 16,
          childAspectRatio: 0.85,
        ),
        itemCount: blendModes.length,
        itemBuilder: (context, index) {
          final item = blendModes[index];
          return _buildBlendModeCard(item['name'], item['mode'], item['desc']);
        },
      ),
    );
  }

  Widget _buildBlendModeCard(String name, BlendMode mode, String desc) {
    return Card(
      clipBehavior: Clip.antiAlias,
      child: Column(
        children: [
          Expanded(
            child: ShaderMask(
              shaderCallback: (bounds) => const LinearGradient(
                colors: [Colors.purple, Colors.pink, Colors.orange],
              ).createShader(bounds),
              blendMode: mode,
              child: Container(
                width: double.infinity,
                color: Colors.blue,
                child: const Center(
                  child: Text(
                    'Flutter',
                    style: TextStyle(
                      fontSize: 28,
                      fontWeight: FontWeight.bold,
                      color: Colors.white,
                    ),
                  ),
                ),
              ),
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8),
            child: Column(
              children: [
                Text(name, style: const TextStyle(fontWeight: FontWeight.bold)),
                Text(desc, style: TextStyle(fontSize: 12, color: Colors.grey[600])),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

/// 渐变按钮组件
class GradientButton extends StatefulWidget {
  final String text;
  final VoidCallback onPressed;
  final List<Color> colors;
  final double borderRadius;

  const GradientButton({
    super.key,
    required this.text,
    required this.onPressed,
    required this.colors,
    this.borderRadius = 25,
  });

  @override
  State<GradientButton> createState() => _GradientButtonState();
}

class _GradientButtonState extends State<GradientButton>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;
  bool _isPressed = false;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(milliseconds: 1500),
      vsync: this,
    )..repeat();
    _animation = Tween<double>(begin: 0, end: 1).animate(_controller);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTapDown: (_) => setState(() => _isPressed = true),
      onTapUp: (_) {
        setState(() => _isPressed = false);
        widget.onPressed();
      },
      onTapCancel: () => setState(() => _isPressed = false),
      child: AnimatedScale(
        scale: _isPressed ? 0.95 : 1,
        duration: const Duration(milliseconds: 100),
        child: AnimatedBuilder(
          animation: _animation,
          builder: (context, child) {
            return Container(
              padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
              decoration: BoxDecoration(
                gradient: LinearGradient(
                  begin: Alignment(-1 + _animation.value * 2, 0),
                  end: Alignment(1 - _animation.value * 2, 0),
                  colors: widget.colors,
                ),
                borderRadius: BorderRadius.circular(widget.borderRadius),
                boxShadow: [
                  BoxShadow(
                    color: widget.colors.first.withOpacity(0.4),
                    blurRadius: 15,
                    offset: const Offset(0, 5),
                  ),
                ],
              ),
              child: Text(
                widget.text,
                style: const TextStyle(
                  color: Colors.white,
                  fontSize: 16,
                  fontWeight: FontWeight.bold,
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}

/// 渐变按钮演示
class GradientButtonDemo extends StatelessWidget {
  const GradientButtonDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('渐变按钮')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            GradientButton(
              text: '开始体验',
              onPressed: () => ScaffoldMessenger.of(context).showSnackBar(
                const SnackBar(content: Text('按钮被点击')),
              ),
              colors: [Colors.purple, Colors.blue],
            ),
            const SizedBox(height: 20),
            GradientButton(
              text: '立即登录',
              onPressed: () {},
              colors: [Colors.orange, Colors.red],
            ),
            const SizedBox(height: 20),
            GradientButton(
              text: '了解更多',
              onPressed: () {},
              colors: [Colors.teal, Colors.green],
            ),
          ],
        ),
      ),
    );
  }
}

/// 闪光效果组件
class ShimmerEffect extends StatefulWidget {
  final Widget child;
  final Duration duration;
  final Color baseColor;
  final Color highlightColor;

  const ShimmerEffect({
    super.key,
    required this.child,
    this.duration = const Duration(milliseconds: 1500),
    this.baseColor = const Color(0xFFE0E0E0),
    this.highlightColor = const Color(0xFFF5F5F5),
  });

  @override
  State<ShimmerEffect> createState() => _ShimmerEffectState();
}

class _ShimmerEffectState extends State<ShimmerEffect>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: widget.duration,
      vsync: this,
    )..repeat();
    _animation = Tween<double>(begin: -2, end: 2).animate(
      CurvedAnimation(parent: _controller, curve: Curves.easeInOutSine),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _animation,
      builder: (context, child) {
        return ShaderMask(
          shaderCallback: (bounds) => LinearGradient(
            begin: Alignment(_animation.value - 1, 0),
            end: Alignment(_animation.value + 1, 0),
            colors: [
              widget.baseColor,
              widget.highlightColor,
              widget.baseColor,
            ],
            stops: const [0.0, 0.5, 1.0],
          ).createShader(bounds),
          blendMode: BlendMode.srcATop,
          child: widget.child,
        );
      },
    );
  }
}

/// 闪光效果演示
class ShimmerDemo extends StatelessWidget {
  const ShimmerDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('闪光效果')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          _buildShimmerCard(),
          const SizedBox(height: 16),
          _buildShimmerCard(),
          const SizedBox(height: 16),
          _buildShimmerCard(),
        ],
      ),
    );
  }

  Widget _buildShimmerCard() {
    return ShimmerEffect(
      baseColor: Colors.grey.shade300,
      highlightColor: Colors.grey.shade100,
      child: Container(
        height: 100,
        decoration: BoxDecoration(
          color: Colors.grey.shade300,
          borderRadius: BorderRadius.circular(12),
        ),
      ),
    );
  }
}

七、最佳实践与注意事项

✅ 7.1 性能优化建议

在使用 ShaderMask 时,性能优化至关重要:

1. 避免过度使用

ShaderMask 会增加 GPU 渲染负担,应避免在大量组件上同时使用:

dart 复制代码
// 不推荐:在列表项中使用 ShaderMask
ListView.builder(
  itemBuilder: (context, index) => ShaderMask(...), // 性能问题
);

// 推荐:仅在关键视觉元素上使用
ListView.builder(
  itemBuilder: (context, index) => index == 0 ? ShaderMask(...) : NormalWidget(),
);

2. 缓存 Shader

对于静态渐变,可以缓存 Shader 对象避免重复创建:

dart 复制代码
class CachedShaderWidget extends StatefulWidget {
  const CachedShaderWidget({super.key});

  @override
  State<CachedShaderWidget> createState() => _CachedShaderWidgetState();
}

class _CachedShaderWidgetState extends State<CachedShaderWidget> {
  Shader? _cachedShader;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    final gradient = const LinearGradient(colors: [Colors.blue, Colors.purple]);
    _cachedShader = gradient.createShader(const Rect.fromLTWH(0, 0, 200, 50));
  }

  @override
  Widget build(BuildContext context) {
    return ShaderMask(
      shaderCallback: (_) => _cachedShader!,
      child: const Text('缓存Shader'),
    );
  }
}

3. 合理使用混合模式

某些混合模式(如 difference)计算复杂度较高,应谨慎使用。

⚠️ 7.2 常见问题与解决方案

问题 原因 解决方案
渐变不显示 子组件颜色设置错误 子组件需设置非透明颜色
性能卡顿 过多 ShaderMask 减少使用数量,优化渐变复杂度
颜色异常 混合模式选择错误 根据需求选择正确的 BlendMode
动画闪烁 每帧创建新 Shader 缓存 Shader 或使用 AnimatedBuilder
边界溢出 Shader 边界计算错误 使用 bounds 参数正确计算

📝 7.3 代码规范建议

  1. 封装常用效果:将常用的着色效果封装为独立组件
  2. 提供配置选项:允许自定义颜色、方向、混合模式等参数
  3. 添加注释说明:对于复杂的着色逻辑添加清晰的注释
  4. 考虑无障碍:确保着色效果不影响文字的可读性

八、总结

本文深入探讨了 Flutter 的 ShaderMask 着色器系统,从基础概念到高级实现,帮助你构建专业级的视觉效果。

核心要点回顾:

📌 ShaderMask 原理:理解着色器如何与子组件像素进行混合

📌 渐变类型:掌握线性渐变、径向渐变、扫描渐变的特点和应用场景

📌 混合模式:了解不同 BlendMode 的效果差异,选择合适的混合方式

📌 动态效果:使用动画控制器驱动渐变变化,创建流动的视觉效果

📌 性能优化:合理使用 ShaderMask,避免过度渲染

通过本文的学习,你应该能够灵活运用 ShaderMask 创建各种视觉效果,为应用增添独特的视觉魅力。在实际开发中,请根据具体需求选择合适的着色方案,并注意性能优化。


九、参考资料

相关推荐
2501_921930832 小时前
进阶实战 Flutter for OpenHarmony:ClipPath 自定义裁剪系统 - 高级视觉效果实现
flutter
2501_921930835 小时前
进阶实战 Flutter for OpenHarmony:复合动画与粒子系统 - 高级视觉效果实现
flutter
2501_921930835 小时前
进阶实战 Flutter for OpenHarmony:Transform 变换矩阵系统 - 高级视觉效果实现
flutter
2501_921930836 小时前
进阶实战 Flutter for OpenHarmony:自定义仪表盘系统 - 高级数据可视化实现
flutter·信息可视化
2601_949593656 小时前
进阶实战 Flutter for OpenHarmony:InheritedWidget 组件实战 - 跨组件数据
flutter
阿林来了6 小时前
Flutter三方库适配OpenHarmony【flutter_speech】— 持续语音识别与长录音
flutter·语音识别·harmonyos
lili-felicity6 小时前
进阶实战 Flutter for OpenHarmony:mobile_device_identifier 第三方库实战 - 设备标识
flutter
松叶似针7 小时前
Flutter三方库适配OpenHarmony【secure_application】— 与 HarmonyOS 安全能力的深度集成
安全·flutter·harmonyos