You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter Canvas中实现仅绘制文字描边的效果

Flutter Canvas 实现文字描边/填充样式切换方案

核心原理

Flutter的ui.Paint类自带绘制模式配置,配合文字路径转换能力,就能直接实现类似设计软件的文字样式效果:

  • PaintingStyle.fill 对应填充模式,为默认绘制样式
  • PaintingStyle.stroke 对应描边模式,可搭配strokeWidth调整描边粗细
  • 需要描边+填充同时显示时,分层绘制即可,先画底层描边再画上层填充,避免描边覆盖填充区域

完整实现代码

import 'package:flutter/material.dart';
import 'dart:ui' as ui;

// 文字样式枚举
enum TextStyleMode { fill, stroke, fillAndStroke }

class StyledTextPainter extends CustomPainter {
  final String text;
  final double fontSize;
  final Color fillColor;
  final Color strokeColor;
  final double strokeWidth;
  final TextStyleMode mode;

  StyledTextPainter({
    required this.text,
    required this.fontSize,
    required this.fillColor,
    required this.strokeColor,
    required this.strokeWidth,
    required this.mode,
  });

  @override
  void paint(Canvas canvas, Size size) {
    // 配置基础文本样式
    final textStyle = TextStyle(
      fontSize: fontSize,
      // 可替换为自定义字体,需提前在pubspec.yaml中配置
      fontFamily: 'system',
    );
    final textSpan = TextSpan(text: text, style: textStyle);
    final textPainter = TextPainter(
      text: textSpan,
      textDirection: TextDirection.ltr,
    );
    textPainter.layout();

    // 生成居中的文字路径
    final textOffset = Offset(
      (size.width - textPainter.width) / 2,
      (size.height - textPainter.height) / 2,
    );
    final textPath = textPainter.text!.toPath(textOffset);

    // 按选中模式绘制
    switch (mode) {
      case TextStyleMode.fill:
        final fillPaint = Paint()
          ..style = PaintingStyle.fill
          ..color = fillColor;
        canvas.drawPath(textPath, fillPaint);
        break;
      case TextStyleMode.stroke:
        final strokePaint = Paint()
          ..style = PaintingStyle.stroke
          ..strokeWidth = strokeWidth
          ..strokeCap = StrokeCap.round
          ..strokeJoin = StrokeJoin.round
          ..color = strokeColor;
        canvas.drawPath(textPath, strokePaint);
        break;
      case TextStyleMode.fillAndStroke:
        // 先画描边底层,再画填充上层,效果更符合设计预期
        final strokePaint = Paint()
          ..style = PaintingStyle.stroke
          ..strokeWidth = strokeWidth
          ..strokeCap = StrokeCap.round
          ..strokeJoin = StrokeJoin.round
          ..color = strokeColor;
        final fillPaint = Paint()
          ..style = PaintingStyle.fill
          ..color = fillColor;
        canvas.drawPath(textPath, strokePaint);
        canvas.drawPath(textPath, fillPaint);
        break;
    }
  }

  @override
  bool shouldRepaint(covariant StyledTextPainter oldDelegate) {
    return oldDelegate.text != text ||
        oldDelegate.fontSize != fontSize ||
        oldDelegate.fillColor != fillColor ||
        oldDelegate.strokeColor != strokeColor ||
        oldDelegate.strokeWidth != strokeWidth ||
        oldDelegate.mode != mode;
  }
}

使用示例

直接通过CustomPaint组件调用即可,需要切换样式时仅修改mode参数触发重绘就行:

// 仅描边模式示例,效果见下图
CustomPaint(
  size: const Size(350, 120),
  painter: StyledTextPainter(
    text: "STROKE TEXT",
    fontSize: 52,
    fillColor: Colors.white,
    strokeColor: Colors.black,
    strokeWidth: 2,
    mode: TextStyleMode.stroke,
  ),
)

仅描边文字效果图

注意事项

  • 拐角处描边出现毛刺时,调整strokeJoin和strokeCap为round即可优化显示效果
  • 使用自定义字体时需要提前在pubspec.yaml中完成字体注册,否则样式不生效
  • 文字转路径的API仅支持Flutter 2.0及以上版本,低版本项目需要先升级SDK

内容的提问来源于stack exchange,提问作者Zihan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 21:36:00