主题
Markdown 继承自:LayoutControl, Control, BaseControl
以 Markdown 格式渲染文本。
python
ft.Markdown(
value="# Welcome\n\nThis is **Markdown** rendered in Flet.",
width=260,
)示例
python
import flet as ft
sample = """
# Markdown Example
Markdown allows you to easily include formatted text, images, and even formatted Dart
code in your app.
## Titles
Setext-style
This is an H1
=============
This is an H2
-------------
Atx-style
# This is an H1
## This is an H2
###### This is an H6
Select the valid headers:
- [x] `# hello`
- [ ] `#hello`
## Links
[inline-style](https://www.google.com)
## Images


## Tables
|Syntax |Result |
|---------------------------------------|-------------------------------------|
|`*italic 1*` |*italic 1* |
|`_italic 2_` | _italic 2_ |
|`**bold 1**` |**bold 1** |
|`__bold 2__` |__bold 2__ |
|`This is a ~~strikethrough~~` |This is a ~~strikethrough~~ |
|`***italic bold 1***` |***italic bold 1*** |
|`___italic bold 2___` |___italic bold 2___ |
|`***~~italic bold strikethrough 1~~***`|***~~italic bold strikethrough 1~~***|
|`~~***italic bold strikethrough 2***~~`|~~***italic bold strikethrough 2***~~|
## Styling
Style text as _italic_, __bold__, ~~strikethrough~~, or `inline code`.
- Use bulleted lists
- To better clarify
- Your points
## Code blocks
Formatted Dart code looks really pretty too:
~~~dart
void main() {
runApp(MaterialApp(
home: Scaffold(
body: ft.Markdown(data: markdownData),
),
));
}
~~~
"""
def main(page: ft.Page):
page.scroll = ft.ScrollMode.AUTO
async def handle_link_tap(e: ft.Event[ft.Markdown]):
await ft.UrlLauncher().launch_url(e.data)
page.add(
ft.SafeArea(
content=ft.Markdown(
value=sample,
selectable=True,
extension_set=ft.MarkdownExtensionSet.GITHUB_WEB,
on_tap_link=handle_link_tap,
),
)
)
if __name__ == "__main__":
ft.run(main)
python
import flet as ft
sample = """
# Flet
<img src="https://raw.githubusercontent.com/flet-dev/flet/refs/heads/main/media/logo/flet-logo.svg" width="50%"/>
Flet is a framework for adding server-driven UI (SDUI) experiences to existing Flutter
apps or building standalone web, mobile and desktop apps with Flutter UI.
Add an interactive `FletApp` widget to your Flutter app whose content is controlled by a remote Python script.
It is an ideal solution for building non-core or frequently changing functionality such as product catalog, feedback form, in-app survey or support chat. Flet enables your team to ship new features faster by reducing the number of App Store validation cycles. Just re-deploy a web app hosting a Python script and your users will get an instant update!
On the server side Flet provides an easy to learn programming model that enables Python developers without prior Flutter (or even front-end) experience to participate in development of your larger Flutter app or build their own apps with Flutter UI from scratch.
## Getting started with Flet
### Install `flet` Python module
Flet requires Python 3.7 or above. To start with Flet, you need to install flet module first:
~~~bash
pip install flet
~~~
### Create Python program
Create a new Python program using Flet which will be driving the content of `FletApp` widget.
Let's do a simple `counter.py` app similar to a Flutter new project template:
~~~python
import flet
from flet import IconButton, Page, Row, TextField, icons
def main(page: Page):
page.title = "Flet counter example"
page.vertical_alignment = "center"
txt_number = TextField(value="0", text_align="right", width=100)
def minus_click(e):
txt_number.value = int(txt_number.value) - 1
page.update()
def plus_click(e):
txt_number.value = int(txt_number.value) + 1
page.update()
page.add(
Row(
[
IconButton(icons.REMOVE, on_click=minus_click),
txt_number,
IconButton(icons.ADD, on_click=plus_click),
],
alignment="center",
)
)
flet.app(main, port=8550)
~~~
Run the app:
~~~bash
python counter.py
~~~
You should see the app running in a native OS window.
There is a web server (Fletd) running in the background on a fixed port `8550`. Fletd web server is a "bridge" between Python and Flutter.
`FletApp` widget in your Flutter application will be communicating with Fletd web server via WebSockets to receive UI updates and send user-generated UI events.
For production use Python app along with Fletd could be [deployed to a public web host](/publishing/web/dynamic-website/) and be accessible via HTTPS with domain name.
### Add Flet widget to a Flutter app
Create a new or open existing Flutter project.
Install Flutter `flet` package:
~~~bash
flutter pub add flet
~~~
For a new project replace `main.dart` with the following:
~~~dart
import 'package:flet/flet.dart';
import 'package:flutter/material.dart';
void main() async {
await setupDesktop();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flet Flutter Demo',
home: FletApp(pageUrl: "http://localhost:8550"),
);
}
}
~~~
In the app above `FletApp` widget is hosted inside `MaterialApp` widget.
If Flet app must be able to handle page route change events (web browser URL changes, mobile app deep linking) it must be the top most widget as it contains its own `MaterialApp` widget handling route changes:
~~~dart
import 'package:flet/flet.dart';
import 'package:flutter/material.dart';
void main() async {
await setupDesktop();
runApp(const FletApp(pageUrl: "http://localhost:8550"));
}
~~~
Run the program and see Flet app running inside a Flutter app.
When adding `FletApp` widget to the existing desktop Flutter app make sure `setupDesktop()` is called before `runApp()` to initialize Flet's built-in window manager.
## Flet learning resources
* [Getting started for Python](/installation)
* [Controls reference](https://flet.dev/docs/controls)
* [Tutorials](https://flet.dev/docs/tutorials)
* [Examples](https://github.com/flet-dev/examples/tree/main/python)
## Flet community
* [Discussions](https://github.com/flet-dev/flet/discussions)
* [Discord](https://discord.gg/dzWXP8SHG8)
* [Twitter](https://twitter.com/fletdev)
* [Email](mailto:hello@flet.dev)
## FAQ
Coming soon.
## Adding custom Flutter widgets
Coming soon.
""" # noqa: E501
def main(page: ft.Page):
page.scroll = ft.ScrollMode.AUTO
page.fonts = {
"Roboto Mono": "RobotoMono-VariableFont_wght.ttf",
}
async def navigate_md_link(e: ft.Event[ft.Markdown]):
await ft.UrlLauncher().launch_url(e.data)
page.add(
ft.SafeArea(
content=ft.Markdown(
value=sample,
selectable=True,
extension_set=ft.MarkdownExtensionSet.GITHUB_WEB,
code_theme=ft.MarkdownCodeTheme.ATOM_ONE_DARK,
code_style_sheet=ft.MarkdownStyleSheet(
code_text_style=ft.TextStyle(font_family="Roboto Mono")
),
on_tap_link=navigate_md_link,
),
)
)
if __name__ == "__main__":
ft.run(main)
python
import flet as ft
def main(page: ft.Page):
page.theme_mode = ft.ThemeMode.DARK
def change_theme_mode(e: ft.Event[ft.Switch]):
if page.theme_mode == ft.ThemeMode.DARK:
page.theme_mode = ft.ThemeMode.LIGHT
switch.thumb_icon = ft.Icons.LIGHT_MODE
else:
switch.thumb_icon = ft.Icons.DARK_MODE
page.theme_mode = ft.ThemeMode.DARK
page.update()
switch = ft.Switch(thumb_icon=ft.Icons.DARK_MODE, on_change=change_theme_mode)
page.add(
ft.SafeArea(
content=ft.Row(
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
controls=[
ft.Container(
bgcolor="#550000",
padding=20,
theme=ft.Theme(
text_theme=ft.TextTheme(
body_medium=ft.TextStyle(color=ft.Colors.WHITE),
body_large=ft.TextStyle(color=ft.Colors.WHITE),
body_small=ft.TextStyle(color=ft.Colors.WHITE),
)
),
content=ft.Markdown("I can read this!"),
),
ft.Container(
padding=ft.Padding.only(bottom=50),
alignment=ft.Alignment.TOP_RIGHT,
content=switch,
),
],
),
)
)
if __name__ == "__main__":
ft.run(main)属性
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | str | '' | 要渲染的 Markdown 内容。 |
selectable | bool | False | 渲染后的文本是否可选中。 |
extension_set | MarkdownExtensionSet | MarkdownExtensionSet.NONE | 渲染 Markdown 内容时使用的扩展集。 |
code_theme | Optional[Union[MarkdownCodeTheme, MarkdownCustomCodeTheme]] | None | 代码块的语法高亮主题。默认为 MarkdownCodeTheme.GITHUB。 |
auto_follow_links | bool | False | 是否自动打开文档中的 URL。如果已注册,之后会触发 on_tap_link 事件。 |
shrink_wrap | bool | True | 滚动视图中滚动方向的范围是否应由所查看的内容决定。 |
fit_content | bool | True | 是否允许控件适应子内容。 |
soft_line_break | bool | False | 软换行用于标识一行文本末尾的空格以及紧随其后一行文本开头的空格。 |
auto_follow_links_target | Optional[UrlTarget] | None | 在 Web 模式下打开 URL 的位置。 |
image_error_content | Optional[Control] | None | 图像加载失败时显示的控件。 |
code_style_sheet | Optional[MarkdownStyleSheet] | None | 显示代码块时使用的样式。 |
md_style_sheet | Optional[MarkdownStyleSheet] | None | 显示 Markdown 时使用的样式。 |
latex_scale_factor | Optional[float] | None | LaTeX 公式渲染的缩放因子。控制渲染出的数学表达式的大小。 |
latex_style | Optional[TextStyle] | None | 应用于 LaTeX 公式的文本样式。可自定义数学表达式的字体、颜色及其他文本属性。 |
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_tap_text | Optional[ControlEventHandler['Markdown']] | None | 当某段文本被点击/点按时调用。 |
on_selection_change | Optional[EventHandler[TextSelectionChangeEvent['Markdown']]] | None | 当文本选区发生变化时调用。 |
on_tap_link | Optional[ControlEventHandler['Markdown']] | None | 当 Markdown 文档中的链接被点击/点按时调用。事件处理器参数的 data 属性包含被点击的 URL。示例:https://github.com/flet-dev/examples/blob/main/python/controls/information-displays/markdown/markdown-event-example.py |
on_size_change | Optional[EventHandler[LayoutSizeChangeEvent['LayoutControl']]] | None | 当此控件的尺寸发生变化时调用。size_change_interval 定义此事件的调用频率。 |
on_animation_end | Optional[ControlEventHandler['LayoutControl']] | None | 动画完成时调用。可用于串联多个动画。事件处理器参数的 data 属性包含动画名称。更多信息见此处。 |
方法
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
is_isolated | — | — | 返回此控件是否被标记为隔离。隔离控件会被排除在父级驱动的更新遍历之外,并需要自行管理更新边界。 |
init | — | — | 在控件实例初始化之后、首次 build/update 周期之前调用。重写此钩子以执行依赖已初始化字段的轻量级设置。不要在此处调用 update()。 |
build | — | — | 在控件初始化期间调用一次,用于定义其子控件。在此方法中 page 属性可用。 |
before_update | — | — | 每当此控件被更新时都会调用此方法。注意:务必不要在此处调用或请求 update()。 |
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 更新。在更改控件状态或属性后调用。控件必须已挂载到页面且未被标记为冻结。 |