Skip to content

Commit d86a8a2

Browse files
committed
Add with_data support to FilePicker
Add support for returning file contents (bytes) from FilePicker via a new with_data flag. Dart changes: introduce withData handling, include bytes in FilePickerFile map, and toggle withReadStream accordingly. Python SDK: add bytes field and docstring to FilePickerFile, expose with_data parameter in pick_files payload, normalize incoming byte arrays to bytes, and update returned objects. Also add an example script (pick_and_save_text_content.py) demonstrating picking and saving text content.
1 parent fd579db commit d86a8a2

File tree

4 files changed

+112
-8
lines changed

4 files changed

+112
-8
lines changed

packages/flet/lib/src/services/file_picker.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class FilePickerService extends FletService {
3434
var allowedExtensions = (args["allowed_extensions"] as List?)
3535
?.map((e) => e.toString())
3636
.toList();
37+
var withData = args["with_data"] == true;
3738
var srcBytes = args["src_bytes"];
3839

3940
if (allowedExtensions != null && allowedExtensions.isNotEmpty) {
@@ -53,16 +54,17 @@ class FilePickerService extends FletService {
5354
type: fileType,
5455
allowedExtensions: allowedExtensions,
5556
allowMultiple: args["allow_multiple"],
56-
withData: false,
57-
withReadStream: true))
57+
withData: withData,
58+
withReadStream: !withData))
5859
?.files;
5960
return _files != null
6061
? _files!.asMap().entries.map((file) {
6162
return FilePickerFile(
6263
id: file.key, // use entry's index as id
6364
name: file.value.name,
6465
path: kIsWeb ? null : file.value.path,
65-
size: file.value.size)
66+
size: file.value.size,
67+
bytes: withData ? file.value.bytes : null)
6668
.toMap();
6769
}).toList()
6870
: [];

packages/flet/lib/src/utils/file_picker.dart

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'dart:typed_data';
2+
13
import 'package:file_picker/file_picker.dart';
24
import 'enums.dart';
35

@@ -20,15 +22,22 @@ class FilePickerFile {
2022
final String name;
2123
final String? path;
2224
final int size;
25+
final Uint8List? bytes;
2326

2427
FilePickerFile(
2528
{required this.id,
2629
required this.name,
2730
required this.path,
28-
required this.size});
31+
required this.size,
32+
required this.bytes});
2933

30-
Map<String, dynamic> toMap() =>
31-
<String, dynamic>{'id': id, 'name': name, 'path': path, 'size': size};
34+
Map<String, dynamic> toMap() => <String, dynamic>{
35+
'id': id,
36+
'name': name,
37+
'path': path,
38+
'size': size,
39+
'bytes': bytes
40+
};
3241
}
3342

3443
class FilePickerUploadFile {
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#
2+
# Example of picking and saving text content with FilePicker.
3+
#
4+
# Run this example with:
5+
# uv run flet run --web examples/services/file_picker/pick_and_save_text_content.py
6+
#
7+
import flet as ft
8+
9+
10+
def main(page: ft.Page):
11+
selected_file_name = ft.Text("No file selected")
12+
selected_file_content = ft.TextField(
13+
label="Selected file content",
14+
multiline=True,
15+
min_lines=8,
16+
max_lines=14,
17+
)
18+
save_status = ft.Text()
19+
20+
async def pick_text_file(_: ft.Event[ft.Button]):
21+
files = await ft.FilePicker().pick_files(
22+
allow_multiple=False,
23+
with_data=True,
24+
file_type=ft.FilePickerFileType.CUSTOM,
25+
allowed_extensions=["txt", "md"],
26+
)
27+
if not files:
28+
selected_file_name.value = "Selection cancelled"
29+
selected_file_content.value = ""
30+
page.update()
31+
return
32+
33+
selected = files[0]
34+
selected_file_name.value = f"Selected: {selected.name} ({selected.size} bytes)"
35+
selected_file_content.value = (
36+
selected.bytes.decode("utf-8", errors="replace") if selected.bytes else ""
37+
)
38+
save_status.value = ""
39+
page.update()
40+
41+
async def save_text_file(_: ft.Event[ft.Button]):
42+
file_name = "flet_text_content.txt"
43+
file_path = await ft.FilePicker().save_file(
44+
file_name=file_name,
45+
file_type=ft.FilePickerFileType.CUSTOM,
46+
allowed_extensions=["txt"],
47+
src_bytes=selected_file_content.value.encode("utf-8"),
48+
)
49+
if page.web:
50+
save_status.value = f"Downloaded as {file_name}"
51+
else:
52+
save_status.value = (
53+
f"Saved to: {file_path}" if file_path else "Save cancelled"
54+
)
55+
page.update()
56+
57+
page.add(
58+
ft.Text("Pick a .txt/.md file and load its text from FilePickerFile.bytes"),
59+
ft.Button(
60+
content="Pick text file",
61+
icon=ft.Icons.UPLOAD_FILE,
62+
on_click=pick_text_file,
63+
),
64+
selected_file_name,
65+
selected_file_content,
66+
ft.Button(
67+
content="Save / Download text",
68+
icon=ft.Icons.DOWNLOAD,
69+
on_click=save_text_file,
70+
),
71+
save_status,
72+
)
73+
74+
75+
ft.run(main)

sdk/python/packages/flet/src/flet/controls/services/file_picker.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from dataclasses import dataclass
22
from enum import Enum
3-
from typing import Optional
3+
from typing import Any, Optional
44

55
from flet.controls.base_control import control
66
from flet.controls.control_event import Event, EventHandler
@@ -131,6 +131,14 @@ class FilePickerFile:
131131
does not expose a filesystem path.
132132
"""
133133

134+
bytes: Optional[bytes] = None
135+
"""
136+
File contents.
137+
138+
Returned only when [`pick_files()`][(c).pick_files] is called with
139+
`with_data=True`. Otherwise this value is `None`.
140+
"""
141+
134142

135143
@dataclass
136144
class FilePickerUploadEvent(Event["FilePicker"]):
@@ -299,6 +307,7 @@ async def pick_files(
299307
file_type: FilePickerFileType = FilePickerFileType.ANY,
300308
allowed_extensions: Optional[list[str]] = None,
301309
allow_multiple: bool = False,
310+
with_data: bool = False,
302311
) -> list[FilePickerFile]:
303312
"""
304313
Opens a pick file dialog.
@@ -312,6 +321,8 @@ async def pick_files(
312321
initial_directory: The initial directory where the dialog should open.
313322
file_type: The file types allowed to be selected.
314323
allow_multiple: Allow the selection of multiple files at once.
324+
with_data: Read selected file contents into
325+
[`FilePickerFile.bytes`][flet.FilePickerFile.].
315326
allowed_extensions: The allowed file extensions. Has effect only if
316327
`file_type` is [`FilePickerFileType.CUSTOM`][flet.].
317328
@@ -326,7 +337,14 @@ async def pick_files(
326337
"file_type": file_type,
327338
"allowed_extensions": allowed_extensions,
328339
"allow_multiple": allow_multiple,
340+
"with_data": with_data,
329341
},
330342
timeout=3600,
331343
)
332-
return [FilePickerFile(**file) for file in files]
344+
return [FilePickerFile(**self._normalize_file(file)) for file in files]
345+
346+
def _normalize_file(self, file: dict[str, Any]) -> dict[str, Any]:
347+
value = file.get("bytes")
348+
if isinstance(value, list):
349+
file["bytes"] = bytes(value)
350+
return file

0 commit comments

Comments
 (0)