深度解析 flet-map 的 SolidStrokePattern:用纯 Python 定义地图实线描边模式
【免费下载链接】fletBuild realtime web, mobile and desktop apps in Python only. No frontend experience required.项目地址: https://gitcode.com/gh_mirrors/fl/flet
SolidStrokePattern是 flet-map(Flet 官方的地图扩展包)中负责"实线描边"的值类型,它决定了地图上PolylineLayer折线与多边形边框是实线、虚线还是点线。本文以 solidstrokepattern.md 文档页为核心,结合 types.py 源码与 Flutter 端解析实现,完整讲解SolidStrokePattern的类设计、序列化机制、默认值行为,以及与DashedStrokePattern、DottedStrokePattern、PatternFit的协作方式。读完后你将能够在地图图层中精确控制线型渲染,并理解 flet-map 值类型从前端到渲染引擎的完整调用链。
SolidStrokePattern 是什么:一行代码定义一个"实线"描边
从官方文档页的 API 定义看,SolidStrokePattern是flet_map包导出的公开类型之一,其语义非常直白:一条连续不断(solid/unbroken)的描边模式。它没有自己的成员字段,全部行为都由父类StrokePattern与类内部的类型标记机制承担。
# sdk/python/packages/flet-map/src/flet_map/types.py @ft.value class StrokePattern: """ Determines whether a stroke should be solid, dotted, or dashed, and the exact characteristics of each. This is an abstract class and shouldn't be used directly. See usable derivatives: - :class:`~flet_map.SolidStrokePattern` - :class:`~flet_map.DashedStrokePattern` - :class:`~flet_map.DottedStrokePattern` """ _type: Optional[str] = field(init=False, repr=False, compare=False, default=None) @ft.value class SolidStrokePattern(StrokePattern): """A solid/unbroken stroke pattern.""" def __post_init__(self): self._type = "solid"从源码结构看,整个实线模式的设计只有两个关键点:
StrokePattern是一个抽象基类,官方 docstring 明确告诫"不应直接使用",它只定义了一个私有的_type字段(init=False、repr=False、compare=False,即不参与初始化、打印与相等比较),用于在传输时标识具体模式类型;SolidStrokePattern通过重写__post_init__,在构造完成后把_type置为字符串"solid"。
这意味着实例化SolidStrokePattern()时不需要任何参数——它天然就是一个"零配置"的实线模式,这也解释了为什么它可以作为PolylineMarker的默认描边。
类层次与兄弟模式:一组可互换的描边值类型
SolidStrokePattern并不是孤立存在的。在 types.py 中,它与另外两个具体子类共同组成StrokePattern家族:
| 类 | 类型标记 | 关键字段 | 适用场景 |
|---|---|---|---|
SolidStrokePattern | "solid" | 无 | 连续实线(默认) |
DashedStrokePattern | "dashed" | segments: list[ft.Number]、pattern_fit | 交替的虚线段与空隙 |
DottedStrokePattern | "dotted" | spacing_factor: ft.Number = 1.5、pattern_fit | 圆形点线 |
三个类都以@ft.value装饰,说明它们是 Flet 的值类型(value type),会被整体序列化后发给客户端渲染,而不是像控件那样拥有独立的生命周期。
DashedStrokePattern:需要显式参数的模式
虚线模式与实线模式形成鲜明对比——它必须提供segments参数,并附带严格的校验逻辑:
segments: list[ft.Number] """ A list of even length with a minimum of 2, in the form of [a₁, b₁, (a₂, b₂, ...)], where `a` should be the length of segments in 'units', and `b` the length of the space after each segment in units. Both values must be strictly positive. ... For example, `[50, 10, 10, 10]` will cause: * a segment of length 50px * followed by a space of 10px * followed by a segment of length 10px * followed by a space of 10px * followed by a segment of length of 50px * ... """ pattern_fit: PatternFit = PatternFit.SCALE_UP def __setattr__(self, name, value): if name == "segments": if len(value) < 2: raise ValueError("segments must contain at least two items") if len(value) % 2 != 0: raise ValueError("segments length must be even") super().__setattr__(name, value)可以看到,segments必须是偶数长度、至少 2 项的列表,以"段长、间距"交替的形式描述模式;一旦违反会在赋值阶段直接抛出ValueError。而SolidStrokePattern因为不需要任何参数,天然规避了这类校验问题。
DottedStrokePattern:以线宽为基准的点距
点线模式用spacing_factor控制点间距——间距是相对于stroke_width(或Polygon.border_stroke_width)的倍数,1.0表示间距恰好等于线宽,值越大点越稀疏,并且它同样在__post_init__中校验spacing_factor <= 0时抛出ValueError。
理解了兄弟模式,就能明白SolidStrokePattern在整个描边体系中的定位:它是唯一无参数、无需任何适配策略的模式,因而成为折线/多边形边框的默认选择。
底层机制:_type标记与跨端序列化
SolidStrokePattern的实现虽然极简,但它依赖的_type序列化机制贯穿了整个 flet-map 的跨端通信。Python 端构造对象时写入_type = "solid",这个字段会随值类型一起被编码为 JSON 发送给 Flutter 客户端;Flutter 端在 utils/map.dart 的parseStrokePattern中根据_type分发到 flutter_map 库对应的构造器:
StrokePattern? parseStrokePattern(dynamic value, [StrokePattern? defaultValue]) { if (value == null) return defaultValue; final type = value['_type']; if (type == 'dotted') { return StrokePattern.dotted( spacingFactor: parseDouble(value['spacing_factor'], 1.5)!, patternFit: parsePatternFit(value['pattern_fit'], PatternFit.scaleUp)!, ); } else if (type == 'solid') { return const StrokePattern.solid(); } else if (type == 'dashed') { var segments = value['segments'] as List<dynamic>; return StrokePattern.dashed( patternFit: parsePatternFit(value['pattern_fit'], PatternFit.scaleUp)!, segments: segments.map((e) => parseDouble(e)).nonNulls.toList(), ); } return defaultValue; }这段 Dart 代码印证了几个实现事实:
- Python 端
_type的取值(solid/dashed/dotted)与 Dart 端的分支判断严格一一对应; - 三个模式在 Flutter 端最终都映射为 flutter_map 库的
StrokePattern系列构造器,其中solid直接映射为无参的const StrokePattern.solid(),与 Python 端零参数的设计完全对称; pattern_fit在 Flutter 端同样有parsePatternFit解析,默认回退到PatternFit.scaleUp,与 Python 端PatternFit.SCALE_UP的默认值保持一致。
这一机制也解释了为什么SolidStrokePattern的 Python 代码如此"空":它不需要携带任何额外配置,一个_type标记就足以驱动整个渲染管线。
如何与地图图层搭配使用:默认值即为实线
SolidStrokePattern的实战入口在PolylineMarker。查看 polyline_layer.py:
stroke_pattern: StrokePattern = field(default_factory=lambda: SolidStrokePattern()) """ Determines whether the line should be solid, dotted, or dashed, and the exact characteristics of each. """也就是说,不设置stroke_pattern时,地图折线默认就是实线——这是SolidStrokePattern最重要的实际行为。而PolygonMarker(多边形)则通过border_stroke_width、border_color等字段控制边框外观(见 polygon_layer.py),当需要在多边形或折线上使用点线/虚线时,spacing_factor的基准宽度即来自stroke_width或border_stroke_width。
下面是一个完整的可运行示例,把默认实线与两种兄弟模式放进同一张地图对比展示(框架参照 examples/extensions/map/multi_layers/main.py):
import flet as ft import flet_map as ftm def main(page: ft.Page): page.add( ftm.Map( layers=[ ftm.TileLayer( url_template="https://tile.openstreetmap.org/{z}/{x}/{y}.png", ), ftm.PolylineLayer( polylines=[ # 默认实线:不传 stroke_pattern 即为 SolidStrokePattern ftm.PolylineMarker( color=ft.Colors.BLUE, stroke_width=5, coordinates=[ ftm.MapLatitudeLongitude(10, 10), ftm.MapLatitudeLongitude(30, 15), ], ), # 显式指定实线(与默认等价) ftm.PolylineMarker( color=ft.Colors.GREEN, stroke_width=5, stroke_pattern=ftm.SolidStrokePattern(), coordinates=[ ftm.MapLatitudeLongitude(10, 12), ftm.MapLatitudeLongitude(30, 17), ], ), # 虚线:段长 50px、间距 10px 交替 ftm.PolylineMarker( color=ft.Colors.RED, stroke_width=5, stroke_pattern=ftm.DashedStrokePattern( segments=[50, 10, 10, 10], pattern_fit=ftm.PatternFit.SCALE_UP, ), coordinates=[ ftm.MapLatitudeLongitude(10, 14), ftm.MapLatitudeLongitude(30, 19), ], ), # 点线:默认间距为线宽的 1.5 倍 ftm.PolylineMarker( color=ft.Colors.ORANGE, stroke_width=5, stroke_pattern=ftm.DottedStrokePattern( spacing_factor=2.0, pattern_fit=ftm.PatternFit.SCALE_UP, ), coordinates=[ ftm.MapLatitudeLongitude(10, 16), ftm.MapLatitudeLongitude(30, 21), ], ), ], ), ] ) ) ft.app(main)需要说明的是:flet_map属于独立的扩展包,运行前需先安装(pip install flet-map),并通过pyproject.toml将其声明为项目依赖;地图控件只有MapLatitudeLongitude(经纬度)与PolylineLayer/PolygonLayer组合使用时,描边模式才生效。
PatternFit:非实线模式的适配策略
SolidStrokePattern无需关心模式与线长是否整除,但它的两个兄弟模式都带有pattern_fit: PatternFit = PatternFit.SCALE_UP字段。理解 PatternFit 枚举有助于你完整掌握描边体系,其四个取值语义如下:
| 枚举值 | 行为说明 |
|---|---|
NONE | 不做任何适配,严格按定义重复模式,到终点即停止;不推荐,可能在线段末端留下缺口 |
SCALE_DOWN | 缩放模式,使其恰好整数次铺满折线(向下取整,模式相对更小) |
SCALE_UP | 缩放模式,使其恰好整数次铺满折线(向上取整,模式相对更大);这是DashedStrokePattern与DottedStrokePattern的默认值 |
APPEND_DOT | 按原样使用模式,末段放不下则截断;若末段未到达终点则在终点补一个点 |
EXTEND_FINAL_DASH | 按原样使用模式,末段放不下则截断;若末段未到达终点则延伸末段直至终点(仅对DashedStrokePattern有意义) |
从源码 docstring 可以看到,"units"(单位)默认指像素,只有启用SCALE_UP时模式才可能被放大。这也是SolidStrokePattern不需要pattern_fit字段的原因——实线不存在"模式长度与线长不整除"的问题,任何长度下它都是连续的。
小结:从文档到渲染的完整链条
SolidStrokePattern是 flet-map 描边体系中"最简单"却也最常用的一环,其技术要点可归纳为四条:
- 零参数值类型:继承自抽象类
StrokePattern,仅通过__post_init__写入_type = "solid"(types.py); - 默认描边:
PolylineMarker.stroke_pattern的默认工厂函数即SolidStrokePattern()(polyline_layer.py),所以地图折线默认就是实线; - 跨端一致:Flutter 端
parseStrokePattern按_type分支,solid映射为const StrokePattern.solid()(utils/map.dart); - 与兄弟模式互补:
DashedStrokePattern需要偶数长度的segments,DottedStrokePattern需要大于 0 的spacing_factor,二者都默认使用PatternFit.SCALE_UP适配线长。
如需进一步探索,推荐按以下路径继续阅读仓库源码:描边类型的完整定义在 types.py,折线图层与默认值在 polyline_layer.py,跨端解析逻辑在 utils/map.dart,而文档体系中与本文配套的类型页还包括 strokepattern.md、dashedstrokepattern.md、dottedstrokepattern.md 与 patternfit.md。
【免费下载链接】fletBuild realtime web, mobile and desktop apps in Python only. No frontend experience required.项目地址: https://gitcode.com/gh_mirrors/fl/flet
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考