主题
DataTable2
为 Flet 提供的增强数据表格,通过 flet-datatable2 扩展加入了固定表头、固定行/列以及其他 UX 改进。
它封装了 Flutter 的 data_table_2 包。
平台支持
| 平台 | Windows | macOS | Linux | iOS | Android | Web |
|---|---|---|---|---|---|---|
| 支持 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
用法
把 flet-datatable2 添加到项目依赖中:
uv
bash
uv add flet-datatable2pip
bash
pip install flet-datatable2 [^1][^1]: 之后你需要手动把这个包添加到 requirements.txt 或 pyproject.toml 中。
示例
python
import flet as ft
import flet_datatable2 as fdt
def main(page: ft.Page):
page.add(
ft.SafeArea(
expand=True,
content=fdt.DataTable2(
expand=True,
empty=ft.Text("This table is empty."),
columns=[
fdt.DataColumn2(label=ft.Text("First name")),
fdt.DataColumn2(label=ft.Text("Last name")),
fdt.DataColumn2(label=ft.Text("Age"), numeric=True),
],
),
)
)
if __name__ == "__main__":
ft.run(main)
python
from dataclasses import dataclass
import flet as ft
import flet_datatable2 as ftd
@dataclass
class Dessert:
name: str
calories: float
fat: float
carbs: float
protein: float
sodium: float
calcium: float
iron: float
desserts = [
Dessert("Frozen Yogurt", 159, 6.0, 24, 4.0, 87, 14, 1),
Dessert("Ice Cream Sandwich", 237, 9.0, 37, 4.3, 129, 8, 1),
Dessert("Eclair", 262, 16.0, 24, 6.0, 337, 6, 7),
Dessert("Cupcake", 305, 3.7, 67, 4.3, 413, 3, 8),
Dessert("Gingerbread", 356, 16.0, 49, 3.9, 327, 7, 16),
Dessert("Jelly Bean", 375, 0.0, 94, 0.0, 50, 0, 0),
Dessert("Lollipop", 392, 0.2, 98, 0.0, 38, 0, 2),
Dessert("Honeycomb", 408, 3.2, 87, 6.5, 562, 0, 45),
Dessert("Donut", 452, 25.0, 51, 4.9, 326, 2, 22),
Dessert("Apple Pie", 518, 26.0, 65, 7.0, 54, 12, 6),
Dessert("Frozen Yougurt with sugar", 168, 6.0, 26, 4.0, 87, 14, 1),
Dessert("Ice Cream Sandwich with sugar", 246, 9.0, 39, 4.3, 129, 8, 1),
Dessert("Eclair with sugar", 271, 16.0, 26, 6.0, 337, 6, 7),
Dessert("Cupcake with sugar", 314, 3.7, 69, 4.3, 413, 3, 8),
Dessert("Gingerbread with sugar", 345, 16.0, 51, 3.9, 327, 7, 16),
Dessert("Jelly Bean with sugar", 364, 0.0, 96, 0.0, 50, 0, 0),
Dessert("Lollipop with sugar", 401, 0.2, 100, 0.0, 38, 0, 2),
Dessert("Honeycomb with sugar", 417, 3.2, 89, 6.5, 562, 0, 45),
Dessert("Donut with sugar", 461, 25.0, 53, 4.9, 326, 2, 22),
Dessert("Apple pie with sugar", 527, 26.0, 67, 7.0, 54, 12, 6),
Dessert("Frozen yougurt with honey", 223, 6.0, 36, 4.0, 87, 14, 1),
Dessert("Ice Cream Sandwich with honey", 301, 9.0, 49, 4.3, 129, 8, 1),
Dessert("Eclair with honey", 326, 16.0, 36, 6.0, 337, 6, 7),
Dessert("Cupcake with honey", 369, 3.7, 79, 4.3, 413, 3, 8),
Dessert("Gingerbread with honey", 420, 16.0, 61, 3.9, 327, 7, 16),
Dessert("Jelly Bean with honey", 439, 0.0, 106, 0.0, 50, 0, 0),
Dessert("Lollipop with honey", 456, 0.2, 110, 0.0, 38, 0, 2),
Dessert("Honeycomb with honey", 472, 3.2, 99, 6.5, 562, 0, 45),
Dessert("Donut with honey", 516, 25.0, 63, 4.9, 326, 2, 22),
Dessert("Apple pie with honey", 582, 26.0, 77, 7.0, 54, 12, 6),
]
def main(page: ft.Page):
page.vertical_alignment = ft.MainAxisAlignment.CENTER
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
sorted_desserts = list(desserts)
def handle_row_selection_change(e: ft.Event[ftd.DataRow2]) -> None:
e.control.selected = not e.control.selected
e.control.update()
def sort_column(e: ft.DataColumnSortEvent) -> None:
sorters = [
lambda d: d.name.lower(),
lambda d: d.calories,
lambda d: d.fat,
lambda d: d.carbs,
lambda d: d.protein,
lambda d: d.sodium,
lambda d: d.calcium,
lambda d: d.iron,
]
sorted_desserts.sort(key=sorters[e.column_index], reverse=not e.ascending)
table.rows = get_data_rows(sorted_desserts)
table.sort_column_index = e.column_index
table.sort_ascending = e.ascending
table.update()
def get_data_columns() -> list[ftd.DataColumn2]:
return [
ftd.DataColumn2(
label=ft.Text("Name"),
size=ftd.DataColumnSize.L,
on_sort=sort_column,
heading_row_alignment=ft.MainAxisAlignment.START,
),
ftd.DataColumn2(
label=ft.Text("Calories"),
on_sort=sort_column,
numeric=True,
heading_row_alignment=ft.MainAxisAlignment.END,
),
ftd.DataColumn2(label=ft.Text("Fat"), on_sort=sort_column, numeric=True),
ftd.DataColumn2(label=ft.Text("Carbs"), on_sort=sort_column, numeric=True),
ftd.DataColumn2(
label=ft.Text("Protein"),
on_sort=sort_column,
numeric=True,
),
ftd.DataColumn2(label=ft.Text("Sodium"), on_sort=sort_column, numeric=True),
ftd.DataColumn2(
label=ft.Text("Calcium"),
on_sort=sort_column,
numeric=True,
),
ftd.DataColumn2(label=ft.Text("Iron"), on_sort=sort_column, numeric=True),
]
def get_data_rows(items: list) -> list[ftd.DataRow2]:
return [
ftd.DataRow2(
specific_row_height=50,
on_select_change=handle_row_selection_change,
cells=[
ft.DataCell(content=ft.Text(dessert.name)),
ft.DataCell(content=ft.Text(dessert.calories)),
ft.DataCell(content=ft.Text(dessert.fat)),
ft.DataCell(content=ft.Text(dessert.carbs)),
ft.DataCell(content=ft.Text(dessert.protein)),
ft.DataCell(content=ft.Text(dessert.sodium)),
ft.DataCell(content=ft.Text(dessert.calcium)),
ft.DataCell(content=ft.Text(dessert.iron)),
],
)
for dessert in items
]
table = ftd.DataTable2(
expand=True,
show_checkbox_column=True,
column_spacing=0,
heading_row_color=ft.Colors.SECONDARY_CONTAINER,
horizontal_margin=12,
sort_ascending=True,
bottom_margin=10,
min_width=600,
on_select_all=lambda _: print("All selected"),
columns=get_data_columns(),
rows=get_data_rows(sorted_desserts),
)
page.add(ft.SafeArea(expand=True, content=table))
if __name__ == "__main__":
ft.run(main)
python
import flet as ft
import flet_datatable2 as fdt
def main(page: ft.Page):
def cell_text(value: str) -> ft.Text:
"""A helper to truncate any overflowing cell text with an ellipsis."""
return ft.Text(value, overflow=ft.TextOverflow.ELLIPSIS, max_lines=1)
page.add(
ft.SafeArea(
expand=True,
content=fdt.DataTable2(
expand=True,
min_width=600,
columns=[
# Absolute pixel width — best for predictable, short fields.
fdt.DataColumn2(label="Name", fixed_width=140),
# Relative size S — compact, auto-fits the remaining space.
fdt.DataColumn2(label="Role", size=fdt.DataColumnSize.S),
# Relative size L — takes the lion's share of what's left.
fdt.DataColumn2(label="Recent work", size=fdt.DataColumnSize.L),
],
rows=[
ft.DataRow(
cells=[
ft.DataCell(cell_text("Alice Nakamura")),
ft.DataCell(cell_text("Engineer")),
ft.DataCell(
cell_text(
"Led the migration of our checkout service "
"to a set of composable workers, cutting "
"p99 latency in half."
)
),
]
),
ft.DataRow(
cells=[
# Longer than 140px — shows ellipsis in a fixed column.
ft.DataCell(cell_text("Bartholomew Laurent-Fitzgerald")),
ft.DataCell(cell_text("Designer")),
ft.DataCell(
cell_text(
"Rebuilt the onboarding flow and maintains "
"the internal design-system token registry."
)
),
]
),
ft.DataRow(
cells=[
ft.DataCell(cell_text("Chen")),
ft.DataCell(cell_text("PM")),
ft.DataCell(
cell_text("Owns the Platform Reliability roadmap.")
),
]
),
],
),
)
)
if __name__ == "__main__":
ft.run(main)
描述
DataTable2 继承自:ft.DataTable, LayoutControl, Control, BaseControl
提供固定标题行、可滚动的数据行,并通过 DataColumn2 和 DataRow2 带来额外的布局灵活性。
注意:DataTable2 不支持父级 DataTable 中存在的 data_row_min_height 和 data_row_max_height 属性。请改用 data_row_height。
属性
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
columns | list[Union[DataColumn2, ft.DataColumn]] | 必填 | 表格列的列表。 |
rows | list[Union[ft.DataRow, DataRow2]] | [] | 表格行的列表。 |
empty | Optional[ft.Control] | None | 没有数据行时显示的占位控件。 |
bottom_margin | Optional[ft.Number] | None | 如果设置了该值,会在最后一行之后添加间距。 |
lm_ratio | ft.Number | 1.2 | Large 列宽与 Medium 列宽的比值。 |
sm_ratio | ft.Number | 0.67 | Small 列宽与 Medium 列宽的比值。 |
fixed_left_columns | int | 0 | 左侧固定列的数量。包含复选框列(如果存在)。 |
fixed_top_rows | int | 1 | 顶部固定行的数量。默认包含标题行。 |
fixed_columns_color | Optional[ft.ColorValue] | None | 固定左侧列的背景色。 |
fixed_corner_color | Optional[ft.ColorValue] | None | 左上角固定单元格的背景色。 |
sort_arrow_icon_color | Optional[ft.ColorValue] | None | 设置后始终覆盖/优先于默认的箭头图标颜色。 |
min_width | Optional[ft.Number] | None | 开始水平滚动之前的最小表格宽度。 |
show_heading_checkbox | bool | True | 控制标题行复选框是否可见。 |
heading_checkbox_theme | Optional[ft.CheckboxTheme] | None | 覆盖标题行复选框的主题。 |
data_row_checkbox_theme | Optional[ft.CheckboxTheme] | None | 覆盖每个数据行中复选框的主题。 |
sort_arrow_icon | ft.IconData | ft.Icons.ARROW_UPWARD | 应用排序时显示的图标。 |
sort_arrow_animation_duration | ft.DurationValue | ft.Duration(milliseconds=150() | 排序箭头动画的时长。 |
visible_horizontal_scroll_bar | Optional[bool] | None | 决定水平滚动条是否可见。 |
visible_vertical_scroll_bar | Optional[bool] | None | 决定垂直滚动条是否可见。 |
checkbox_alignment | ft.Alignment | ft.Alignment.CENTER() | 复选框的对齐方式。 |
data_row_height | Optional[ft.Number] | None | 每个数据行的高度。 |
data_row_min_height | None | init=False, repr=False, compare=False, metadata={'skip': True} | — |
data_row_max_height | None | init=False, repr=False, compare=False, metadata={'skip': True} | — |
sort_ascending | bool | False | sort_column_index 中指定的列(如果有)是否按升序排序。 如果为 True,则按升序排列(也就是说,当前排序列值最小的行排在表格最前面)。 如果为 False,则按降序排列(也就是说,当前排序列值最小的行排在表格最后面)。 |
show_checkbox_column | bool | False | 控件是否应为可选中行显示复选框。 如果为 True,每个可选中行的开头都会放置一个复选框。不过,如果任何行都没有设置 on_select_change,即使此值为 True,也不会放置复选框。 如果为 False,所有行都不显示复选框。 |
sort_column_index | Optional[int] | None | 当前主排序键所在的列。 如果指定,表示所指示的列是数据排序所依据的列。该数字必须与 columns 中相关列的索引一致。 设置此值会让相关列显示排序指示器。 当此值为 None 时,表示表格的排序顺序不对应任何列。 抛出异常: ValueError:如果它相对于可见的 columns 超出范围。 |
show_bottom_border | bool | False | 是否显示表格底部的边框。 默认情况下不在底部显示边框,以便通过 decoration 定义表格周围的边框。 |
border | Optional[Border] | None | 表格的边框。 |
border_radius | Optional[BorderRadiusValue] | None | 边框圆角。 |
horizontal_lines | Optional[BorderSide] | None | 设置行之间水平线的颜色和宽度。 |
vertical_lines | Optional[BorderSide] | None | 设置列之间垂直线的颜色和宽度。 |
checkbox_horizontal_margin | Optional[Number] | None | 复选框周围(如果显示)的水平外边距。 |
column_spacing | Optional[Number] | None | 各数据列内容之间的水平外边距。 |
data_row_color | Optional[ControlStateValue[ColorValue]] | None | 数据行的背景色。 实际背景色可以取决于 ControlState 状态,即行处于选中、按下、悬停、聚焦、禁用或启用状态。该颜色作为覆盖层绘制在行上。为确保行的 InkWell 可见(在按下、悬停和聚焦时),建议使用半透明的背景色。 |
data_text_style | Optional[TextStyle] | None | 数据 rows 的文本样式。 |
bgcolor | Optional[ColorValue] | None | 此表格的背景色。 |
gradient | Optional[Gradient] | None | 此表格的背景渐变。 |
divider_thickness | Annotated[Number, V.ge(0)] | 1.0 | 出现在 rows 之间的分隔线的宽度。 抛出异常: ValueError:如果它不大于或等于 0。 |
heading_row_color | Optional[ControlStateValue[ColorValue]] | None | 标题行的背景色。 实际背景色可以取决于 ControlState 状态,即排序时行处于按下、悬停、聚焦状态。该颜色作为覆盖层绘制在行上。为确保行的 InkWell 可见(在按下、悬停和聚焦时),建议使用半透明颜色。 |
heading_row_height | Optional[Number] | None | 标题行的高度。 |
heading_text_style | Optional[TextStyle] | None | 标题行的文本样式。 |
horizontal_margin | Optional[Number] | None | 此表格边缘与每行第一个和最后一个单元格内容之间的水平外边距。 当显示复选框时,它同时也是复选框与第一个数据列内容之间的外边距。 |
clip_behavior | ClipBehavior | ClipBehavior.NONE | 定义此表格的内容如何被裁剪。 |
width | Optional[Number] | None | 控件的强制宽度(以虚拟像素为单位)。 |
height | Optional[Number] | None | 控件的强制高度(以虚拟像素为单位)。 |
left | Optional[Number] | None | 子控件左边缘相对于 Stack 左侧的内缩距离。 注意: 仅当此控件是以下之一的后代时才生效: Stack 控件、overlay 列表。 |
top | Optional[Number] | None | 子控件上边缘相对于 Stack 顶部的内缩距离。 注意: 仅当此控件是以下之一的后代时才生效: Stack 控件、overlay 列表。 |
right | Optional[Number] | None | 子控件右边缘相对于 Stack 右侧的内缩距离。 注意: 仅当此控件是以下之一的后代时才生效: Stack 控件、overlay 列表。 |
bottom | Optional[Number] | None | 子控件下边缘相对于 Stack 底部的内缩距离。 注意: 仅当此控件是以下之一的后代时才生效: Stack 控件、overlay 列表。 |
align | Optional[Alignment] | None | 控件在其父级中的对齐方式。 |
margin | Optional[MarginValue] | None | 设置控件的外边距。 |
rotate | Optional[RotateValue] | None | 围绕控件中心旋转来变换此控件。 rotate 属性的值可以是以下类型之一: * number —— 顺时针旋转的弧度值。整圈 360° 为 math.pi * 2 弧度,90° 为 pi / 2,45° 为 pi / 4,以此类推。 * Rotate —— 允许同时指定旋转 angle 和 alignment,即旋转中心的位置。 示例: python ft.Image( src="https://picsum.photos/100/100", width=100, height=100, border_radius=5, rotate=Rotate(angle=0.25 * pi, alignment=ft.Alignment.CENTER_LEFT) ) |
scale | Optional[ScaleValue] | None | 沿二维平面缩放此控件。默认缩放因子为 1.0,表示不缩放。 例如,将此属性设为 0.5 会让控件缩小一半,而设为 2.0 则会放大一倍。 通过将 Control.scale 属性设为 Scale 类的实例,可以为 x 和 y 轴指定不同的缩放倍数。可以指定 scale,或者指定 scale_x 和 scale_y,但不能同时指定全部。 示例: python ft.Image( src="https://picsum.photos/100/100", width=100, height=100, border_radius=5, scale=ft.Scale(scale_x=2, scale_y=0.5) ) |
offset | Optional[OffsetValue] | None | 在绘制控件之前应用平移变换。 平移量表示为按控件尺寸缩放的 Offset。例如,Offset(x=0.25, y=0) 会使控件沿水平方向平移其宽度的四分之一。 示例 下面的示例把容器显示在 Stack 的 0, 0 左上角,因为变换对控件应用了 -1 * 100, -1 * 100(即 offset * control's size)的水平与垂直平移: python import flet as ft def main(page: ft.Page): page.add( ft.Stack( width=1000, height=1000, controls=[ ft.Container( bgcolor=ft.Colors.RED, width=100, height=100, left=100, top=100, offset=ft.Offset(-1, -1), ) ], ) ) ft.run(main) |
flip | Optional[Flip] | None | 水平方向和/或垂直方向翻转此控件。 设为 Flip 的实例即可沿 x 轴、y 轴或两者镜像翻转。 |
transform | Optional[Transform] | None | 对此控件应用通用矩阵变换。 设为带 Matrix4 记录的 Transform 实例,用以描述任意的变换序列。 |
aspect_ratio | Optional[Number] | None | 控件的宽高比,定义为 width 与 height 的比值。 注意: 在当前实现中,如果设置了 aspect_ratio,同一控件上的 width 和 height 将在最终渲染尺寸中被忽略。 |
animate_opacity | Optional[AnimationValue] | None | 启用 opacity 属性的隐式动画。 更多信息见此处。 |
animate_size | Optional[AnimationValue] | None | 启用此控件尺寸的隐式动画。 如果设置了 width 或 height,则这些维度的变化会以动画呈现。如果两者都未设置,则对控件自然尺寸的变化进行动画。 更多信息见此处。 |
animate_position | Optional[AnimationValue] | None | 启用定位属性(left、right、top 和 bottom)的隐式动画。 更多信息见此处。 |
animate_align | Optional[AnimationValue] | None | 启用 align 属性的隐式动画。 更多信息见此处。 |
animate_margin | Optional[AnimationValue] | None | 启用 margin 属性的隐式动画。 更多信息见此处。 |
animate_rotation | Optional[AnimationValue] | None | 启用 rotate 属性的隐式动画。 更多信息见此处。 |
animate_scale | Optional[AnimationValue] | None | 启用 scale 属性的隐式动画。 更多信息见此处。 |
animate_offset | Optional[AnimationValue] | None | 启用 offset 属性的隐式动画。 更多信息见此处。 |
size_change_interval | int | 10 | on_size_change 事件的采样间隔(毫秒)。 设为 0 时,每次变化都会立即调用 on_size_change。 |
expand | Annotated[Optional[Union[bool, int]], V.instance_of((bool, int))] | None | 指定此控件是否/如何扩展以填满父布局中的可用空间。 更多信息见此处。 注意: 仅当此控件的直接父级是以下控件或其子类时才生效:Column、Row、View、Page。 抛出异常: ValueError:如果它不是 bool 或 int 类型。 |
expand_loose | bool | False | 允许控件在主轴方向有可用空间时扩展,但不要求它填满所有可用空间。 更多信息见此处。 注意: 如果 expand_loose 为 True,则仅在以下条件满足时才生效: - expand 不为 None,且 - 此控件的直接父级是以下控件或其子类之一:Column、Row、View、Page。 |
col | ResponsiveNumber | 12 | 如果此控件的父级是 ResponsiveRow,则此属性决定该控件在屏幕上跨越多少虚拟列。 可以是一个数字,也可以是一个按断点配置不同值的字典,例如 col={"sm": 6}。 值为 0 时会在该断点隐藏控件,因此它不会占用父级 ResponsiveRow 中的任何列。 此控件默认跨越 12 个虚拟列。 | 断点 | 尺寸 | |---|---| | xs | <576px | | sm | ≥576px | | md | ≥768px | | lg | ≥992px | | xl | ≥1200px | | xxl | ≥1400px | |
opacity | Annotated[Number, V.between(0.0, 1.0)] | 1.0 | 定义控件的透明度。 取值范围从 0.0(完全透明)到 1.0(完全不透明,无任何透明感)。 抛出异常: ValueError:如果它不在 0.0 到 1.0(含)之间。 |
tooltip | Optional[TooltipValue] | None | 鼠标悬停在此控件上时显示的工具提示。 |
badge | Optional[BadgeValue] | None | 显示在此控件顶部的徽章。 |
visible | bool | True | 每个控件都有 visible 属性,默认为 True —— 控件会渲染在页面上。将 visible 设为 False 会彻底阻止控件(及其所有子控件,如果有)渲染到页面画布上。隐藏的控件无法通过键盘或鼠标获得焦点或选中,也不会触发任何事件。 |
disabled | bool | False | 每个控件都有 disabled 属性,默认为 False —— 控件及其所有子控件都处于启用状态。 注意: 此属性的值会递归向下传播到所有子控件。 示例: 例如,如果你有一个包含多个输入控件的表单,可以通过禁用容器来一并禁用它们: python ft.Column( disabled = True, controls=[ ft.TextField(), ft.TextField() ] ) |
rtl | bool | False | 控件的文本方向是否为从右到左(RTL)。 |
data | Any | skip_ | 任意类型的自定义数据。 |
key | Optional['KeyValue'] | None | 用于在更新之间保持控件标识稳定的键。 |
只读属性
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
parent | Optional['BaseControl'] | — | 此控件的直接祖先(父级)。 默认为 None,只有在此控件被挂载(加入页面树)后才有值。 Page 控件(页面树的根)是个例外 —— 它的 parent 始终为 None。 |
page | 'Page' | — | 此控件所属的页面。 |
事件
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
on_select_all | Optional[ControlEventHandler['DataTable']] | None | 当用户使用标题行中的复选框选中或取消选中每一行时调用。 如果此值为 None,则会改为相应地调用此表格每一个 rows 的 on_select_change 回调。 提示: 要控制某一行是否可选中,请参见 on_select_change。只有当任何行可选中时,此回调才有意义。 |
on_size_change | Optional[EventHandler[LayoutSizeChangeEvent['LayoutControl']]] | None | 当此控件的尺寸发生变化时调用。 size_change_interval 定义此事件的调用频率。 |
on_animation_end | Optional[ControlEventHandler['LayoutControl']] | None | 动画完成时调用。 可用于串联多个动画。 事件处理器参数的 data 属性包含动画名称。 更多信息见此处。 |
方法
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
before_update | — | — | — |
is_isolated | — | — | 返回此控件是否被标记为隔离。 隔离控件会被排除在父级驱动的更新遍历之外,并需要自行管理更新边界。 |
init | — | — | 在控件实例初始化之后、首次 build / update 循环之前调用。 重写此钩子可在依赖已初始化字段时执行轻量级设置。不要在此处调用 update()。 |
build | — | — | 在控件初始化期间调用一次,用于定义其子控件。在此方法中 page 属性可用。 |
before_event | — | — | 在事件处理器执行之前拦截事件。 返回 False 可取消分发。返回 True 或 None 则继续正常的事件处理。 |
did_mount | — | — | 在控件挂载到页面树之后调用。 重写此方法以启动需要已挂载页面的资源,例如订阅、定时器或服务监听器。 |
will_unmount | — | — | 在控件从页面树移除之前调用。 重写此方法以释放 did_mount() 中创建的资源,例如订阅、定时器或外部句柄。 |
get_data_channel | — | — | 解析为此 widget 在 Dart 侧分配的 [DataChannel]。模式: on_data_channel_open: Optional[ft.EventHandler[DataChannelOpenEvent]] = None def init(self): self.on_data_channel_open = self._on_open def _on_open(self, e): self._channel = self.get_data_channel(e.channel_id) 幂等 —— 底层的 Connection 会按 id 缓存 DataChannel,因此重复调用会返回同一实例。没有错误路径:该 id 始终来自框架触发的事件,所以当此方法运行时,通道在两端都已存在。 |
update | None | — | 为此控件请求一次 UI 更新。 在更改控件状态或属性后调用。控件必须已挂载到页面且未被标记为冻结。 |