or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdcharts-visualization.mdevents-interaction.mdindex.mdlayout-navigation.mdtheming-styling.mdui-controls.mdutilities-platform.md

events-interaction.mddocs/

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.