0
# Events and Interaction
1
2
This document covers Flet's comprehensive event system and user interaction capabilities, including gesture recognition, drag and drop, keyboard/mouse events, and real-time communication.
3
4
## Import
5
6
```python
7
import flet as ft
8
```
9
10
## Core Event System
11
12
### Event Handling Basics
13
14
All Flet controls support event handling through callback functions. Events are triggered by user interactions and system changes.
15
16
**Common Event Patterns:**
17
```python
18
def handle_click(e):
19
# e.control - reference to the control that triggered the event
20
# e.data - event-specific data
21
print(f"Button clicked: {e.control.text}")
22
23
button = ft.ElevatedButton(
24
"Click me!",
25
on_click=handle_click
26
)
27
```
28
29
### ControlEvent
30
31
```python { .api }
32
class ControlEvent:
33
"""Base control event class."""
34
35
control: Control # Control that triggered the event
36
name: str # Event name
37
data: str # Event data (varies by event type)
38
page: Page # Page reference
39
target: str # Target control ID
40
```
41
42
### Event
43
44
```python { .api }
45
class Event:
46
"""Base event class for system events."""
47
48
name: str # Event name
49
data: str # Event data
50
page: Page # Page reference
51
```
52
53
## Gesture Events
54
55
### TapEvent
56
57
```python { .api }
58
class TapEvent(ControlEvent):
59
"""Tap gesture event."""
60
61
local_x: float # X coordinate relative to control
62
local_y: float # Y coordinate relative to control
63
global_x: float # X coordinate relative to screen
64
global_y: float # Y coordinate relative to screen
65
```
66
67
**Example:**
68
```python
69
def on_tap(e):
70
print(f"Tapped at: ({e.local_x}, {e.local_y})")
71
72
ft.Container(
73
content=ft.Text("Tap me"),
74
width=200, height=100,
75
bgcolor=ft.colors.BLUE_100,
76
on_click=on_tap
77
)
78
```
79
80
### HoverEvent
81
82
```python { .api }
83
class HoverEvent(ControlEvent):
84
"""Hover event."""
85
86
local_x: float # X coordinate relative to control
87
local_y: float # Y coordinate relative to control
88
global_x: float # X coordinate relative to screen
89
global_y: float # Y coordinate relative to screen
90
```
91
92
**Example:**
93
```python
94
def on_hover(e):
95
e.control.bgcolor = ft.colors.GREY_200 if e.data == "true" else ft.colors.WHITE
96
e.control.update()
97
98
ft.Container(
99
content=ft.Text("Hover over me"),
100
padding=20,
101
bgcolor=ft.colors.WHITE,
102
on_hover=on_hover
103
)
104
```
105
106
### GestureDetector
107
108
```python { .api }
109
class GestureDetector(Control):
110
"""Gesture detection wrapper control."""
111
112
def __init__(
113
self,
114
content: Control = None,
115
on_tap: callable = None,
116
on_tap_down: callable = None,
117
on_tap_up: callable = None,
118
on_secondary_tap: callable = None,
119
on_secondary_tap_down: callable = None,
120
on_secondary_tap_up: callable = None,
121
on_long_press_start: callable = None,
122
on_long_press_end: callable = None,
123
on_double_tap: callable = None,
124
on_double_tap_down: callable = None,
125
on_force_press_start: callable = None,
126
on_force_press_end: callable = None,
127
on_force_press_peak: callable = None,
128
on_pan_start: callable = None,
129
on_pan_update: callable = None,
130
on_pan_end: callable = None,
131
on_scale_start: callable = None,
132
on_scale_update: callable = None,
133
on_scale_end: callable = None,
134
on_hover: callable = None,
135
on_enter: callable = None,
136
on_exit: callable = None,
137
drag_interval: int = None,
138
hover_interval: int = None,
139
**kwargs
140
)
141
```
142
143
**Parameters:**
144
- `content` (Control, optional): Child control to wrap
145
- `on_tap` (callable, optional): Single tap event
146
- `on_double_tap` (callable, optional): Double tap event
147
- `on_long_press_start` (callable, optional): Long press start event
148
- `on_pan_start` (callable, optional): Pan gesture start event
149
- `on_pan_update` (callable, optional): Pan gesture update event
150
- `on_scale_start` (callable, optional): Scale gesture start event
151
- `on_scale_update` (callable, optional): Scale gesture update event
152
153
**Example:**
154
```python
155
def on_pan_update(e):
156
e.control.top = max(0, e.control.top + e.delta_y)
157
e.control.left = max(0, e.control.left + e.delta_x)
158
e.control.update()
159
160
draggable_box = ft.GestureDetector(
161
content=ft.Container(
162
content=ft.Text("Drag me"),
163
width=100, height=100,
164
bgcolor=ft.colors.BLUE,
165
border_radius=10
166
),
167
on_pan_update=on_pan_update
168
)
169
```
170
171
## Drag and Drop Events
172
173
### DragStartEvent
174
175
```python { .api }
176
class DragStartEvent(ControlEvent):
177
"""Drag operation start event."""
178
179
local_x: float # X coordinate relative to control
180
local_y: float # Y coordinate relative to control
181
global_x: float # X coordinate relative to screen
182
global_y: float # Y coordinate relative to screen
183
timestamp: int # Event timestamp
184
```
185
186
### DragUpdateEvent
187
188
```python { .api }
189
class DragUpdateEvent(ControlEvent):
190
"""Drag operation update event."""
191
192
delta_x: float # X movement delta
193
delta_y: float # Y movement delta
194
local_x: float # Current X coordinate relative to control
195
local_y: float # Current Y coordinate relative to control
196
global_x: float # Current X coordinate relative to screen
197
global_y: float # Current Y coordinate relative to screen
198
primary_delta: float # Primary axis movement delta
199
timestamp: int # Event timestamp
200
```
201
202
### DragEndEvent
203
204
```python { .api }
205
class DragEndEvent(ControlEvent):
206
"""Drag operation end event."""
207
208
primary_velocity: float # Primary axis velocity
209
velocity: float # Overall velocity
210
local_x: float # Final X coordinate relative to control
211
local_y: float # Final Y coordinate relative to control
212
global_x: float # Final X coordinate relative to screen
213
global_y: float # Final Y coordinate relative to screen
214
```
215
216
### Draggable
217
218
```python { .api }
219
class Draggable(Control):
220
"""Draggable control wrapper."""
221
222
def __init__(
223
self,
224
group: str = None,
225
content: Control = None,
226
content_when_dragging: Control = None,
227
content_feedback: Control = None,
228
**kwargs
229
)
230
```
231
232
**Parameters:**
233
- `group` (str, optional): Drag group identifier
234
- `content` (Control, optional): Control to make draggable
235
- `content_when_dragging` (Control, optional): Content shown while dragging
236
- `content_feedback` (Control, optional): Drag feedback content
237
238
### DragTarget
239
240
```python { .api }
241
class DragTarget(Control):
242
"""Drop target for draggable controls."""
243
244
def __init__(
245
self,
246
group: str = None,
247
content: Control = None,
248
on_will_accept: callable = None,
249
on_accept: callable = None,
250
on_leave: callable = None,
251
**kwargs
252
)
253
```
254
255
**Example:**
256
```python
257
def on_accept_drag(e):
258
# Handle dropped item
259
src_control = page.get_control(e.src_id)
260
print(f"Dropped: {src_control.content.value}")
261
262
# Draggable item
263
draggable_item = ft.Draggable(
264
group="items",
265
content=ft.Container(
266
content=ft.Text("Drag me"),
267
width=100, height=50,
268
bgcolor=ft.colors.BLUE_200,
269
border_radius=5
270
)
271
)
272
273
# Drop target
274
drop_target = ft.DragTarget(
275
group="items",
276
content=ft.Container(
277
content=ft.Text("Drop here"),
278
width=200, height=100,
279
bgcolor=ft.colors.GREEN_100,
280
border_radius=5,
281
alignment=ft.alignment.center
282
),
283
on_accept=on_accept_drag
284
)
285
```
286
287
### Dismissible
288
289
```python { .api }
290
class Dismissible(Control):
291
"""Swipe-to-dismiss wrapper."""
292
293
def __init__(
294
self,
295
content: Control = None,
296
background: Control = None,
297
secondary_background: Control = None,
298
dismiss_direction: DismissDirection = None,
299
dismiss_thresholds: dict = None,
300
movement_duration: int = None,
301
resize_duration: int = None,
302
cross_axis_end_offset: float = None,
303
on_dismiss: callable = None,
304
on_update: callable = None,
305
**kwargs
306
)
307
```
308
309
**Parameters:**
310
- `content` (Control, optional): Content to make dismissible
311
- `background` (Control, optional): Background shown when swiping
312
- `dismiss_direction` (DismissDirection, optional): Allowed dismiss directions
313
- `on_dismiss` (callable, optional): Dismiss event handler
314
- `on_update` (callable, optional): Dismiss progress update handler
315
316
## Scroll Events
317
318
### ScrollEvent
319
320
```python { .api }
321
class ScrollEvent(ControlEvent):
322
"""Scroll event."""
323
324
pixels: float # Current scroll offset
325
min_scroll_extent: float # Minimum scroll extent
326
max_scroll_extent: float # Maximum scroll extent
327
viewport_dimension: float # Viewport size
328
scroll_delta: float # Scroll delta
329
direction: str # Scroll direction
330
```
331
332
**Example:**
333
```python
334
def on_scroll(e):
335
print(f"Scroll position: {e.pixels}")
336
337
# Show/hide scroll indicator based on position
338
if e.pixels > 100:
339
scroll_indicator.visible = True
340
else:
341
scroll_indicator.visible = False
342
page.update()
343
344
scrollable_content = ft.ListView(
345
controls=[ft.ListTile(title=ft.Text(f"Item {i}")) for i in range(100)],
346
on_scroll=on_scroll,
347
auto_scroll=True
348
)
349
```
350
351
## Keyboard Events
352
353
### KeyboardEvent
354
355
```python { .api }
356
class KeyboardEvent(Event):
357
"""Keyboard input event."""
358
359
key: str # Key identifier
360
shift: bool # Shift key pressed
361
ctrl: bool # Ctrl key pressed
362
alt: bool # Alt key pressed
363
meta: bool # Meta key pressed
364
```
365
366
**Example:**
367
```python
368
def on_keyboard(e):
369
if e.key == "Enter":
370
handle_submit()
371
elif e.key == "Escape":
372
handle_cancel()
373
elif e.ctrl and e.key == "s":
374
handle_save()
375
376
page.on_keyboard_event = on_keyboard
377
```
378
379
## Page Events
380
381
### Page Lifecycle Events
382
383
```python { .api }
384
def page_event_handlers(page: ft.Page):
385
"""Example page event handlers."""
386
387
def on_route_change(e):
388
"""Route navigation event."""
389
print(f"Route changed to: {e.route}")
390
391
def on_view_pop(e):
392
"""View pop event."""
393
print(f"View popped: {e.view}")
394
395
def on_resize(e):
396
"""Window resize event."""
397
print(f"Window resized: {page.window_width}x{page.window_height}")
398
399
def on_close(e):
400
"""Page close event."""
401
print("Page closing")
402
e.page.window_destroy()
403
404
page.on_route_change = on_route_change
405
page.on_view_pop = on_view_pop
406
page.on_resize = on_resize
407
page.on_window_event = on_close
408
```
409
410
### RouteChangeEvent
411
412
```python { .api }
413
class RouteChangeEvent(Event):
414
"""Route change navigation event."""
415
416
route: str # New route
417
old_route: str # Previous route
418
```
419
420
### ViewPopEvent
421
422
```python { .api }
423
class ViewPopEvent(Event):
424
"""View pop navigation event."""
425
426
view: View # View being popped
427
```
428
429
### WindowEvent
430
431
```python { .api }
432
class WindowEvent(Event):
433
"""Window state change event."""
434
435
event_type: WindowEventType # Event type (close, focus, etc.)
436
```
437
438
## Interactive Controls Events
439
440
### ReorderableListView
441
442
```python { .api }
443
class ReorderableListView(Control):
444
"""List with reorderable items."""
445
446
def __init__(
447
self,
448
controls: List[Control] = None,
449
on_reorder: callable = None,
450
padding: PaddingValue = None,
451
**kwargs
452
)
453
```
454
455
### OnReorderEvent
456
457
```python { .api }
458
class OnReorderEvent(ControlEvent):
459
"""List reorder event."""
460
461
old_index: int # Original item index
462
new_index: int # New item index
463
```
464
465
**Example:**
466
```python
467
def handle_reorder(e):
468
# Reorder items in data model
469
item = items.pop(e.old_index)
470
items.insert(e.new_index, item)
471
print(f"Moved item from {e.old_index} to {e.new_index}")
472
473
items = ["Item 1", "Item 2", "Item 3", "Item 4"]
474
475
ft.ReorderableListView(
476
controls=[
477
ft.ListTile(title=ft.Text(item)) for item in items
478
],
479
on_reorder=handle_reorder
480
)
481
```
482
483
### InteractiveViewer
484
485
```python { .api }
486
class InteractiveViewer(Control):
487
"""Zoomable and pannable content viewer."""
488
489
def __init__(
490
self,
491
content: Control = None,
492
clip_behavior: ClipBehavior = None,
493
pan_enabled: bool = True,
494
scale_enabled: bool = True,
495
double_tap_to_zoom_enabled: bool = True,
496
min_scale: float = None,
497
max_scale: float = None,
498
interaction_end_friction_coefficient: float = None,
499
on_interaction_start: callable = None,
500
on_interaction_update: callable = None,
501
on_interaction_end: callable = None,
502
**kwargs
503
)
504
```
505
506
**Parameters:**
507
- `content` (Control, optional): Content to make interactive
508
- `pan_enabled` (bool, optional): Enable panning
509
- `scale_enabled` (bool, optional): Enable scaling/zooming
510
- `min_scale` (float, optional): Minimum zoom scale
511
- `max_scale` (float, optional): Maximum zoom scale
512
513
## Real-time Communication
514
515
### PubSub System
516
517
```python { .api }
518
class PubSubClient(Control):
519
"""Publish-subscribe client for real-time communication."""
520
521
def __init__(
522
self,
523
on_message: callable = None,
524
**kwargs
525
)
526
527
def send_all(self, message: str) -> None:
528
"""Send message to all subscribers."""
529
530
def send_all_on_topic(self, topic: str, message: str) -> None:
531
"""Send message to topic subscribers."""
532
533
def send_others(self, message: str) -> None:
534
"""Send message to other subscribers."""
535
536
def send_others_on_topic(self, topic: str, message: str) -> None:
537
"""Send message to other topic subscribers."""
538
```
539
540
**Example:**
541
```python
542
def on_message_received(e):
543
messages.controls.append(
544
ft.Text(f"{e.topic}: {e.message}")
545
)
546
page.update()
547
548
pubsub = ft.PubSubClient(
549
on_message=on_message_received
550
)
551
552
def send_message(e):
553
message = message_field.value
554
if message:
555
pubsub.send_all_on_topic("chat", message)
556
message_field.value = ""
557
page.update()
558
559
message_field = ft.TextField(
560
hint_text="Enter message",
561
on_submit=send_message
562
)
563
564
messages = ft.Column(scroll=ft.ScrollMode.AUTO)
565
```
566
567
## Advanced Event Patterns
568
569
### Event Delegation
570
571
```python
572
class EventManager:
573
"""Centralized event management."""
574
575
def __init__(self, page):
576
self.page = page
577
self.handlers = {}
578
579
def register_handler(self, event_type, handler):
580
if event_type not in self.handlers:
581
self.handlers[event_type] = []
582
self.handlers[event_type].append(handler)
583
584
def emit_event(self, event_type, data):
585
if event_type in self.handlers:
586
for handler in self.handlers[event_type]:
587
handler(data)
588
589
def handle_control_event(self, e):
590
# Delegate to registered handlers
591
self.emit_event(f"{e.control.data}_click", e)
592
593
# Usage
594
event_manager = EventManager(page)
595
event_manager.register_handler("menu_item_click", handle_menu_click)
596
event_manager.register_handler("button_click", handle_button_click)
597
```
598
599
### Gesture Composition
600
601
```python
602
def create_multi_gesture_control():
603
"""Control with multiple gesture handlers."""
604
605
def on_tap(e):
606
print("Single tap")
607
608
def on_double_tap(e):
609
print("Double tap")
610
611
def on_long_press(e):
612
print("Long press")
613
614
def on_pan_update(e):
615
# Handle panning
616
pass
617
618
def on_scale_update(e):
619
# Handle pinch-to-zoom
620
pass
621
622
return ft.GestureDetector(
623
content=ft.Container(
624
content=ft.Text("Multi-gesture area"),
625
width=300, height=200,
626
bgcolor=ft.colors.BLUE_100,
627
alignment=ft.alignment.center
628
),
629
on_tap=on_tap,
630
on_double_tap=on_double_tap,
631
on_long_press_start=on_long_press,
632
on_pan_update=on_pan_update,
633
on_scale_update=on_scale_update
634
)
635
```
636
637
### Event Throttling
638
639
```python
640
import time
641
642
class ThrottledEventHandler:
643
"""Throttle event handling to prevent spam."""
644
645
def __init__(self, delay_ms=100):
646
self.delay_ms = delay_ms
647
self.last_call = 0
648
649
def throttle(self, func):
650
def wrapper(*args, **kwargs):
651
now = time.time() * 1000
652
if now - self.last_call >= self.delay_ms:
653
self.last_call = now
654
return func(*args, **kwargs)
655
return wrapper
656
657
# Usage
658
throttler = ThrottledEventHandler(delay_ms=200)
659
660
@throttler.throttle
661
def on_scroll_throttled(e):
662
# Handle scroll event with throttling
663
print(f"Throttled scroll: {e.pixels}")
664
665
scrollable_list.on_scroll = on_scroll_throttled
666
```
667
668
### State Management with Events
669
670
```python
671
class AppState:
672
"""Application state management with events."""
673
674
def __init__(self):
675
self.data = {}
676
self.listeners = {}
677
678
def set_state(self, key, value):
679
old_value = self.data.get(key)
680
self.data[key] = value
681
682
# Notify listeners
683
if key in self.listeners:
684
for listener in self.listeners[key]:
685
listener(value, old_value)
686
687
def get_state(self, key):
688
return self.data.get(key)
689
690
def listen(self, key, callback):
691
if key not in self.listeners:
692
self.listeners[key] = []
693
self.listeners[key].append(callback)
694
695
# Usage
696
app_state = AppState()
697
698
def on_counter_change(new_value, old_value):
699
counter_text.value = f"Count: {new_value}"
700
page.update()
701
702
app_state.listen("counter", on_counter_change)
703
704
def increment_counter(e):
705
current = app_state.get_state("counter") or 0
706
app_state.set_state("counter", current + 1)
707
708
ft.ElevatedButton("Increment", on_click=increment_counter)
709
```
710
711
## Performance Optimization
712
713
### Efficient Event Handling
714
715
```python
716
# Use event delegation for many similar controls
717
def create_efficient_list():
718
def handle_item_click(e):
719
# Single handler for all items
720
item_index = int(e.control.data)
721
print(f"Clicked item {item_index}")
722
723
items = []
724
for i in range(1000):
725
item = ft.ListTile(
726
title=ft.Text(f"Item {i}"),
727
data=str(i), # Store index in data
728
on_click=handle_item_click
729
)
730
items.append(item)
731
732
return ft.ListView(controls=items)
733
734
# Debounce rapid events
735
def debounce(delay_ms):
736
def decorator(func):
737
timer = None
738
def wrapper(*args, **kwargs):
739
nonlocal timer
740
if timer:
741
timer.cancel()
742
timer = threading.Timer(delay_ms / 1000, func, args, kwargs)
743
timer.start()
744
return wrapper
745
return decorator
746
747
@debounce(300)
748
def on_search_input(e):
749
# Debounced search
750
search_term = e.control.value
751
perform_search(search_term)
752
```
753
754
This covers Flet's comprehensive event system and interaction capabilities, enabling you to create highly interactive and responsive applications with sophisticated user interactions.