Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions client/integration_test/flutter_tester.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,20 +71,21 @@ class FlutterWidgetTester implements Tester {
}

@override
Future<void> tap(TestFinder finder) =>
_tester.tap((finder as FlutterTestFinder).raw);
Future<void> tap(TestFinder finder, int finderIndex) =>
_tester.tap((finder as FlutterTestFinder).raw.at(finderIndex));

@override
Future<void> longPress(TestFinder finder) =>
_tester.longPress((finder as FlutterTestFinder).raw);

Future<void> longPress(TestFinder finder, int finderIndex) =>
_tester.longPress((finder as FlutterTestFinder).raw.at(finderIndex));
@override
Future<void> enterText(TestFinder finder, String text) =>
_tester.enterText((finder as FlutterTestFinder).raw, text);
Future<void> enterText(TestFinder finder, int finderIndex, String text) =>
_tester.enterText(
(finder as FlutterTestFinder).raw.at(finderIndex), text);

@override
Future<void> mouseHover(TestFinder finder) async {
final center = _tester.getCenter((finder as FlutterTestFinder).raw);
Future<void> mouseHover(TestFinder finder, int finderIndex) async {
final center =
_tester.getCenter((finder as FlutterTestFinder).raw.at(finderIndex));
final gesture = await _tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
await gesture.moveTo(center);
Expand Down
16 changes: 12 additions & 4 deletions packages/flet/lib/src/controls/time_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class _TimePickerControlState extends State<TimePickerControl> {

var open = widget.control.getBool("open", false)!;
var value = widget.control.getTimeOfDay("value", TimeOfDay.now())!;
var hourFormat = widget.control.getString("hour_format");

void onClosed(TimeOfDay? timeValue) {
widget.control.updateProperties({"_open": false}, python: false);
Expand All @@ -44,15 +45,22 @@ class _TimePickerControlState extends State<TimePickerControl> {
hourLabelText: widget.control.getString("hour_label_text"),
minuteLabelText: widget.control.getString("minute_label_text"),
errorInvalidText: widget.control.getString("error_invalid_text"),
initialEntryMode: widget.control.getTimePickerEntryMode(
"time_picker_entry_mode", TimePickerEntryMode.dial)!,
initialEntryMode: widget.control
.getTimePickerEntryMode("entry_mode", TimePickerEntryMode.dial)!,
orientation: widget.control.getOrientation("orientation"),
onEntryModeChanged: (TimePickerEntryMode mode) {
widget.control.triggerEvent("entry_mode_change", mode.name);
widget.control.updateProperties({"entry_mode": mode.name});
widget.control
.triggerEvent("entry_mode_change", {"entry_mode": mode.name});
},
);

return dialog;
final hourFormatMap = {"h12": false, "h24": true, "system": null};
return MediaQuery(
data: MediaQuery.of(context)
.copyWith(alwaysUse24HourFormat: hourFormatMap[hourFormat]),
child: dialog,
);
}

if (open && (open != lastOpen)) {
Expand Down
12 changes: 7 additions & 5 deletions packages/flet/lib/src/flet_backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,13 @@ class FletBackend extends ChangeNotifier {
};
Brightness platformBrightness = Brightness.light;
PageMediaData media = PageMediaData(
padding: PaddingData(EdgeInsets.zero),
viewPadding: PaddingData(EdgeInsets.zero),
viewInsets: PaddingData(EdgeInsets.zero),
devicePixelRatio: 0,
orientation: Orientation.portrait);
padding: PaddingData(EdgeInsets.zero),
viewPadding: PaddingData(EdgeInsets.zero),
viewInsets: PaddingData(EdgeInsets.zero),
devicePixelRatio: 0,
orientation: Orientation.portrait,
alwaysUse24HourFormat: false,
);
TargetPlatform platform = defaultTargetPlatform;

late Control _page;
Expand Down
13 changes: 11 additions & 2 deletions packages/flet/lib/src/protocol/page_media_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ class PageMediaData extends Equatable {
final PaddingData viewInsets;
final double devicePixelRatio;
final Orientation orientation;
final bool alwaysUse24HourFormat;

const PageMediaData({
required this.padding,
required this.viewPadding,
required this.viewInsets,
required this.devicePixelRatio,
required this.orientation,
required this.alwaysUse24HourFormat,
});

Map<String, dynamic> toMap() => <String, dynamic>{
Expand All @@ -22,11 +24,18 @@ class PageMediaData extends Equatable {
'view_insets': viewInsets.toMap(),
'device_pixel_ratio': devicePixelRatio,
'orientation': orientation.name,
'always_use_24_hour_format': alwaysUse24HourFormat,
};

@override
List<Object?> get props =>
[padding, viewPadding, viewInsets, devicePixelRatio, orientation];
List<Object?> get props => [
padding,
viewPadding,
viewInsets,
devicePixelRatio,
orientation,
alwaysUse24HourFormat,
];
}

class PaddingData extends Equatable {
Expand Down
20 changes: 11 additions & 9 deletions packages/flet/lib/src/services/tester.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class TesterService extends FletService {
? control.backend.globalKeys[controlKey.toString()]
: ValueKey(controlKey.value);
if (key == null) {
throw Exception("Scroll key not found: $key");
throw Exception("Key not found: $key");
}
var finder = control.backend.tester!.findByKey(key);
_finders[finder.id] = finder;
Expand All @@ -74,27 +74,29 @@ class TesterService extends FletService {
return await control.backend.tester!.takeScreenshot(args["name"]);

case "tap":
var finder = _finders[args["id"]];
var finder = _finders[args["finder_id"]];
if (finder != null) {
await control.backend.tester!.tap(finder);
await control.backend.tester!.tap(finder, args["finder_index"]);
}

case "long_press":
var finder = _finders[args["id"]];
var finder = _finders[args["finder_id"]];
if (finder != null) {
await control.backend.tester!.longPress(finder);
await control.backend.tester!.longPress(finder, args["finder_index"]);
}

case "enter_text":
var finder = _finders[args["id"]];
var finder = _finders[args["finder_id"]];
if (finder != null) {
await control.backend.tester!.enterText(finder, args["text"]);
await control.backend.tester!
.enterText(finder, args["finder_index"], args["text"]);
}

case "mouse_hover":
var finder = _finders[args["id"]];
var finder = _finders[args["finder_id"]];
if (finder != null) {
await control.backend.tester!.mouseHover(finder);
await control.backend.tester!
.mouseHover(finder, args["finder_index"]);
}

case "teardown":
Expand Down
8 changes: 4 additions & 4 deletions packages/flet/lib/src/testing/tester.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ abstract class Tester {
TestFinder findByTooltip(String value);
TestFinder findByIcon(IconData icon);
Future<Uint8List> takeScreenshot(String name);
Future<void> tap(TestFinder finder);
Future<void> longPress(TestFinder finder);
Future<void> enterText(TestFinder finder, String text);
Future<void> mouseHover(TestFinder finder);
Future<void> tap(TestFinder finder, int finderIndex);
Future<void> longPress(TestFinder finder, int finderIndex);
Future<void> enterText(TestFinder finder, int finderIndex, String text);
Future<void> mouseHover(TestFinder finder, int finderIndex);
void teardown();
Future waitForTeardown();
}
1 change: 1 addition & 0 deletions packages/flet/lib/src/widgets/page_media.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class _PageMediaState extends State<PageMedia> {
viewInsets: PaddingData(MediaQuery.viewInsetsOf(context)),
devicePixelRatio: MediaQuery.devicePixelRatioOf(context),
orientation: MediaQuery.orientationOf(context),
alwaysUse24HourFormat: MediaQuery.alwaysUse24HourFormatOf(context),
);

if (newMedia != backend.media || !pageSizeUpdated) {
Expand Down
18 changes: 11 additions & 7 deletions sdk/python/examples/controls/time_picker/basic.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import flet as ft
from datetime import time

import flet as ft


def main(page: ft.Page):
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER

def handle_change(e: ft.Event[ft.TimePicker]):
page.add(ft.Text(f"TimePicker change: {time_picker.value}"))
selection.value = f"Selection: {time_picker.value}"
page.show_dialog(ft.SnackBar(f"TimePicker change: {time_picker.value}"))

def handle_dismissal(e: ft.Event[ft.TimePicker]):
page.add(ft.Text(f"TimePicker dismissed: {time_picker.value}"))
page.show_dialog(ft.SnackBar("TimePicker dismissed!"))

def handle_entry_mode_change(e: ft.TimePickerEntryModeChangeEvent):
page.add(ft.Text(f"TimePicker Entry mode changed to {e.entry_mode}"))
page.show_dialog(ft.SnackBar(f"Entry mode changed: {time_picker.entry_mode}"))

time_picker = ft.TimePicker(
value=time(1, 2),
value=time(hour=1, minute=2),
confirm_text="Confirm",
error_invalid_text="Time out of range",
help_text="Pick your time slot",
entry_mode=ft.TimePickerEntryMode.DIAL,
on_change=handle_change,
on_dismiss=handle_dismissal,
on_entry_mode_change=handle_entry_mode_change,
Expand All @@ -28,8 +31,9 @@ def handle_entry_mode_change(e: ft.TimePickerEntryModeChangeEvent):
ft.Button(
content="Pick time",
icon=ft.Icons.TIME_TO_LEAVE,
on_click=lambda _: page.show_dialog(time_picker),
)
on_click=lambda: page.show_dialog(time_picker),
),
selection := ft.Text(weight=ft.FontWeight.BOLD),
)


Expand Down
70 changes: 70 additions & 0 deletions sdk/python/examples/controls/time_picker/hour_formats.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from datetime import time

import flet as ft


def main(page: ft.Page):
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER

def get_system_hour_format():
"""Returns the current system's hour format."""
return "24h" if page.media.always_use_24_hour_format else "12h"

def format_time(value: time) -> str:
"""Returns a formatted time string based on TimePicker and system settings."""
use_24h = time_picker.hour_format == ft.TimePickerHourFormat.H24 or (
time_picker.hour_format == ft.TimePickerHourFormat.SYSTEM
and page.media.always_use_24_hour_format
)
return value.strftime("%H:%M" if use_24h else "%I:%M %p")

def handle_change(e: ft.Event[ft.TimePicker]):
selection.value = f"Selection: {format_time(time_picker.value)}"

time_picker = ft.TimePicker(
value=time(hour=9, minute=30),
help_text="Choose your meeting time",
on_change=handle_change,
)

def open_picker(e: ft.Event[ft.Button]):
choice = format_dropdown.value
hour_format_map = {
"system": ft.TimePickerHourFormat.SYSTEM,
"12h": ft.TimePickerHourFormat.H12,
"24h": ft.TimePickerHourFormat.H24,
}
time_picker.hour_format = hour_format_map[choice]
page.show_dialog(time_picker)

page.add(
ft.Row(
alignment=ft.MainAxisAlignment.CENTER,
controls=[
format_dropdown := ft.Dropdown(
label="Hour format",
value="system",
width=260,
key="dd",
options=[
ft.DropdownOption(
key="system",
text=f"System default ({get_system_hour_format()})",
),
ft.DropdownOption(key="12h", text="12-hour clock"),
ft.DropdownOption(key="24h", text="24-hour clock"),
],
),
ft.Button(
"Open TimePicker",
icon=ft.Icons.SCHEDULE,
on_click=open_picker,
),
],
),
selection := ft.Text(weight=ft.FontWeight.BOLD),
)


if __name__ == "__main__":
ft.run(main)
10 changes: 9 additions & 1 deletion sdk/python/packages/flet/docs/controls/timepicker.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,15 @@ example_images: ../test-images/examples/material/golden/macos/time_picker
--8<-- "{{ examples }}/basic.py"
```

{{ image(example_images + "/basic.png", alt="basic", width="80%") }}
{{ image(example_images + "/basic.png", width="80%") }}

### Hour Formats

```python
--8<-- "{{ examples }}/hour_formats.py"
```

{{ image(example_images + "/hour_formats.gif", width="80%") }}


{{ class_members(class_name) }}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{ class_all_options("flet.TimePickerHourFormat", separate_signature=False) }}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ async def test_basic(flet_app: ftt.FletTestApp, request):
),
)

blue_option = await flet_app.tester.find_by_text("blue")
assert blue_option.count == 2

await flet_app.tester.tap(blue_option.last)
await flet_app.tester.pump_and_settle()
flet_app.assert_screenshot(
"basic_2",
await flet_app.page.take_screenshot(
pixel_ratio=flet_app.screenshots_pixel_ratio
),
)


@pytest.mark.asyncio(loop_scope="function")
async def test_theme(flet_app: ftt.FletTestApp, request):
Expand Down
Loading
Loading