-
Notifications
You must be signed in to change notification settings - Fork 579
v1: DateRangePicker and more integration tests #5642
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
3dee2c5
c9d957f
6a46316
c1bca60
259451a
8e75d43
a1918b9
26a8956
45830ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import 'package:flutter/material.dart'; | ||
|
||
import '../models/control.dart'; | ||
import '../utils/colors.dart'; | ||
import '../utils/form_field.dart'; | ||
import '../utils/icons.dart'; | ||
import '../utils/numbers.dart'; | ||
import '../utils/time.dart'; | ||
|
||
class DateRangePickerControl extends StatefulWidget { | ||
final Control control; | ||
|
||
DateRangePickerControl({Key? key, required this.control}) | ||
: super(key: key ?? ValueKey("control_${control.id}")); | ||
|
||
@override | ||
State<DateRangePickerControl> createState() => _DateRangePickerControlState(); | ||
} | ||
|
||
class _DateRangePickerControlState extends State<DateRangePickerControl> { | ||
@override | ||
Widget build(BuildContext context) { | ||
debugPrint("DateRangePicker build: ${widget.control.id}"); | ||
|
||
bool lastOpen = widget.control.getBool("_open", false)!; | ||
|
||
var open = widget.control.getBool("open", false)!; | ||
var currentDate = widget.control.getDateTime("current_date"); | ||
var startValue = widget.control.getDateTime("start_value"); | ||
var endValue = widget.control.getDateTime("end_value"); | ||
var value = DateTimeRange<DateTime>( | ||
start: startValue ?? currentDate ?? DateTime.now(), | ||
end: endValue ?? currentDate ?? DateTime.now()); | ||
|
||
var switchToCalendarEntryModeIcon = | ||
widget.control.getIconData("switch_to_calendar_icon"); | ||
var switchToInputEntryModeIcon = | ||
widget.control.getIconData("switch_to_input_icon"); | ||
|
||
void onClosed(DateTimeRange<DateTime>? dateRangeValue) { | ||
widget.control.updateProperties({"_open": false}, python: false); | ||
var props = { | ||
"start_value": dateRangeValue?.start ?? startValue, | ||
"end_value": dateRangeValue?.end ?? endValue, | ||
"open": false | ||
}; | ||
widget.control.updateProperties(props); | ||
if (dateRangeValue != null) { | ||
widget.control.triggerEvent("change", dateRangeValue); | ||
} | ||
widget.control.triggerEvent("dismiss", dateRangeValue == null); | ||
} | ||
|
||
Widget createSelectDateDialog() { | ||
Widget dialog = DateRangePickerDialog( | ||
initialDateRange: value, | ||
firstDate: widget.control.getDateTime("first_date", DateTime(1900))!, | ||
lastDate: widget.control.getDateTime("last_date", DateTime(2050))!, | ||
currentDate: currentDate ?? DateTime.now(), | ||
helpText: widget.control.getString("help_text"), | ||
cancelText: widget.control.getString("cancel_text"), | ||
confirmText: widget.control.getString("confirm_text"), | ||
saveText: widget.control.getString("save_text"), | ||
errorInvalidRangeText: | ||
widget.control.getString("error_invalid_range_text"), | ||
errorFormatText: widget.control.getString("error_format_text"), | ||
errorInvalidText: widget.control.getString("error_invalid_text"), | ||
fieldStartHintText: widget.control.getString("field_start_hint_text"), | ||
fieldEndHintText: widget.control.getString("field_end_hint_text"), | ||
fieldStartLabelText: widget.control.getString("field_start_label_text"), | ||
fieldEndLabelText: widget.control.getString("field_end_label_text"), | ||
keyboardType: parseTextInputType( | ||
widget.control.getString("keyboard_type"), TextInputType.text)!, | ||
initialEntryMode: widget.control.getDatePickerEntryMode( | ||
"date_picker_entry_mode", DatePickerEntryMode.calendar)!, | ||
switchToCalendarEntryModeIcon: switchToCalendarEntryModeIcon != null | ||
? Icon(switchToCalendarEntryModeIcon) | ||
: null, | ||
switchToInputEntryModeIcon: switchToInputEntryModeIcon != null | ||
? Icon(switchToInputEntryModeIcon) | ||
: null, | ||
); | ||
|
||
return dialog; | ||
} | ||
|
||
if (open && (open != lastOpen)) { | ||
widget.control.updateProperties({"_open": open}, python: false); | ||
|
||
WidgetsBinding.instance.addPostFrameCallback((_) { | ||
showDialog<DateTimeRange<DateTime>>( | ||
barrierDismissible: !widget.control.getBool("modal", false)!, | ||
barrierColor: widget.control.getColor("barrier_color", context), | ||
useRootNavigator: false, | ||
context: context, | ||
builder: (context) => createSelectDateDialog()).then((result) { | ||
debugPrint("pickDate() completed"); | ||
onClosed(result); | ||
}); | ||
}); | ||
} | ||
return const SizedBox.shrink(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import datetime | ||
|
||
import flet as ft | ||
|
||
|
||
def main(page: ft.Page): | ||
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER | ||
|
||
def handle_change(e): | ||
page.add( | ||
ft.Text( | ||
f"Start Date changed: {e.control.start_value.strftime('%m/%d/%Y')}" | ||
), | ||
ft.Text(f"End Date changed: {e.control.end_value.strftime('%m/%d/%Y')}"), | ||
) | ||
|
||
def handle_dismissal(e): | ||
page.add(ft.Text("DatePicker dismissed")) | ||
|
||
page.add( | ||
ft.Button( | ||
content=ft.Text("Pick date"), | ||
icon=ft.Icons.PHONE, | ||
on_click=lambda e: page.show_dialog( | ||
ft.DateRangePicker( | ||
start_value=datetime.datetime(year=2000, month=10, day=1), | ||
end_value=datetime.datetime(year=2000, month=10, day=15), | ||
on_change=handle_change, | ||
on_dismiss=handle_dismissal, | ||
) | ||
), | ||
) | ||
) | ||
|
||
|
||
ft.run(main) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
## Examples | ||
|
||
[Live example](https://flet-controls-gallery.fly.dev/dialogs/daterangepicker) | ||
|
||
### Basic Example | ||
|
||
```python | ||
--8<-- "../../examples/controls/date_range_picker/basic.py" | ||
``` | ||
|
||
{width="60%"} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I won't suggest we use references to golden images folder. The image for this example should be in |
||
/// caption | ||
/// | ||
|
||
::: flet.DateRangePicker |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import datetime | ||
|
||
import pytest | ||
import pytest_asyncio | ||
|
||
import flet as ft | ||
import flet.testing as ftt | ||
|
||
|
||
# Create a new flet_app instance for each test method | ||
@pytest_asyncio.fixture(scope="function", autouse=True) | ||
def flet_app(flet_app_function): | ||
return flet_app_function | ||
|
||
|
||
@pytest.mark.asyncio(loop_scope="function") | ||
async def test_date_picker_theme(flet_app: ftt.FletTestApp, request): | ||
flet_app.page.theme = ft.Theme( | ||
date_picker_theme=ft.DatePickerTheme( | ||
bgcolor=ft.Colors.GREEN_200, | ||
) | ||
) | ||
|
||
dp = ft.DatePicker( | ||
current_date=datetime.datetime(year=2025, month=8, day=15), | ||
first_date=datetime.datetime(year=2000, month=10, day=1), | ||
last_date=datetime.datetime(year=2025, month=10, day=1), | ||
) | ||
flet_app.page.enable_screenshots = True | ||
flet_app.page.window.width = 400 | ||
flet_app.page.window.height = 600 | ||
flet_app.page.show_dialog(dp) | ||
flet_app.page.update() | ||
await flet_app.tester.pump_and_settle() | ||
|
||
flet_app.assert_screenshot( | ||
"theme1", | ||
await flet_app.page.take_screenshot( | ||
pixel_ratio=flet_app.screenshots_pixel_ratio | ||
), | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import datetime | ||
|
||
import pytest | ||
import pytest_asyncio | ||
|
||
import flet as ft | ||
import flet.testing as ftt | ||
|
||
|
||
# Create a new flet_app instance for each test method | ||
@pytest_asyncio.fixture(scope="function", autouse=True) | ||
def flet_app(flet_app_function): | ||
return flet_app_function | ||
|
||
|
||
@pytest.mark.asyncio(loop_scope="function") | ||
async def test_basic(flet_app: ftt.FletTestApp, request): | ||
dp = ft.DateRangePicker( | ||
start_value=datetime.datetime(year=2000, month=10, day=1), | ||
end_value=datetime.datetime(year=2000, month=10, day=15), | ||
first_date=datetime.datetime(year=2000, month=10, day=1), | ||
last_date=datetime.datetime(year=2000, month=11, day=15), | ||
current_date=datetime.datetime(year=2000, month=10, day=16), | ||
) | ||
flet_app.page.enable_screenshots = True | ||
flet_app.page.window.width = 400 | ||
flet_app.page.window.height = 600 | ||
flet_app.page.show_dialog(dp) | ||
flet_app.page.update() | ||
await flet_app.tester.pump_and_settle() | ||
|
||
flet_app.assert_screenshot( | ||
"basic", | ||
await flet_app.page.take_screenshot( | ||
pixel_ratio=flet_app.screenshots_pixel_ratio | ||
), | ||
) | ||
|
||
|
||
@pytest.mark.asyncio(loop_scope="function") | ||
async def test_properties1(flet_app: ftt.FletTestApp, request): | ||
dp = ft.DateRangePicker( | ||
start_value=datetime.datetime(year=2000, month=10, day=7), | ||
end_value=datetime.datetime(year=2000, month=10, day=15), | ||
first_date=datetime.datetime(year=2000, month=10, day=1), | ||
last_date=datetime.datetime(year=2000, month=11, day=15), | ||
current_date=datetime.datetime(year=2000, month=10, day=16), | ||
switch_to_calendar_icon=ft.Icons.BABY_CHANGING_STATION, | ||
switch_to_input_icon=ft.Icons.ACCESS_ALARM, | ||
save_text="Custom save text", | ||
error_invalid_range_text="Invalid range custom text", | ||
help_text="Custom help text", | ||
cancel_text="Custom cancel text", | ||
confirm_text="Custom confirm text", | ||
error_format_text="Custom error format text", | ||
error_invalid_text="Custom error invalid text", | ||
field_end_hint_text="Custom end hint text", | ||
field_start_hint_text="Custom start hint text", | ||
field_end_label_text="Custom end label text", | ||
field_start_label_text="Custom start label text", | ||
modal=False, | ||
barrier_color=ft.Colors.RED, | ||
keyboard_type=ft.KeyboardType.EMAIL, | ||
# date_picker_entry_mode=ft.DatePickerEntryMode.CALENDAR, | ||
) | ||
flet_app.page.enable_screenshots = True | ||
flet_app.page.window.width = 400 | ||
flet_app.page.window.height = 600 | ||
flet_app.page.show_dialog(dp) | ||
flet_app.page.update() | ||
await flet_app.tester.pump_and_settle() | ||
|
||
flet_app.assert_screenshot( | ||
"properties_calendar", | ||
await flet_app.page.take_screenshot( | ||
pixel_ratio=flet_app.screenshots_pixel_ratio | ||
), | ||
) | ||
|
||
# change to input mode | ||
input_icon = await flet_app.tester.find_by_icon(ft.Icons.ACCESS_ALARM) | ||
assert input_icon.count == 1 | ||
await flet_app.tester.tap(input_icon) | ||
await flet_app.tester.pump_and_settle() | ||
flet_app.assert_screenshot( | ||
"properties_input", | ||
await flet_app.page.take_screenshot( | ||
pixel_ratio=flet_app.screenshots_pixel_ratio | ||
), | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import datetime | ||
|
||
import pytest | ||
import pytest_asyncio | ||
|
||
import flet as ft | ||
import flet.testing as ftt | ||
|
||
|
||
# Create a new flet_app instance for each test method | ||
@pytest_asyncio.fixture(scope="function", autouse=True) | ||
def flet_app(flet_app_function): | ||
return flet_app_function | ||
|
||
|
||
@pytest.mark.asyncio(loop_scope="function") | ||
async def test_date_picker_theme(flet_app: ftt.FletTestApp, request): | ||
flet_app.page.theme = ft.Theme( | ||
date_picker_theme=ft.DatePickerTheme( | ||
bgcolor=ft.Colors.GREEN_200, | ||
range_picker_bgcolor=ft.Colors.GREEN_100, | ||
range_picker_elevation=10, | ||
range_picker_header_foreground_color=ft.Colors.GREEN_900, | ||
range_picker_header_headline_text_style=ft.TextStyle(italic=True), | ||
range_picker_shape=ft.BeveledRectangleBorder( | ||
radius=20, | ||
side=ft.BorderSide(5, color=ft.Colors.PURPLE), | ||
), | ||
range_picker_header_help_text_style=ft.TextStyle(color=ft.Colors.GREEN_800), | ||
range_selection_bgcolor=ft.Colors.YELLOW_200, | ||
range_selection_overlay_color=ft.Colors.YELLOW_400, | ||
) | ||
) | ||
|
||
dp = ft.DateRangePicker( | ||
current_date=datetime.datetime(year=2025, month=8, day=15), | ||
first_date=datetime.datetime(year=2000, month=10, day=1), | ||
last_date=datetime.datetime(year=2025, month=10, day=1), | ||
start_value=datetime.datetime(year=2000, month=10, day=7), | ||
end_value=datetime.datetime(year=2000, month=10, day=15), | ||
) | ||
flet_app.page.enable_screenshots = True | ||
flet_app.page.window.width = 400 | ||
flet_app.page.window.height = 600 | ||
flet_app.page.show_dialog(dp) | ||
flet_app.page.update() | ||
await flet_app.tester.pump_and_settle() | ||
|
||
flet_app.assert_screenshot( | ||
"theme1", | ||
await flet_app.page.take_screenshot( | ||
pixel_ratio=flet_app.screenshots_pixel_ratio | ||
), | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -146,6 +146,9 @@ plugins: | |
- source_dir: ../../examples | ||
target_url_path: examples | ||
include_exts: [".png", ".gif", ".svg"] | ||
- source_dir: integration_tests | ||
target_url_path: test-images | ||
include_exts: [".png", ".gif", ".svg"] | ||
Comment on lines
+149
to
+151
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can be removed when addressing #5642 (comment). |
||
|
||
# Markdown Extensions | ||
markdown_extensions: | ||
|
@@ -268,6 +271,7 @@ nav: | |
- CupertinoPicker: controls/cupertinopicker.md | ||
- CupertinoTimerPicker: controls/cupertinotimerpicker.md | ||
- DatePicker: controls/datepicker.md | ||
- DateRangePicker: controls/daterangepicker.md | ||
- SnackBar: controls/snackbar.md | ||
- TimePicker: controls/timepicker.md | ||
- Information Displays: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please type annotate
e
in both functions?