Skip to content

Commit 9dd1530

Browse files
v1: DateRangePicker and more integration tests (#5642)
* Add DatePicker theme integration test and doc updates Introduces an integration test for DatePicker theme customization with a golden screenshot for macOS. Updates docstrings for Theme class to clarify DataTableTheme and DatePickerTheme usage. Also refines ListTile hover_color docstring to specify when it takes effect. * Comment out DateRangePicker theme properties DateRangePicker-related theme properties have been commented out in both Dart and Python implementations. This change likely reflects that these properties are not yet supported or are pending future implementation. * Add DateRangePicker control to Flet Introduced DateRangePicker control for both Dart and Python SDKs, including its registration in FletCoreExtension and Python __init__.py. This enables Material-style date range selection dialogs with customizable properties and event handling. * Add DateRangePicker control and documentation Introduces the DateRangePicker control to the Python SDK, including its implementation, example usage, documentation, and integration tests. Updates the Dart backend to support new properties and events for date range selection. Adds related documentation and navigation entry in mkdocs. * Update DateRangePicker docs and refactor entry mode import Updated the image path and size in the DateRangePicker documentation. Modified mkdocs.yml to include test images from integration_tests. Refactored date_range_picker.py to import DatePickerEntryMode from the material.date_picker module instead of defining it locally. * Enable full DateRangePicker theme customization Uncommented and activated all DateRangePicker theme properties in both Dart and Python implementations, allowing full customization of elevation, background, header, shape, and selection colors. Also improved event handling in the Dart DateRangePicker control by passing the selected range to 'change' and 'dismiss' events. * Add and update DateRangePicker integration tests Updated the 'basic' DateRangePicker test and golden image, and added a new 'properties1' test with corresponding screenshot. The new test covers various DateRangePicker properties to improve test coverage and reliability. * Update date range picker integration test screenshots Renamed properties1.png to properties_calendar.png and added properties_input.png for input mode. Modified test_date_range_picker.py to capture screenshots for both calendar and input modes, improving test coverage for date picker entry modes. * Add date range picker theme integration test Introduces a new integration test for the DateRangePicker theme on macOS, including a golden screenshot for visual regression. This ensures custom theme properties are correctly applied and rendered. * Update docstrings for DateRangePicker and ListTile Corrected references and improved clarity in docstrings for DateRangePicker and ListTile controls. Updated event handler type annotations in the date_range_picker example for better type safety. * Update doc references to use generic (c). links Replaced specific class references in docstrings with generic [(c).] links in DateRangePicker and ListTile controls. This change improves maintainability and consistency in documentation references. * Update docstrings and mkdocs config for clarity Set 'inherited_members' to false in mkdocs.yml to exclude inherited members from documentation. Updated docstrings in date_range_picker.py and divider.py for improved clarity and accuracy. * Refactor on_tap_down event type in Container Simplified the on_tap_down event handler type in the Container control to only accept TapEvent, removing support for Event. Also removed outdated documentation regarding the event argument type. --------- Co-authored-by: Feodor Fitsner <feodor@appveyor.com>
1 parent 8a91f71 commit 9dd1530

22 files changed

Lines changed: 555 additions & 28 deletions

File tree

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import 'package:flutter/material.dart';
2+
3+
import '../models/control.dart';
4+
import '../utils/colors.dart';
5+
import '../utils/form_field.dart';
6+
import '../utils/icons.dart';
7+
import '../utils/numbers.dart';
8+
import '../utils/time.dart';
9+
10+
class DateRangePickerControl extends StatefulWidget {
11+
final Control control;
12+
13+
DateRangePickerControl({Key? key, required this.control})
14+
: super(key: key ?? ValueKey("control_${control.id}"));
15+
16+
@override
17+
State<DateRangePickerControl> createState() => _DateRangePickerControlState();
18+
}
19+
20+
class _DateRangePickerControlState extends State<DateRangePickerControl> {
21+
@override
22+
Widget build(BuildContext context) {
23+
debugPrint("DateRangePicker build: ${widget.control.id}");
24+
25+
bool lastOpen = widget.control.getBool("_open", false)!;
26+
27+
var open = widget.control.getBool("open", false)!;
28+
var currentDate = widget.control.getDateTime("current_date");
29+
var startValue = widget.control.getDateTime("start_value");
30+
var endValue = widget.control.getDateTime("end_value");
31+
var value = DateTimeRange<DateTime>(
32+
start: startValue ?? currentDate ?? DateTime.now(),
33+
end: endValue ?? currentDate ?? DateTime.now());
34+
35+
var switchToCalendarEntryModeIcon =
36+
widget.control.getIconData("switch_to_calendar_icon");
37+
var switchToInputEntryModeIcon =
38+
widget.control.getIconData("switch_to_input_icon");
39+
40+
void onClosed(DateTimeRange<DateTime>? dateRangeValue) {
41+
widget.control.updateProperties({"_open": false}, python: false);
42+
var props = {
43+
"start_value": dateRangeValue?.start ?? startValue,
44+
"end_value": dateRangeValue?.end ?? endValue,
45+
"open": false
46+
};
47+
widget.control.updateProperties(props);
48+
if (dateRangeValue != null) {
49+
widget.control.triggerEvent("change", dateRangeValue);
50+
}
51+
widget.control.triggerEvent("dismiss", dateRangeValue == null);
52+
}
53+
54+
Widget createSelectDateDialog() {
55+
Widget dialog = DateRangePickerDialog(
56+
initialDateRange: value,
57+
firstDate: widget.control.getDateTime("first_date", DateTime(1900))!,
58+
lastDate: widget.control.getDateTime("last_date", DateTime(2050))!,
59+
currentDate: currentDate ?? DateTime.now(),
60+
helpText: widget.control.getString("help_text"),
61+
cancelText: widget.control.getString("cancel_text"),
62+
confirmText: widget.control.getString("confirm_text"),
63+
saveText: widget.control.getString("save_text"),
64+
errorInvalidRangeText:
65+
widget.control.getString("error_invalid_range_text"),
66+
errorFormatText: widget.control.getString("error_format_text"),
67+
errorInvalidText: widget.control.getString("error_invalid_text"),
68+
fieldStartHintText: widget.control.getString("field_start_hint_text"),
69+
fieldEndHintText: widget.control.getString("field_end_hint_text"),
70+
fieldStartLabelText: widget.control.getString("field_start_label_text"),
71+
fieldEndLabelText: widget.control.getString("field_end_label_text"),
72+
keyboardType: parseTextInputType(
73+
widget.control.getString("keyboard_type"), TextInputType.text)!,
74+
initialEntryMode: widget.control.getDatePickerEntryMode(
75+
"date_picker_entry_mode", DatePickerEntryMode.calendar)!,
76+
switchToCalendarEntryModeIcon: switchToCalendarEntryModeIcon != null
77+
? Icon(switchToCalendarEntryModeIcon)
78+
: null,
79+
switchToInputEntryModeIcon: switchToInputEntryModeIcon != null
80+
? Icon(switchToInputEntryModeIcon)
81+
: null,
82+
);
83+
84+
return dialog;
85+
}
86+
87+
if (open && (open != lastOpen)) {
88+
widget.control.updateProperties({"_open": open}, python: false);
89+
90+
WidgetsBinding.instance.addPostFrameCallback((_) {
91+
showDialog<DateTimeRange<DateTime>>(
92+
barrierDismissible: !widget.control.getBool("modal", false)!,
93+
barrierColor: widget.control.getColor("barrier_color", context),
94+
useRootNavigator: false,
95+
context: context,
96+
builder: (context) => createSelectDateDialog()).then((result) {
97+
debugPrint("pickDate() completed");
98+
onClosed(result);
99+
});
100+
});
101+
}
102+
return const SizedBox.shrink();
103+
}
104+
}

packages/flet/lib/src/flet_core_extension.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import 'controls/cupertino_textfield.dart';
4444
import 'controls/cupertino_timer_picker.dart';
4545
import 'controls/datatable.dart';
4646
import 'controls/date_picker.dart';
47+
import 'controls/date_range_picker.dart';
4748
import 'controls/dismissible.dart';
4849
import 'controls/divider.dart';
4950
import 'controls/drag_target.dart';
@@ -223,6 +224,8 @@ class FletCoreExtension extends FletExtension {
223224
return DataTableControl(key: key, control: control);
224225
case "DatePicker":
225226
return DatePickerControl(key: key, control: control);
227+
case "DateRangePicker":
228+
return DateRangePickerControl(key: key, control: control);
226229
case "Dismissible":
227230
return DismissibleControl(key: key, control: control);
228231
case "Divider":

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import 'dismissible.dart';
1616
import 'edge_insets.dart';
1717
import 'geometry.dart';
1818
import 'icons.dart';
19-
import 'locale.dart';
2019
import 'material_state.dart';
2120
import 'menu.dart';
2221
import 'misc.dart';
@@ -773,7 +772,7 @@ DatePickerThemeData? parseDatePickerTheme(
773772
yearOverlayColor: parseWidgetStateColor(value["year_overlay_color"], theme),
774773
weekdayStyle: parseTextStyle(value["weekday_text_style"], theme),
775774
dayShape: parseWidgetStateOutlinedBorder(value["day_shape"], theme),
776-
locale: parseLocale(value["locale"]),
775+
//locale: parseLocale(value["locale"]),
777776
);
778777
}
779778

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import datetime
2+
3+
import flet as ft
4+
5+
6+
def main(page: ft.Page):
7+
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
8+
9+
def handle_change(e: ft.Event[ft.DateRangePicker]):
10+
page.add(
11+
ft.Text(
12+
f"Start Date changed: {e.control.start_value.strftime('%m/%d/%Y')}"
13+
),
14+
ft.Text(f"End Date changed: {e.control.end_value.strftime('%m/%d/%Y')}"),
15+
)
16+
17+
def handle_dismissal(e: ft.Event[ft.DialogControl]):
18+
page.add(ft.Text("DatePicker dismissed"))
19+
20+
page.add(
21+
ft.Button(
22+
content=ft.Text("Pick date"),
23+
icon=ft.Icons.PHONE,
24+
on_click=lambda e: page.show_dialog(
25+
ft.DateRangePicker(
26+
start_value=datetime.datetime(year=2000, month=10, day=1),
27+
end_value=datetime.datetime(year=2000, month=10, day=15),
28+
on_change=handle_change,
29+
on_dismiss=handle_dismissal,
30+
)
31+
),
32+
)
33+
)
34+
35+
36+
ft.run(main)
83.9 KB
Loading
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
## Examples
2+
3+
[Live example](https://flet-controls-gallery.fly.dev/dialogs/daterangepicker)
4+
5+
### Basic Example
6+
7+
```python
8+
--8<-- "../../examples/controls/date_range_picker/basic.py"
9+
```
10+
11+
![basic](../test-images/controls/golden/macos/date_range_picker/basic.png){width="60%"}
12+
/// caption
13+
///
14+
15+
::: flet.DateRangePicker
55.6 KB
Loading
65.5 KB
Loading
69.2 KB
Loading
50.8 KB
Loading

0 commit comments

Comments
 (0)