跳到主要内容

4 篇博文 含有标签「release」

查看所有标签

手势检测器

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

我们刚刚发布了支持手势处理的 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),将身份验证集成到您的应用程序中,并让我们知道您的想法 (链接)

Enjoy!

动画乐趣

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

尽管 Flet 的发布版本早就推出了动画支持,但我们刚刚完成了对其新功能的文件化!我们都知道,如果功能没有被文档化,那就等于不存在!😉

Flutter 提供了多种方法来创建动画,例如“隐式动画”、“显式动画”、“补间动画”、“分阶段动画”、“预置动画”,还能显示在 Rive 和 Lottie 编辑器中预制的动画场景。

我们将从“隐式动画”开始,它允许您通过设置目标值来实现对控件属性的动画效果;每当目标值发生变化时,控件将把属性从旧值动画到新值。

演示时间

探索演示源码。顺便提一下,演示是托管在 Heroku 上的,所以您可以将其用作自己部署的起点。

隐式动画

可以为以下控件属性启用隐式动画:

此外,现在可以对全部 Container 控件属性 进行动画处理,并且添加了一个新的 AnimatedSwitcher 控件,用于在旧内容和新内容之间进行动画过渡。

其他新功能

Markdown 控件

允许以 Markdown 格式呈现文本。支持各种扩展:CommonMarkGitHub WebGitHub Flavored

请查看 Markdown 控件文档 以获取更多信息和示例。

URL Launcher

page.launch_url(url) 方法允许以编程方式在新浏览器窗口中打开链接,例如:

page.launch_url("https://google.com")

它还可以与 Markdown 控件很好地配合,用于在 Markdown 文档中打开链接。

快捷键

现在 Page 包含 on_keyboard_event 事件处理程序,用于全局拦截所有按键。

请查看此 简单使用示例

辅助功能改进

我们在文档中添加了 辅助功能 部分,涵盖了屏幕阅读器的语义支持。

ShaderMark 控件

一个应用Shader生成的遮罩到其内容上的控件。可以创建出漂亮的效果,例如 逐渐淡出图像

就是这些了!

希望你喜欢使用 Flet!如果你有任何问题,请随时在我们的 Discord 服务器上联系我们。

在新的Flet版本中,我们为您带来了美丽的渐变效果、按钮样式和TextField圆角

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

我们刚刚发布了Flet 0.1.46,添加了一些令人兴奋的新功能:

  • 容器中的渐变背景
  • 对按钮、文本框和下拉框控件进行了全面的样式设置
  • ...等等

渐变背景

线性渐变

import math
import flet as ft

def main(page: ft.Page):

page.add(
ft.Container(
alignment=ft.alignment.center,
gradient=ft.LinearGradient(
begin=ft.alignment.top_left,
end=Alignment(0.8, 1),
colors=[
"0xff1f005c",
"0xff5b0060",
"0xff870160",
"0xffac255e",
"0xffca485c",
"0xffe16b5c",
"0xfff39060",
"0xffffb56b",
],
tile_mode=ft.GradientTileMode.MIRROR,
rotation=math.pi / 3,
),
width=150,
height=150,
border_radius=5,
)
)

ft.app(target=main)

请查看Container.gradient文档,以获取有关LinearGradient属性的更多信息。

径向渐变

import flet as ft

def main(page: ft.Page):

page.add(
ft.Container(
alignment=ft.alignment.center,
gradient=ft.RadialGradient(
center=Alignment(0.7, -0.6),
radius=0.2,
colors=[
"0xFFFFFF00", # yellow sun
"0xFF0099FF", # blue sky
],
stops=[0.4, 1.0],
),
width=150,
height=150,
border_radius=5,
)
)

ft.app(target=main)

请查看Container.gradient文档,以获取有关RadialGradient属性的更多信息。

扫描渐变

import math
import flet as ft

def main(page: ft.Page):

page.add(
ft.Container(
alignment=ft.alignment.center,
gradient=SweepGradient(
center=ft.alignment.center,
start_angle=0.0,
end_angle=math.pi * 2,
colors=[
"0xFF4285F4",
"0xFF34A853",
"0xFFFBBC05",
"0xFFEA4335",
"0xFF4285F4",
],
stops=[0.0, 0.25, 0.5, 0.75, 1.0],
),
width=150,
height=150,
border_radius=5,
)
)

ft.app(target=main)

请查看Container.gradient文档,了解有关SweepGradient属性的更多信息。

按钮样式

这个 Flet 版本引入了style属性到所有按钮控件中,它是ButtonStyle类的一个实例。 ButtonStyle允许控制按钮的所有视觉方面,例如形状、前景、背景和阴影颜色、内容填充、边框宽度和半径!

此外,每个单独的样式属性都可以针对按钮的不同“材料状态”进行配置,例如“悬停”、“聚焦”、“禁用”等。例如,您可以为悬停状态配置不同的形状、背景颜色,并为所有其他状态配置回退值。

请查看这个“极端”的样式示例:

import flet as ft
from flet.border import BorderSide
from flet.buttons import RoundedRectangleBorder

def main(page: ft.Page):

page.add(
ft.ElevatedButton(
"Styled button 1",
style=ft.ButtonStyle(
color={
ft.MaterialState.HOVERED: ft.colors.WHITE,
ft.MaterialState.FOCUSED: ft.colors.BLUE,
ft.MaterialState.DEFAULT: ft.colors.BLACK,
},
bgcolor={ft.MaterialState.FOCUSED: ft.colors.PINK_200, "": ft.colors.YELLOW},
padding={ft.MaterialState.HOVERED: 20},
overlay_color=ft.colors.TRANSPARENT,
elevation={"pressed": 0, "": 1},
animation_duration=500,
side={
ft.MaterialState.DEFAULT: BorderSide(1, ft.colors.BLUE),
ft.MaterialState.HOVERED: BorderSide(2, ft.colors.BLUE),
},
shape={
ft.MaterialState.HOVERED: RoundedRectangleBorder(radius=20),
ft.MaterialState.DEFAULT: RoundedRectangleBorder(radius=2),
},
),
)
)

ft.app(target=main)

ft.MaterialState.DEFAULT 状态是一个备用样式。

按钮的形状也可以通过 ButtonStyle.shape 属性进行更改:

import flet as ft
from flet.buttons import (
BeveledRectangleBorder,
CircleBorder,
ContinuousRectangleBorder,
RoundedRectangleBorder,
StadiumBorder,
)

def main(page: ft.Page):
page.padding = 30
page.spacing = 30
page.add(
ft.FilledButton(
"Stadium",
style=ft.ButtonStyle(
shape=ft.StadiumBorder(),
),
),
ft.FilledButton(
"Rounded rectangle",
style=ft.ButtonStyle(
shape=ft.RoundedRectangleBorder(radius=10),
),
),
ft.FilledButton(
"Continuous rectangle",
style=ft.ButtonStyle(
shape=ft.ContinuousRectangleBorder(radius=30),
),
),
ft.FilledButton(
"Beveled rectangle",
style=ft.ButtonStyle(
shape=ft.BeveledRectangleBorder(radius=10),
),
),
ft.FilledButton(
"Circle",
style=ft.ButtonStyle(shape=ft.CircleBorder(), padding=30),
),
)

ft.app(target=main)

请查看ElevatedButton.style属性文档,了解ButtonStyle类及其属性的完整描述。

文本字段和下拉菜单的样式设置

现在可以为TextFieldDropdown控件的正常状态和焦点状态配置文本大小、边框样式和圆角半径。TextField还允许配置光标和选定文本的颜色。

此外,现在可以使用max_length属性限制输入到TextField中的最大长度。

我们还引入了capitalization属性,用于在将字符键入TextField时自动进行大小写转换。您可以从4种大小写转换策略中选择:none(默认值)、characterswordssentences

以下是一个带有max_lengthcapitalization的样式化TextField示例:

import flet as ft

def main(page: ft.Page):
page.padding = 50
page.add(
ft.TextField(
text_size=30,
cursor_color=ft.colors.RED,
selection_color=ft.colors.YELLOW,
color=ft.colors.PINK,
bgcolor=ft.colors.BLACK26,
filled=True,
focused_color=ft.colors.GREEN,
focused_bgcolor=ft.colors.CYAN_200,
border_radius=30,
border_color=ft.colors.GREEN_800,
focused_border_color=ft.colors.GREEN_ACCENT_400,
max_length=20,
capitalization="characters",
)
)

ft.app(target=main)

下面是一个样式化的Dropdown控件的示例代码:

import flet as ft

def main(page: ft.Page):
page.padding = 50
page.add(
ft.Dropdown(
options=[
ft.dropdown.Option("a", "Item A"),
ft.dropdown.Option("b", "Item B"),
ft.dropdown.Option("c", "Item C"),
],
border_radius=30,
filled=True,
border_color=ft.colors.TRANSPARENT,
bgcolor=ft.colors.BLACK12,
focused_bgcolor=ft.colors.BLUE_100,
)
)

ft.app(target=main)

其他变化

IconButton 增加了 selected 状态,并与新的 style 配合使用。

这是一个切换图标按钮的示例:

import flet as ft

def main(page: ft.Page):

def toggle_icon_button(e):
e.control.selected = not e.control.selected
e.control.update()

page.add(
ft.IconButton(
icon=ft.icons.BATTERY_1_BAR,
selected_icon=ft.icons.BATTERY_FULL,
on_click=toggle_icon_button,
selected=False,
style=ft.ButtonStyle(color={"selected": ft.colors.GREEN, "": ft.colors.RED}),
)
)

ft.app(target=main)

试试Flet,并告诉我们你的想法!

导航与路由

· 阅读需 7 分钟
Feodor Fitsner
Flet创始人兼开发者

Flet 0.1.42已发布,其中包含导航和路由功能!

导航和路由是单页面应用程序(SPA)的重要功能,它允许将应用程序用户界面组织成虚拟页面(视图),并在应用程序URL反映应用程序的当前状态时在它们之间进行“导航”。

对于移动应用程序,导航和路由还可以作为特定应用程序部分的深度链接。

为了将导航和路由添加到Flet中,我们花费了比预期更多的努力。这个实现是基于Flutter的Navigator 2.0 API,并需要用“Page和Views”替换Flet的“Page”抽象。Flutter的新导航和路由API有很多改进,包括:

  1. 对历史堆栈的编程控制。
  2. 捕获AppBar中“返回”按钮调用的简单方法。
  3. 与浏览器历史的强大同步。

探索上述示例的源代码

页面路由

页面路由是在#符号之后的应用程序URL的一部分:

默认的应用程序路由(如果用户没有在应用程序URL中设置)是/。所有路由都以/开头,例如/store/authors/1/books/2

可以通过读取page.route属性来获取应用程序路由,例如:

import flet as ft

def main(page: ft.Page):
page.add(ft.Text(f"Initial route: {page.route}"))

ft.app(target=main, view=ft.AppView.WEB_BROWSER)

获取应用程序URL,打开一个新的浏览器标签页,粘贴URL,将#后面的部分修改为/test,然后按回车键。您应该看到"Initial route: /test"。

每当URL中的路由发生更改(通过编辑URL或使用后退/前进按钮导航浏览器历史记录)时,Flet都会调用page.on_route_change事件处理程序:

import flet as ft

def main(page: ft.Page):
page.add(ft.Text(f"Initial route: {page.route}"))

def route_change(route):
page.add(ft.Text(f"New route: {route}"))

page.on_route_change = route_change
page.update()

ft.app(target=main, view=ft.AppView.WEB_BROWSER)

现在尝试几次更新URL哈希,然后使用后退/前进按钮!您应该看到每次路由更改时都会向页面添加一条新消息:

路由可以通过在代码中更新page.route属性来进行程序化更改:

import flet as ft

def main(page: ft.Page):
page.add(ft.Text(f"Initial route: {page.route}"))

def route_change(route):
page.add(ft.Text(f"New route: {route}"))

def go_store(e):
page.route = "/store"
page.update()

page.on_route_change = route_change
page.add(ft.ElevatedButton("Go to Store", on_click=go_store))

ft.app(target=main, view=ft.AppView.WEB_BROWSER)

点击"Go to Store"按钮,您将看到应用程序URL已更改,并且在浏览器历史记录中推入了一个新项。您可以使用浏览器的"返回"按钮导航到以前的路由。

页面视图

Flet的Page现在不仅仅是一个单独的页面,而是一个包含叠加在一起的View的容器,就像三明治一样:

视图的集合表示导航器历史记录。Page具有page.views属性以访问视图集合。

列表中的最后一个视图是当前显示在页面上的视图。视图列表必须至少有一个元素(根视图)。

要模拟页面之间的过渡,请更改page.route并在page.view列表的末尾添加一个新的View

从集合中弹出最后一个视图,并在page.on_view_pop事件处理程序中将路由更改为“上一个”路由以返回。

在路由更改时构建视图

为了构建可靠的导航,程序中必须有一个单一的位置,根据当前路由构建视图列表。换句话说,导航历史堆栈(由视图列表表示)必须是路由的函数。

这个位置是page.on_route_change事件处理程序。

让我们将所有内容整合到一个完整的示例中,该示例允许在两个页面之间导航:

import flet as ft

def main(page: ft.Page):
page.title = "Routes Example"

def route_change(route):
page.views.clear()
page.views.append(
ft.View(
"/",
[
ft.AppBar(title=ft.Text("Flet app"), bgcolor=ft.colors.SURFACE_VARIANT),
ft.ElevatedButton("Visit Store", on_click=lambda _: page.go("/store")),
],
)
)
if page.route == "/store":
page.views.append(
ft.View(
"/store",
[
ft.AppBar(title=ft.Text("Store"), bgcolor=ft.colors.SURFACE_VARIANT),
ft.ElevatedButton("Go Home", on_click=lambda _: page.go("/")),
],
)
)
page.update()

def view_pop(view):
page.views.pop()
top_view = page.views[-1]
page.go(top_view.route)

page.on_route_change = route_change
page.on_view_pop = view_pop
page.go(page.route)


ft.app(target=main, view=ft.AppView.WEB_BROWSER)

尝试使用"Visit Store"和"Go Home"按钮、后退/前进浏览器按钮、手动更改URL中的路由进行页面之间的导航-无论如何都可以工作! :)

备注

为了在页面之间"导航",我们使用了page.go(route) - 一个帮助方法,它更新了page.route,调用page.on_route_change事件处理程序来更新视图,最后调用page.update()

请注意page.on_view_pop事件处理程序的使用。当用户点击AppBar控件中的自动"返回"按钮时,它会触发。在处理程序中,我们从视图集合中移除最后一个元素,并导航到视图的根部。

路由模板

Flet提供了TemplateRoute - 一个基于repath库的实用类,它允许匹配类似ExpressJS的路由并解析其参数,例如/account/:account_id/orders/:order_id

TemplateRoute与路由更改事件非常搭配:

troute = TemplateRoute(page.route)

if troute.match("/books/:id"):
print("Book view ID:", troute.id)
elif troute.match("/account/:account_id/orders/:order_id"):
print("Account:", troute.account_id, "Order:", troute.order_id)
else:
print("Unknown route")

您可以在这里阅读有关repath库支持的模板语法的更多信息。

今天就介绍这么多!

试用一下Flet,并告诉我们您的想法!