跳到主要内容

ResponsiveRow 和移动端控件

· 阅读需 4 分钟
Feodor Fitsner
Flet 创始人和开发者

我们刚刚发布了 Flet 0.1.65,新增了一系列移动端优化控件,修复了一些错误,并引入了一个新的布局控件 - ResponsiveRow

ResponsiveRow 控件

ResponsiveRow 控件借鉴了 Bootstrap web 框架中的网格布局思想。

ResponsiveRow 允许将子控件对齐到虚拟列中。默认情况下,虚拟网格有 12 列,但可以通过 ResponsiveRow.columns 属性进行自定义。

类似于 expand 属性,现在每个控件都有一个 col 属性,用于指定控件应跨越多少列。例如,要创建一个包含两列,每列跨越 6 个虚拟列的布局:

import flet as ft

ft.ResponsiveRow([
ft.Column(col=6, controls=ft.Text("第一列")),
ft.Column(col=6, controls=ft.Text("第二列"))
])

ResponsiveRow 是"响应式"的,因为它可以根据屏幕(页面、窗口)大小的变化调整其子项的大小。上面示例中的 col 属性是一个常数,这意味着子项将在任何屏幕大小下跨越 6 列。

如果 ResponsiveRow 的子项未指定 col 属性,则该子项将跨越最大数量的列。

col 可以配置为在特定的"断点"上具有不同的值。断点是命名的维度范围:

断点尺寸
xs<576px
sm≥576px
md≥768px
lg≥992px
xl≥1200px
xxl≥1400px

例如,以下示例在移动设备上将内容折叠为单列,并在较大屏幕上占据两列:

import flet as ft

ft.ResponsiveRow([
ft.Column(col={"sm": 6}, controls=ft.Text("第一列")),
ft.Column(col={"sm": 6}, controls=ft.Text("第二列"))
])

下面是一个更复杂的响应式布局示例:

import flet as ft

def main(page: ft.Page):
def page_resize(e):
pw.value = f"{page.width} 像素"
pw.update()

page.on_resize = page_resize

pw = ft.Text(bottom=50, right=50, style="displaySmall")
page.overlay.append(pw)
page.add(
ft.ResponsiveRow(
[
ft.Container(
ft.Text("第一列"),
padding=5,
bgcolor=ft.colors.YELLOW,
col={"sm": 6, "md": 4, "xl": 2},
),
ft.Container(
ft.Text("第二列"),
padding=5,
bgcolor=ft.colors.GREEN,
col={"sm": 6, "md": 4, "xl": 2},
),
ft.Container(
ft.Text("第三列"),
padding=5,
bgcolor=ft.colors.BLUE,
col={"sm": 6, "md": 4, "xl": 2},
),
ft.Container(
ft.Text("第四列"),
padding=5,
bgcolor=ft.colors.PINK_300,
col={"sm": 6, "md": 4, "xl": 2},
),
],
),
ft.ResponsiveRow(
[
ft.TextField(label="文本框 1", col={"md": 4}),
ft.TextField(label="文本框 2", col={"md": 4}),
ft.TextField(label="文本框 3", col={"md": 4}),
],
run_spacing={"xs": 10},
),
)
page_resize(None)

ft.app(target=main)

ResponsiveRow 文档, 示例

其他新控件

此版本添加了 Flet 社区请求的新的可视化和非可视化控件,也是构建即将推出的 Flet Studio 的用户界面所需的控件。

BottomSheet

显示一个模态 Material Design 底部表单:

BottomSheet 文档, 示例

底部导航栏,提供了一种固定且方便的方式来在应用程序中切换主要目的地:

NavigationBar 文档, 示例

Tooltip

一个工具提示控件:

Tooltip 文档, 示例

HapticFeedback

允许访问设备上的触觉反馈(点击和振动)接口。

HapticFeedback 文档

ShakeDetector

用于检测手机晃动的控件。基于shake小部件。

ShakeDetector 文档

其他改进

Markdown 代码语法高亮

示例代码

可变字体支持

Flutter 终于支持了 可变字体,我们也将其引入到了 Flet 中!

示例代码

升级 Flet 模块到最新版本(pip install flet --upgrade),并告诉我们你的想法!

享受!

Matplotlib 和 Plotly 图表

· 阅读需 3 分钟
Feodor Fitsner
Flet 创始人和开发者

我们非常高兴地介绍 Flet 0.1.63 中的 Matplotlib 和 Plotly 图表控件!

MatplotlibPlotly是最受认可的Python图表库,具有丰富的功能。它们与其他科学Python库(如Numpy或Pandas)非常兼容。

毫无疑问,要想将它们的功能作为纯粹的Flutter小部件复制是几乎不可能的。幸运的是,Matplotlib和Plotly都可以将图表导出为各种格式,例如SVG。而Flet可以显示SVG图像,这给出了一个完美的组合 - Flet用于Matplotlib和Plotly的图表控件!

这个解决方案的效果非常好,几乎可以显示MatplotlibPlotly示例库中的任何示例 - 只有您的想象力是限制!

绘制一个简单的条形图

一个漂亮的带图例的散点图

或一些多图的等高线图

查看 Matplotlib 和 Plotly 图表控件的文档:

探索 Flet 图表示例

通过示例学习Python库:

在未来的版本中,我们可能会通过实现自定义 后端 为 Matplotlib 图表添加一个交互式 "工具栏"。或者也许这是 Flet 用户的一个很好的练习?😉

此外,当Flet支持其他语言时,我们需要重新审视图表功能,使其与语言无关,因为当前的图表实现依赖于Python库。

将Flet模块升级到最新版本(pip install flet --upgrade),将认证集成到您的应用程序中,并告诉我们您的想法!

尽情享受!

手势检测器

· 阅读需 2 分钟
Feodor Fitsner
Flet 创始人和开发者

我们刚刚发布了支持手势处理的 Flet 0.1.62版本!

我们刚刚发布了Flet 0.1.62,新增了手势处理的支持!

新增了一个控件 - GestureDetector,它允许处理各种手势:单击和双击,包括左(主要)和右(次要)鼠标(指针)按钮,垂直、水平和双向拖动,缩放(捏合和放大)手势以及悬停事件。现在,通过将其包装到 GestureDetector 中,您可以使任何 Flet 控件可“点击”和“拖动”!

以下是一个简单的示例应用,允许您在 Stack 内拖动容器:

import flet as ft

def main(page: ft.Page):
def on_pan_update(e: ft.DragUpdateEvent):
e.control.top = max(0, e.control.top + e.delta_y)
e.control.left = max(0, e.control.left + e.delta_x)
e.control.update()

gd = ft.GestureDetector(
mouse_cursor=ft.MouseCursor.MOVE,
on_vertical_drag_update=on_pan_update,
left=100,
top=100,
content=ft.Container(bgcolor=ft.colors.BLUE, width=50, height=50, border_radius=5),
)

page.add( ft.Stack([gd], expand=True))

ft.app(target=main)

手势检测器是 Flet 原语集合中的又一个伟大补充,它使您可以构建仅受您想象力限制的应用程序。本月晚些时候将推出2D绘图功能,将完善该组合!

此次发布不仅涉及手势 - 这也是一个“稳定性”发布。我们修复了一些错误,并添加了一些其他小功能,您可以在这里查看

升级到最新版本的 Flet 模块 (pip install flet --upgrade),在您的应用程序中集成身份验证,然后告诉我们您的想法!

享受吧!

用户认证

· 阅读需 5 分钟
Feodor Fitsner
Flet 创始人和开发者

Flet 现在支持用户认证了!🎉

现在,您可以在 Flet 应用中使用第三方身份提供商(如 GitHub、Google、Azure、Auth0、LinkedIn 等)实现用户认证(“使用 X 登录”按钮):

这个发布版本不仅仅是关于认证,还新增了很多相关功能和小的改进:

认证

Flet 认证功能:

  • 适用于 Flet 桌面、Web 和移动应用。
  • 在一个应用中使用多个认证提供商。
  • 内置 OAuth 提供商,自动获取用户详细信息:
    • GitHub
    • Azure
    • Google
    • Auth0
  • 可选的群组获取。
  • 自动刷新令牌。
  • 使用已保存的令牌登录(“记住我”)。
  • 自定义 OAuth 提供商。

一个简单示例,展示如何在 Flet 应用中添加“使用 GitHub 登录”按钮:

import os

import flet as ft
from flet.auth.providers.github_oauth_provider import GitHubOAuthProvider

def main(page: ft.Page):

provider = GitHubOAuthProvider(
client_id=os.getenv("GITHUB_CLIENT_ID"),
client_secret=os.getenv("GITHUB_CLIENT_SECRET"),
redirect_url="http://localhost:8550/api/oauth/redirect",
)

def login_click(e):
page.login(provider)

def on_login(e):
print("访问令牌:", page.auth.token.access_token)
print("用户 ID:", page.auth.user.id)

page.on_login = on_login
page.add(ft.ElevatedButton("使用 GitHub 登录", on_click=login_click))

ft.app(target=main, port=8550, view=ft.AppView.WEB_BROWSER)
备注

在运行应用之前,请在命令行中设置密钥环境变量:

$ export GITHUB_CLIENT_ID="<client_id>"
$ export GITHUB_CLIENT_SECRET="<client_secret>"

阅读认证指南获取更多信息和示例

客户端存储

Flet 的客户端存储 API 允许在客户端的持久存储中存储键值数据。Flet 实现使用了 shared_preferences Flutter 包。

将数据写入存储:

page.client_storage.set("key", "value")

读取数据:

value = page.client_storage.get("key")

阅读客户端存储指南获取更多信息和示例

会话存储

Flet 引入了在服务器端用户会话中存储键值数据的 API。

将数据写入会话:

page.session.set("key", "value")

读取数据:

value = page.session.get("key")

阅读会话存储指南获取更多信息和示例

加密 API

在此版本中,Flet 引入了使用对称算法(使用相同密钥进行加密和解密)加密和解密敏感文本数据的实用方法。它使用 cryptography 包中的 Fernet 实现,Fernet 是 AES 128 的增强版本,加上 PBKDF2 从用户密码短语派生加密密钥。

加密数据:

from flet.security import encrypt, decrypt
secret_key = "S3CreT!"
plain_text = "这是一条秘密消息!"
encrypted_data = encrypt(plain_text, secret_key)

解密数据:

from flet.security import encrypt, decrypt
secret_key = "S3CreT!"
plain_text = decrypt(encrypted_data, secret_key)
print(plain_text)

继续阅读获取更多信息和示例

其他改进

import flet as ft
def main(page: ft.Page):
page.window_bgcolor = ft.colors.TRANSPARENT
page.bgcolor=ft.colors.TRANSPARENT
page.window_title_bar_hidden = True
page.window_frameless = True
page.window_left = 400
page.window_top = 400
page.add(ft.ElevatedButton("我是一颗浮动的按钮!"))
ft.app(target=main)
  • page.get_clipboard()
  • page.launch_url() - 通过额外参数更好地控制:
    • web_window_name - 打开 URL 的窗口选项卡/名称:_self - 当前选项卡,_blank - 新选项卡,或 <your name> - 指定名称的选项卡。
    • web_popup_window - 设置为 True 在浏览器弹出窗口中显示 URL。默认值为 False
    • window_width - 可选,弹出窗口宽度。
    • window_height - 可选,弹出窗口高度。
  • page.window_to_front()
  • page.close_in_app_web_view()

升级 Flet 模块到最新版本(pip install flet --upgrade),将认证集成到您的应用中并 告诉我们 您的想法!

享受吧!

文件选择器和上传

· 阅读需 6 分钟
Feodor Fitsner
Flet 创始人和开发者

终于,文件选择器和上传功能来了!🎉

文件选择器控件打开一个原生操作系统对话框,用于选择文件和目录。它基于出色的 file_picker Flutter 包。

它可以在所有平台上运行:Web、macOS、Windows、Linux、iOS 和 Android。

查看上图示例的源码

文件选择器允许打开三种对话框:

  • 选择文件 - 一个或多个文件,可以是任意文件或仅特定类型的文件。
  • 保存文件 - 选择目录和文件名。
  • 获取目录 - 选择目录。

在浏览器中运行 Flet 应用程序时,仅提供“选择文件”选项,并且仅用于上传,因为它显然不会返回所选文件的完整路径。

文件选择器真正出色的地方在于桌面!所有三个对话框都会返回所选文件和目录的完整路径,为您的用户提供了很好的帮助!

在您的应用中使用文件选择器

建议将文件选择器添加到 page.overlay.controls 集合中,这样它就不会影响您的应用布局。尽管文件选择器的尺寸为 0x0,但放入 RowColumn 时仍被视为控件。

import flet as ft

file_picker = ft.FilePicker()
page.overlay.append(file_picker)
page.update()

要打开文件选择器对话框,请调用以下三种方法之一:

  • pick_files()
  • save_file()
  • get_directory_path()

Lambda 表达式在这种情况下非常好用:

ft.ElevatedButton("选择文件...",
on_click=lambda _: file_picker.pick_files(allow_multiple=True))

当对话框关闭时,会调用 FilePicker.on_result 事件处理程序,该事件对象具有以下属性之一:

  • files - "选择文件"对话框,所选文件的列表,如果对话框被取消,则为 None
  • path - "保存文件"和"获取目录"对话框,所选文件或目录的完整路径,如果对话框被取消,则为 None
import flet as ft

def on_dialog_result(e: ft.FilePickerResultEvent):
print("选定的文件:", e.files)
print("选定的文件或目录:", e.path)

file_picker = ft.FilePicker(on_result=on_dialog_result)

最后的结果始终可在 FilePicker.result 属性中获取。

有关所有可用对话框方法及其参数,请查看 文件选择器 控件文档。

上传文件

文件选择器具有内置的上传功能,可以在所有平台和 Web 上运行。

要上传一个或多个文件,首先需要调用 FilePicker.pick_files()。 当用户选择文件后,它们不会自动上传到任何地方,而是将它们的引用保存在文件选择器的状态中。

要执行实际的上传,可以调用 FilePicker.upload() 方法,并传入需要上传的文件列表,以及它们的上传 URL 和上传方法(PUTPOST):

import flet as ft

def upload_files(e):
upload_list = []
if file_picker.result != None and file_picker.result.files != None:
for f in file_picker.result.files:
upload_list.append(
FilePickerUploadFile(
f.name,
upload_url=page.get_upload_url(f.name, 600),
)
)
file_picker.upload(upload_list)

ft.ElevatedButton("上传", on_click=upload_files)
备注

如果需要为每个用户单独上传,可以在 page.get_upload_url() 调用中指定带有任意数量目录的文件名,例如:

upload_url = page.get_upload_url(f"/{username}/pictures/{f.name}", 600)

如果 /{username}/pictures 目录不存在,将会自动在 upload_dir 内创建。

上传存储

请注意page.get_upload_url()方法的使用 - 它会为Flet的内部上传存储生成一个预签名的上传URL。

使用任意存储进行文件上传

可以使用 boto3 库为 AWS S3 存储生成预签名上传 URL

相同的技术应适用于 WasabiBackblazeMinIO 和任何其他具有 S3 兼容 API 的存储提供商。

要使Flet将上传的文件保存到目录中,请在flet.app()调用中提供目录的完整路径或相对路径:

ft.app(target=main, upload_dir="uploads")

您甚至可以将上传文件放在"assets"目录中,以便可以从Flet客户端直接访问上传的文件,例如图片、文档或其他媒体:

ft.app(target=main, assets_dir="assets", upload_dir="assets/uploads")

在应用程序的某个地方,您可以使用以下代码显示已上传的图片:

page.add(ft.Image(src="/uploads/<some-uploaded-picture.png>"))

上传进度

一旦调用FilePicker.upload()方法,Flet客户端就会异步地逐个上传选择的文件,并通过FilePicker.on_upload回调报告进度。

on_upload 事件的事件对象是 FilePickerUploadEvent 类的实例,具有以下字段:

  • file_name
  • progress - 值从 0.01.0
  • error

对于每个上传的文件,回调至少被调用两次:上传开始前进度为 0 和上传完成时进度为 1.0。对于大于 1 MB 的文件,每上传 10% 还会额外报告一次进度。

查看这个展示多个文件上传的示例

查看 文件选择器 控件文档以了解其所有属性和示例。

将Flet模块升级到最新版本(pip install flet --upgrade),尝试一下File Picker,并告诉我们您的想法!

享受吧!