Skip to content

在你的 Flet 应用中嵌入功能完整的视频播放器,支持播放列表、硬件加速控制和字幕配置。

它由 Flutter 的 media_kit 包提供支持。

平台支持 ​

平台WindowsmacOSLinuxiOSAndroidWeb
支持✅✅✅✅✅✅

用法 ​

把 flet-video 包添加到项目依赖中:

uv

bash
uv add flet-video

pip

bash
pip install flet-video  [^1]

[^1]: 之后你需要手动把这个包添加到 requirements.txt 或 pyproject.toml 中。

环境要求 ​

以下各节展示了各平台所需的配置。

Android ​

不需要任何权限即可播放来自 URL、打包的静态资源,或用户通过 FilePicker 选取的文件中的媒体 —— 系统选择器会授予对所选取文件的访问权。

只有当你的应用直接从设备共享媒体库读取媒体(而不是让用户逐个选择文件)时,才需要媒体权限:

Google Play 政策

Google Play 的照片和视频权限政策会拒绝目标为 Android 13(API 33)或更高版本、且声明了 READ_MEDIA_IMAGES 或 READ_MEDIA_VIDEO 而系统选择器已足够使用的应用。 在任何生效的 version code 中声明它们 —— 包括内部测试和封闭测试 轨道 —— 都足以阻止发布。

除非你确实需要宽泛的媒体库访问权限,否则请使用 FilePicker; 若确实需要,你还必须在 Play Console 中提交照片选择器声明 表。

bash
flet build apk \
  --android-permissions android.permission.READ_MEDIA_AUDIO=true \
  --android-permissions android.permission.READ_MEDIA_VIDEO=true
toml
[tool.flet.android.permission]
"android.permission.READ_MEDIA_AUDIO" = true
"android.permission.READ_MEDIA_VIDEO" = true
"android.permission.READ_EXTERNAL_STORAGE" = { maxSdkVersion = "32" }

使用 PermissionHandler 在运行时请求权限。

另见:

Linux ​

libmpv 库必须已安装,并存在于运行应用的机器上。

在 Ubuntu/Debian 上,可以这样安装:

bash
sudo apt install libmpv-dev mpv

如果遇到 libmpv.so.1 加载错误,请运行:

bash
sudo apt update
sudo apt install libmpv-dev libmpv2
sudo ln -s /usr/lib/x86_64-linux-gnu/libmpv.so /usr/lib/libmpv.so.1

示例 ​

python
import flet as ft
import flet_video as ftv


def main(page: ft.Page):
    page.add(
        ft.SafeArea(
            expand=True,
            content=ftv.Video(
                expand=True,
                autoplay=True,
                playlist=[
                    ftv.VideoMedia(
                        "https://user-images.githubusercontent.com/28951144/229373720-14d69157-1a56-4a78-a2f4-d7a134d7c3e9.mp4"
                    ),
                ],
            ),
        )
    )


if __name__ == "__main__":
    ft.run(main)
python
import flet as ft
import flet_video as ftv


def main(page: ft.Page):
    async def handle_play(e: ft.Event[ft.Button]):
        await video.play()

    async def handle_play_or_pause(e: ft.Event[ft.Button]):
        await video.play_or_pause()

    async def handle_pause(e: ft.Event[ft.Button]):
        await video.pause()

    async def handle_stop(e: ft.Event[ft.Button]):
        await video.stop()

    async def handle_seek(e: ft.Event[ft.Button]):
        await video.seek(ft.Duration(seconds=10))

    async def handle_next(e: ft.Event[ft.Button]):
        await video.next()

    async def handle_previous(e: ft.Event[ft.Button]):
        await video.previous()

    async def handle_status(e: ft.Event[ft.Button]):
        playing = await video.is_playing()
        position = (await video.get_current_position()).in_seconds
        duration = (await video.get_duration()).in_seconds
        page.show_dialog(
            ft.SnackBar(
                f"Playing?: {playing} | Duration: {duration}s | Position: {position}s"
            )
        )

    page.add(
        ft.SafeArea(
            expand=True,
            content=ft.Column(
                expand=True,
                horizontal_alignment=ft.CrossAxisAlignment.CENTER,
                controls=[
                    video := ftv.Video(
                        expand=True,
                        playlist=[
                            ftv.VideoMedia(
                                "https://user-images.githubusercontent.com/28951144/229373720-14d69157-1a56-4a78-a2f4-d7a134d7c3e9.mp4"
                            ),
                            ftv.VideoMedia(
                                "https://user-images.githubusercontent.com/28951144/229373718-86ce5e1d-d195-45d5-baa6-ef94041d0b90.mp4"
                            ),
                            ftv.VideoMedia(
                                "https://user-images.githubusercontent.com/28951144/229373716-76da0a4e-225a-44e4-9ee7-3e9006dbc3e3.mp4"
                            ),
                        ],
                    ),
                    ft.Row(
                        wrap=True,
                        alignment=ft.MainAxisAlignment.CENTER,
                        controls=[
                            ft.Button("Play", on_click=handle_play),
                            ft.Button("Play Or Pause", on_click=handle_play_or_pause),
                            ft.Button("Pause", on_click=handle_pause),
                            ft.Button("Stop", on_click=handle_stop),
                            ft.Button("Seek 10s", on_click=handle_seek),
                            ft.Button("Previous", on_click=handle_previous),
                            ft.Button("Next", on_click=handle_next),
                            ft.Button("Show Status", on_click=handle_status),
                        ],
                    ),
                ],
            ),
        )
    )


if __name__ == "__main__":
    ft.run(main)
python
import flet as ft
import flet_video as ftv


def main(page: ft.Page):
    async def handle_screenshot(e: ft.Event[ft.FloatingActionButton]):
        image = await video.take_screenshot(format="image/png")
        if image is None:
            status.value = "No video frame is available yet."
        else:
            preview.content = ft.Image(
                src=image,
                width=260,
                height=146,
                fit=ft.BoxFit.CONTAIN,
            )
            status.value = f"Captured {len(image)} bytes."
        page.update()

    page.floating_action_button = ft.FloatingActionButton(
        content="Take screenshot",
        icon=ft.Icons.CAMERA_ALT,
        on_click=handle_screenshot,
    )
    page.add(
        ft.SafeArea(
            expand=True,
            content=ft.Column(
                expand=True,
                horizontal_alignment=ft.CrossAxisAlignment.CENTER,
                controls=[
                    video := ftv.Video(
                        expand=True,
                        autoplay=True,
                        playlist=[
                            ftv.VideoMedia(
                                "https://user-images.githubusercontent.com/28951144/229373720-14d69157-1a56-4a78-a2f4-d7a134d7c3e9.mp4"
                            ),
                        ],
                    ),
                    preview := ft.Container(
                        width=260,
                        height=146,
                        alignment=ft.Alignment.CENTER,
                        bgcolor=ft.Colors.BLACK_12,
                        content=ft.Text("No screenshot yet"),
                    ),
                    status := ft.Text(),
                ],
            ),
        )
    )


if __name__ == "__main__":
    ft.run(main)
python
import random

import flet as ft
import flet_video as ftv

media = [
    ftv.VideoMedia(
        "https://user-images.githubusercontent.com/28951144/229373720-14d69157-1a56-4a78-a2f4-d7a134d7c3e9.mp4"
    ),
    ftv.VideoMedia(
        "https://user-images.githubusercontent.com/28951144/229373718-86ce5e1d-d195-45d5-baa6-ef94041d0b90.mp4"
    ),
    ftv.VideoMedia(
        "https://user-images.githubusercontent.com/28951144/229373716-76da0a4e-225a-44e4-9ee7-3e9006dbc3e3.mp4"
    ),
    ftv.VideoMedia(
        "https://user-images.githubusercontent.com/28951144/229373695-22f88f13-d18f-4288-9bf1-c3e078d83722.mp4"
    ),
]


def main(page: ft.Page):
    def handle_add(e: ft.Event[ft.Button]):
        video.playlist.append(random.choice(media))

    def handle_remove(e: ft.Event[ft.Button]):
        if video.playlist:
            video.playlist.pop(random.randint(0, len(video.playlist) - 1))

    def handle_replace(e: ft.Event[ft.Button]):
        video.playlist = random.sample(media, 3)

    async def handle_next(e: ft.Event[ft.Button]):
        await video.next()

    async def handle_previous(e: ft.Event[ft.Button]):
        await video.previous()

    async def handle_jump(e: ft.Event[ft.Button]):
        await video.jump_to(0)

    page.add(
        ft.SafeArea(
            expand=True,
            content=ft.Column(
                expand=True,
                horizontal_alignment=ft.CrossAxisAlignment.CENTER,
                controls=[
                    video := ftv.Video(
                        expand=True,
                        autoplay=True,
                        playlist=media[:2],
                        playlist_mode=ftv.PlaylistMode.LOOP,
                    ),
                    ft.Row(
                        wrap=True,
                        alignment=ft.MainAxisAlignment.CENTER,
                        controls=[
                            ft.Button("Add Random", on_click=handle_add),
                            ft.Button("Remove Random", on_click=handle_remove),
                            ft.Button("Replace Playlist", on_click=handle_replace),
                            ft.Button("Next", on_click=handle_next),
                            ft.Button("Previous", on_click=handle_previous),
                            ft.Button("Jump to First", on_click=handle_jump),
                        ],
                    ),
                ],
            ),
        )
    )


if __name__ == "__main__":
    ft.run(main)
python
import flet as ft
import flet_video as ftv


def main(page: ft.Page):
    def handle_load(e: ft.Event[ftv.Video]):
        print("Video loaded")

    def handle_complete(e: ft.Event[ftv.Video]):
        print(f"Track completed: {e.data}")

    def handle_track_change(e: ft.Event[ftv.Video]):
        print(f"Track changed to index {e.data}")

    def handle_position_change(e: ft.Event[ftv.Video]):
        print(f"Position: {e.data.in_milliseconds / 1000:.3f}s")

    def handle_duration_change(e: ft.Event[ftv.Video]):
        print(f"Duration changed: {e.data.in_seconds}s")

    def handle_error(e: ft.Event[ftv.Video]):
        print(f"Error: {e.data}")

    def handle_enter_fullscreen(e: ft.Event[ftv.Video]):
        print("Entered fullscreen")

    def handle_exit_fullscreen(e: ft.Event[ftv.Video]):
        print("Exited fullscreen")

    page.add(
        ft.SafeArea(
            expand=True,
            content=ft.Column(
                expand=True,
                controls=[
                    ftv.Video(
                        expand=True,
                        autoplay=True,
                        on_load=handle_load,
                        on_complete=handle_complete,
                        on_track_change=handle_track_change,
                        on_position_change=handle_position_change,
                        on_duration_change=handle_duration_change,
                        on_error=handle_error,
                        on_enter_fullscreen=handle_enter_fullscreen,
                        on_exit_fullscreen=handle_exit_fullscreen,
                        playlist=[
                            ftv.VideoMedia(
                                "https://user-images.githubusercontent.com/28951144/229373720-14d69157-1a56-4a78-a2f4-d7a134d7c3e9.mp4"
                            ),
                            ftv.VideoMedia(
                                "https://user-images.githubusercontent.com/28951144/229373718-86ce5e1d-d195-45d5-baa6-ef94041d0b90.mp4"
                            ),
                        ],
                    ),
                ],
            ),
        )
    )


if __name__ == "__main__":
    ft.run(main)
python
import flet as ft
import flet_video as ftv

SAMPLE_VTT = """WEBVTT

00:00:00.000 --> 00:00:04.000
Welcome to flet-video!

00:00:04.000 --> 00:00:08.000
Subtitles are styled via VideoSubtitleConfiguration.

00:00:08.000 --> 00:00:14.000
This track is provided as raw VTT text.
"""


def main(page: ft.Page):
    page.spacing = 20
    page.horizontal_alignment = ft.CrossAxisAlignment.CENTER

    subtitle_track = ftv.VideoSubtitleTrack(
        src=SAMPLE_VTT,
        title="English",
        language="en",
    )

    def handle_change(e: ft.Event[ft.Switch]):
        if e.control.value:
            video.subtitle_track = subtitle_track
        else:
            video.subtitle_track = ftv.VideoSubtitleTrack.none()

    page.add(
        ft.SafeArea(
            expand=True,
            content=ft.Column(
                expand=True,
                horizontal_alignment=ft.CrossAxisAlignment.CENTER,
                controls=[
                    video := ftv.Video(
                        expand=True,
                        autoplay=False,
                        playlist=[
                            ftv.VideoMedia(
                                "https://user-images.githubusercontent.com/28951144/229373720-14d69157-1a56-4a78-a2f4-d7a134d7c3e9.mp4"
                            ),
                        ],
                        subtitle_track=subtitle_track,
                        subtitle_configuration=ftv.VideoSubtitleConfiguration(
                            text_align=ft.TextAlign.CENTER,
                            text_style=ft.TextStyle(
                                size=24,
                                color=ft.Colors.YELLOW,
                                weight=ft.FontWeight.BOLD,
                                bgcolor=ft.Colors.BLACK_54,
                            ),
                        ),
                    ),
                    ft.Switch(
                        value=True, label="Show Subtitles", on_change=handle_change
                    ),
                ],
            ),
        )
    )


if __name__ == "__main__":
    ft.run(main)
python
import flet as ft
import flet_video as ftv


def main(page: ft.Page):
    def set_controls(
        value: ftv.VideoControls | ft.Control | None,
    ):
        video.controls = value

    page.add(
        ft.SafeArea(
            expand=True,
            content=ft.Column(
                expand=True,
                horizontal_alignment=ft.CrossAxisAlignment.CENTER,
                controls=[
                    video := ftv.Video(
                        expand=True,
                        playlist=[
                            ftv.VideoMedia(
                                "https://user-images.githubusercontent.com/28951144/229373720-14d69157-1a56-4a78-a2f4-d7a134d7c3e9.mp4"
                            ),
                        ],
                        controls=ftv.MaterialDesktopVideoControls(
                            visible_on_mount=True,
                            display_seek_bar=True,
                            modify_volume_on_scroll=True,
                            toggle_fullscreen_on_double_press=True,
                            play_and_pause_on_tap=True,
                            seek_bar_position_color=ft.Colors.BLUE,
                            volume_bar_active_color=ft.Colors.YELLOW,
                            controls_hover_duration=ft.Duration(seconds=5),
                        ),
                    ),
                    ft.Row(
                        wrap=True,
                        alignment=ft.MainAxisAlignment.CENTER,
                        controls=[
                            ft.Button(
                                "Adaptive",
                                on_click=lambda _: set_controls(
                                    ftv.AdaptiveVideoControls(
                                        material=ftv.MaterialVideoControls(
                                            visible_on_mount=True,
                                            display_seek_bar=True,
                                            volume_gesture=True,
                                            brightness_gesture=True,
                                            seek_gesture=True,
                                            seek_on_double_tap=True,
                                            speed_up_on_long_press=True,
                                            seek_bar_position_color=ft.Colors.BLUE,
                                            button_bar_button_color="#E0F7FA",
                                        ),
                                        material_desktop=ftv.MaterialDesktopVideoControls(
                                            visible_on_mount=True,
                                            display_seek_bar=True,
                                            modify_volume_on_scroll=True,
                                            toggle_fullscreen_on_double_press=True,
                                            play_and_pause_on_tap=True,
                                            hide_mouse_on_controls_removal=True,
                                            seek_bar_position_color=ft.Colors.BLUE,
                                            volume_bar_active_color=ft.Colors.YELLOW,
                                        ),
                                    )
                                ),
                            ),
                            ft.Button(
                                "Material",
                                on_click=lambda _: set_controls(
                                    ftv.MaterialVideoControls(
                                        visible_on_mount=True,
                                        volume_gesture=True,
                                        brightness_gesture=True,
                                        seek_gesture=True,
                                        seek_on_double_tap=True,
                                        speed_up_on_long_press=True,
                                        speed_up_factor=2.5,
                                        controls_transition_duration=ft.Duration(
                                            milliseconds=450
                                        ),
                                        seek_bar_position_color=ft.Colors.BLUE,
                                        button_bar_button_color="#E0F7FA",
                                    )
                                ),
                            ),
                            ft.Button(
                                "Material Desktop",
                                on_click=lambda _: set_controls(
                                    ftv.MaterialDesktopVideoControls(
                                        visible_on_mount=True,
                                        display_seek_bar=True,
                                        modify_volume_on_scroll=True,
                                        toggle_fullscreen_on_double_press=True,
                                        play_and_pause_on_tap=True,
                                        hide_mouse_on_controls_removal=True,
                                        controls_hover_duration=ft.Duration(seconds=5),
                                        seek_bar_position_color=ft.Colors.BLUE,
                                        seek_bar_hover_height=8,
                                        volume_bar_active_color=ft.Colors.YELLOW,
                                    )
                                ),
                            ),
                            ft.Button(
                                "Custom Control",
                                on_click=lambda _: set_controls(
                                    ft.Container(
                                        alignment=ft.Alignment.BOTTOM_CENTER,
                                        padding=16,
                                        content=ft.Text("This is a custom control..."),
                                    )
                                ),
                            ),
                            ft.Button(
                                "No Controls",
                                on_click=lambda _: set_controls(None),
                            ),
                        ],
                    ),
                ],
            ),
        )
    )


if __name__ == "__main__":
    ft.run(main)
python
import flet as ft
import flet_video as ftv


def main(page: ft.Page):
    page.add(
        ft.SafeArea(
            expand=True,
            content=ftv.Video(
                expand=True,
                playlist=[
                    ftv.VideoMedia(
                        "https://user-images.githubusercontent.com/28951144/229373720-14d69157-1a56-4a78-a2f4-d7a134d7c3e9.mp4"
                    ),
                ],
                controls={
                    ftv.VideoControlsMode.NORMAL: ftv.MaterialDesktopVideoControls(
                        visible_on_mount=True,
                        bottom_button_bar=[
                            ftv.VideoPlayOrPauseButton(),
                            ftv.VideoSpacer(),
                            ft.Text("NORMAL Mode", weight=ft.FontWeight.BOLD),
                            ftv.VideoFullscreenButton(),
                        ],
                    ),
                    ftv.VideoControlsMode.FULLSCREEN: ftv.MaterialDesktopVideoControls(
                        visible_on_mount=True,
                        bottom_button_bar=[
                            ftv.VideoFullscreenButton(),
                            ftv.VideoSpacer(),
                            ft.Text("FULLSCREEN Mode", weight=ft.FontWeight.BOLD),
                            ftv.VideoPlayOrPauseButton(),
                        ],
                    ),
                },
            ),
        )
    )


if __name__ == "__main__":
    ft.run(main)
python
import flet as ft
import flet_video as ftv


def main(page: ft.Page):
    label_style = ft.TextStyle(
        size=12,
        color=ft.Colors.WHITE,
        weight=ft.FontWeight.BOLD,
    )

    page.add(
        ft.SafeArea(
            expand=True,
            content=ftv.Video(
                expand=True,
                playlist=[
                    ftv.VideoMedia("video-sample.mp4"),
                ],
                controls=ftv.MaterialDesktopVideoControls(
                    visible_on_mount=True,
                    primary_button_bar=[
                        ftv.VideoSkipPreviousButton(icon_color=ft.Colors.CYAN),
                        ftv.VideoPlayOrPauseButton(
                            icon_size=40,
                            icon_color=ft.Colors.CYAN,
                        ),
                        ftv.VideoSkipNextButton(
                            icon=ft.Icon(ft.Icons.FAST_FORWARD),
                            icon_color=ft.Colors.CYAN,
                        ),
                    ],
                    top_button_bar=[
                        ft.Text("Top button bar", style=label_style),
                        ftv.VideoSpacer(),
                        ftv.VideoFullscreenButton(icon_color=ft.Colors.AMBER),
                    ],
                    bottom_button_bar=[
                        ft.Text("Bottom button bar", style=label_style),
                        ftv.VideoSpacer(),
                        ftv.VideoPositionIndicator(
                            text_style=ft.TextStyle(
                                size=13,
                                color=ft.Colors.WHITE,
                            )
                        ),
                        ftv.VideoVolumeButton(
                            slider_width=96,
                            icon_color=ft.Colors.AMBER,
                        ),
                    ],
                ),
            ),
        )
    )


if __name__ == "__main__":
    ft.run(main)

描述 ​

Video 继承自:ft.LayoutControl, Control, BaseControl

一个从播放列表显示视频的控件。

属性 ​

名称类型默认值说明
playlistlist[VideoMedia][]表示要播放的视频文件的 VideoMedia 列表。
titlestr'flet-video'定义原生后端底层窗口和进程的名称。它会显示在系统窗口音量混合器中。
fitft.BoxFitft.BoxFit.CONTAIN用于视频的 box fit。
fill_colorft.ColorValueft.Colors.BLACK定义用于填充视频背景的颜色。
wakelockboolTrue播放视频时是否获取唤醒锁。为 True 时,视频播放期间设备屏幕不会进入待机/休眠。
autoplayboolFalse视频是否应自动开始播放。
controlsOptional[Union[VideoControls, ft.Control, dict[VideoControlsMode, Optional[Union[VideoControls, ft.Control]]]]]AdaptiveVideoControls(()显示在视频之上的控件。 设为 VideoControls 对象可使用内置控件,设为 Control 对象可使用自定义 Flet 控件,设为 None 则隐藏控件。 若要在全屏内外使用不同的控件,请把此属性设为一个以 VideoControlsMode 为键的字典。当未提供 NORMAL 控件时,会使用 DEFAULT 值。如果未提供 FULLSCREEN 控件,会先复用 NORMAL 控件,再回退到 DEFAULT。某个模式的值设为 None 时,仅对该模式隐藏控件。
fullscreenboolFalse视频播放器是否以全屏模式呈现。 设为 True 可进入全屏,设为 False 可通过编程方式退出全屏。
mutedboolFalse定义视频播放器是否应以静音状态启动。
playlist_modeOptional[PlaylistMode]None表示播放列表的播放模式。
shuffle_playlistboolFalse定义是否应随机打乱播放列表。
volumeft.Number100.0定义视频播放器的音量。 注意: 其取值范围在 0.0 到 100.0(含)之间,其中 0.0 为静音,100.0 为最大音量。 如果取值超出该范围会抛出异常。 抛出异常: ValueError:如果它的值不在 0.0 到 100.0(含)之间。
playback_rateft.Number1.0定义视频播放器的播放速率。
alignmentft.Alignmentft.Alignment.CENTER()定义视口的对齐方式。
filter_qualityft.FilterQualityft.FilterQuality.LOW用于渲染视频输出的纹理的过滤质量。 注意: 据报告,Android 在使用 HIGH 时会显示模糊图像。在该平台上建议使用 MEDIUM。
pause_upon_entering_background_modeboolTrue应用进入后台模式时是否暂停视频。
resume_upon_entering_foreground_modeboolFalse应用进入前台模式时是否恢复视频。仅当 pause_upon_entering_background_mode 也设为 True 时才生效。
pitchft.Number1.0定义视频播放器的相对音调。
configurationVideoConfigurationVideoConfiguration(()视频播放器的额外配置。
subtitle_configurationVideoSubtitleConfigurationVideoSubtitleConfiguration(()定义视频播放器的字幕配置。
subtitle_trackOptional[VideoSubtitleTrack]None定义视频播放器的字幕轨道。
widthOptional[Number]None控件的强制宽度(以虚拟像素为单位)。
heightOptional[Number]None控件的强制高度(以虚拟像素为单位)。
leftOptional[Number]None子控件左边缘相对于 Stack 左侧的内缩距离。 注意: 仅当此控件是以下之一的后代时才生效: Stack 控件、overlay 列表。
topOptional[Number]None子控件上边缘相对于 Stack 顶部的内缩距离。 注意: 仅当此控件是以下之一的后代时才生效: Stack 控件、overlay 列表。
rightOptional[Number]None子控件右边缘相对于 Stack 右侧的内缩距离。 注意: 仅当此控件是以下之一的后代时才生效: Stack 控件、overlay 列表。
bottomOptional[Number]None子控件下边缘相对于 Stack 底部的内缩距离。 注意: 仅当此控件是以下之一的后代时才生效: Stack 控件、overlay 列表。
alignOptional[Alignment]None控件在其父级中的对齐方式。
marginOptional[MarginValue]None设置控件的外边距。
rotateOptional[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) )
scaleOptional[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) )
offsetOptional[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)
flipOptional[Flip]None水平方向和/或垂直方向翻转此控件。 设为 Flip 的实例即可沿 x 轴、y 轴或两者镜像翻转。
transformOptional[Transform]None对此控件应用通用矩阵变换。 设为带 Matrix4 记录的 Transform 实例,用以描述任意的变换序列。
aspect_ratioOptional[Number]None控件的宽高比,定义为 width 与 height 的比值。 注意: 在当前实现中,如果设置了 aspect_ratio,同一控件上的 width 和 height 将在最终渲染尺寸中被忽略。
animate_opacityOptional[AnimationValue]None启用 opacity 属性的隐式动画。 更多信息见此处。
animate_sizeOptional[AnimationValue]None启用此控件尺寸的隐式动画。 如果设置了 width 或 height,则这些维度的变化会以动画呈现。如果两者都未设置,则对控件自然尺寸的变化进行动画。 更多信息见此处。
animate_positionOptional[AnimationValue]None启用定位属性(left、right、top 和 bottom)的隐式动画。 更多信息见此处。
animate_alignOptional[AnimationValue]None启用 align 属性的隐式动画。 更多信息见此处。
animate_marginOptional[AnimationValue]None启用 margin 属性的隐式动画。 更多信息见此处。
animate_rotationOptional[AnimationValue]None启用 rotate 属性的隐式动画。 更多信息见此处。
animate_scaleOptional[AnimationValue]None启用 scale 属性的隐式动画。 更多信息见此处。
animate_offsetOptional[AnimationValue]None启用 offset 属性的隐式动画。 更多信息见此处。
size_change_intervalint10on_size_change 事件的采样间隔(毫秒)。 设为 0 时,每次变化都会立即调用 on_size_change。
expandAnnotated[Optional[Union[bool, int]], V.instance_of((bool, int))]None指定此控件是否/如何扩展以填满父布局中的可用空间。 更多信息见此处。 注意: 仅当此控件的直接父级是以下控件或其子类时才生效:Column、Row、View、Page。 抛出异常: ValueError:如果它不是 bool 或 int 类型。
expand_looseboolFalse允许控件在主轴方向有可用空间时扩展,但不要求它填满所有可用空间。 更多信息见此处。 注意: 如果 expand_loose 为 True,则仅在以下条件满足时才生效: - expand 不为 None,且 - 此控件的直接父级是以下控件或其子类之一:Column、Row、View、Page。
colResponsiveNumber12如果此控件的父级是 ResponsiveRow,则此属性决定该控件在屏幕上跨越多少虚拟列。 可以是一个数字,也可以是一个按断点配置不同值的字典,例如 col={"sm": 6}。 值为 0 时会在该断点隐藏控件,因此它不会占用父级 ResponsiveRow 中的任何列。 此控件默认跨越 12 个虚拟列。 | 断点 | 尺寸 | |---|---| | xs | <576px | | sm | ≥576px | | md | ≥768px | | lg | ≥992px | | xl | ≥1200px | | xxl | ≥1400px |
opacityAnnotated[Number, V.between(0.0, 1.0)]1.0定义控件的透明度。 取值范围从 0.0(完全透明)到 1.0(完全不透明,无任何透明感)。 抛出异常: ValueError:如果它不在 0.0 到 1.0(含)之间。
tooltipOptional[TooltipValue]None鼠标悬停在此控件上时显示的工具提示。
badgeOptional[BadgeValue]None显示在此控件顶部的徽章。
visibleboolTrue每个控件都有 visible 属性,默认为 True —— 控件会渲染在页面上。将 visible 设为 False 会彻底阻止控件(及其所有子控件,如果有)渲染到页面画布上。隐藏的控件无法通过键盘或鼠标获得焦点或选中,也不会触发任何事件。
disabledboolFalse每个控件都有 disabled 属性,默认为 False —— 控件及其所有子控件都处于启用状态。 注意: 此属性的值会递归向下传播到所有子控件。 示例: 例如,如果你有一个包含多个输入控件的表单,可以通过禁用容器来一并禁用它们: python ft.Column( disabled = True, controls=[ ft.TextField(), ft.TextField() ] )
rtlboolFalse控件的文本方向是否为从右到左(RTL)。
dataAnyskip_任意类型的自定义数据。
keyOptional['KeyValue']None用于在更新之间保持控件标识稳定的键。

只读属性 ​

名称类型默认值说明
parentOptional['BaseControl']—此控件的直接祖先(父级)。 默认为 None,只有在此控件被挂载(加入页面树)后才有值。 Page 控件(页面树的根)是个例外 —— 它的 parent 始终为 None。
page'Page'—此控件所属的页面。

事件 ​

名称类型默认值说明
on_loadOptional[ft.ControlEventHandler['Video']]None当视频播放器初始化完成并可以播放时触发。
on_enter_fullscreenOptional[ft.ControlEventHandler['Video']]None当视频播放器进入全屏时触发。
on_exit_fullscreenOptional[ft.ControlEventHandler['Video']]None当视频播放器退出全屏时触发
on_errorOptional[ft.ControlEventHandler['Video']]None当发生错误时触发。 事件处理器参数的 data 属性包含错误的相关信息。
on_completeOptional[ft.ControlEventHandler['Video']]None当视频播放器播放完成时触发。
on_track_changeOptional[ft.ControlEventHandler['Video']]None当视频轨道变化时触发。 事件处理器参数的 data 属性包含新轨道的索引。
on_position_changeOptional[ft.ControlEventHandler['Video']]None当前播放位置变化时触发。 事件处理器参数的 data 属性包含当前位置,类型为 Duration。
on_duration_changeOptional[ft.ControlEventHandler['Video']]None当前媒体时长变化时触发。 事件处理器参数的 data 属性包含当前时长,类型为 Duration。
on_size_changeOptional[EventHandler[LayoutSizeChangeEvent['LayoutControl']]]None当此控件的尺寸发生变化时调用。 size_change_interval 定义此事件的调用频率。
on_animation_endOptional[ControlEventHandler['LayoutControl']]None动画完成时调用。 可用于串联多个动画。 事件处理器参数的 data 属性包含动画名称。 更多信息见此处。

方法 ​

名称类型默认值说明
before_update———
play——开始播放视频。
pause——暂停视频播放器。
play_or_pause——在视频播放器的播放与暂停状态之间切换,即:暂停时播放,播放时暂停。
stop——停止视频播放器。
next——跳到 playlist 中的下一个 VideoMedia。
previous——跳到 playlist 中的上一个 VideoMedia。
seek——将 playlist 中当前播放的 VideoMedia 跳转到指定的 position。
jump_to——跳到 playlist 中指定 media_index 处的 VideoMedia。
is_playingbool—返回: 如果视频播放器当前正在播放则返回 True,否则返回 False。
is_completedbool—返回: 如果视频播放器已到达当前播放媒体的末尾则返回 True,否则返回 False。
get_durationft.Duration—返回: 当前播放媒体的时长。
get_current_positionft.Duration—返回: 当前播放媒体的当前位置。
take_screenshotOptional[bytes]—捕获当前视频帧的截图。 参数: format:要返回的图像格式。支持的值为 "image/png"(PNG 编码图像)、"image/jpeg"(JPEG 编码图像),以及 None(原生后端上的原始 BGRA 像素缓冲区)。 include_libass_subtitles:原生后端上是否在截图中包含 libass 字幕。这需要底层播放器支持 libass,在 Web 后端上会被忽略。 返回: 编码后的图像字节;如果当前后端无法捕获视频帧则返回 None。 抛出异常: ValueError:如果 format 不受支持。
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 始终来自框架触发的事件,所以当此方法运行时,通道在两端都已存在。
updateNone—为此控件请求一次 UI 更新。 在更改控件状态或属性后调用。控件必须已挂载到页面且未被标记为冻结。

关注我,学习大模型、大数据、Python等热门技术。