diff --git a/packages/flet/lib/src/controls/bottom_sheet.dart b/packages/flet/lib/src/controls/bottom_sheet.dart index d9cb20beb1..8a9624afff 100644 --- a/packages/flet/lib/src/controls/bottom_sheet.dart +++ b/packages/flet/lib/src/controls/bottom_sheet.dart @@ -30,6 +30,10 @@ class _BottomSheetControlState extends State { var maintainBottomViewInsetsPadding = widget.control.getBool("maintain_bottom_view_insets_padding", true)!; + final fullscreen = widget.control.getBool("fullscreen", false)!; + final scrollable = + fullscreen || widget.control.getBool("scrollable", false)!; + final draggable = widget.control.getBool("draggable", false)!; if (open && !lastOpen) { widget.control.updateProperties({"_open": open}, python: false); @@ -53,19 +57,23 @@ class _BottomSheetControlState extends State { ); } + if (fullscreen) { + content = SizedBox.expand(child: content); + } + return content; }, isDismissible: widget.control.getBool("dismissible", true)!, backgroundColor: widget.control.getColor("bgcolor", context), elevation: widget.control.getDouble("elevation"), - isScrollControlled: - widget.control.getBool("scroll_controlled", false)!, - enableDrag: widget.control.getBool("enable_drag", false)!, + isScrollControlled: scrollable, + enableDrag: draggable, barrierColor: widget.control.getColor("barrier_color", context), sheetAnimationStyle: widget.control.getAnimationStyle("animation_style"), - constraints: - widget.control.getBoxConstraints("size_constraints"), + constraints: fullscreen + ? null + : widget.control.getBoxConstraints("size_constraints"), showDragHandle: widget.control.getBool("show_drag_handle", false)!, clipBehavior: widget.control.getClipBehavior("clip_behavior"), diff --git a/packages/flet/lib/src/controls/cupertino_activity_indicator.dart b/packages/flet/lib/src/controls/cupertino_activity_indicator.dart index 8e467a7b0e..1d2cd4d87e 100644 --- a/packages/flet/lib/src/controls/cupertino_activity_indicator.dart +++ b/packages/flet/lib/src/controls/cupertino_activity_indicator.dart @@ -16,11 +16,23 @@ class CupertinoActivityIndicatorControl extends StatelessWidget { @override Widget build(BuildContext context) { debugPrint("CupertinoActivityIndicatorControl build: ${control.id}"); - final activityIndicator = CupertinoActivityIndicator( - radius: control.getDouble("radius", 10)!, - animating: control.getBool("animating", true)!, - color: control.getColor("color", context), - ); + final radius = control.getDouble("radius", 10)!; + final color = control.getColor("color", context); + final progress = control.getDouble("progress"); + final bool animating = + progress == null ? control.getBool("animating", true)! : false; + + final activityIndicator = progress != null + ? CupertinoActivityIndicator.partiallyRevealed( + radius: radius, + color: color, + progress: progress, + ) + : CupertinoActivityIndicator( + radius: radius, + animating: animating, + color: color, + ); return LayoutControl(control: control, child: activityIndicator); } } diff --git a/packages/flet/lib/src/controls/date_picker.dart b/packages/flet/lib/src/controls/date_picker.dart index 6d2c65bd48..956ab22c7e 100644 --- a/packages/flet/lib/src/controls/date_picker.dart +++ b/packages/flet/lib/src/controls/date_picker.dart @@ -1,12 +1,6 @@ +import 'package:flet/flet.dart'; 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 DatePickerControl extends StatefulWidget { final Control control; @@ -58,11 +52,15 @@ class _DatePickerControlState extends State { initialCalendarMode: widget.control .getDatePickerMode("date_picker_mode", DatePickerMode.day)!, initialEntryMode: widget.control.getDatePickerEntryMode( - "date_picker_entry_mode", DatePickerEntryMode.calendar)!, + "entry_mode", DatePickerEntryMode.calendar)!, fieldHintText: widget.control.getString("field_hint_text"), fieldLabelText: widget.control.getString("field_label_text"), + insetPadding: widget.control.getPadding("inset_padding", + const EdgeInsets.symmetric(horizontal: 16.0, vertical: 24.0))!, onDatePickerModeChange: (DatePickerEntryMode 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}); }, switchToCalendarEntryModeIcon: switchToCalendarEntryModeIcon != null ? Icon(switchToCalendarEntryModeIcon) diff --git a/packages/flet/lib/src/controls/date_range_picker.dart b/packages/flet/lib/src/controls/date_range_picker.dart index 01dcd4c3d5..00b09c2b5a 100644 --- a/packages/flet/lib/src/controls/date_range_picker.dart +++ b/packages/flet/lib/src/controls/date_range_picker.dart @@ -72,7 +72,7 @@ class _DateRangePickerControlState extends State { keyboardType: parseTextInputType( widget.control.getString("keyboard_type"), TextInputType.text)!, initialEntryMode: widget.control.getDatePickerEntryMode( - "date_picker_entry_mode", DatePickerEntryMode.calendar)!, + "entry_mode", DatePickerEntryMode.calendar)!, switchToCalendarEntryModeIcon: switchToCalendarEntryModeIcon != null ? Icon(switchToCalendarEntryModeIcon) : null, diff --git a/packages/flet/lib/src/controls/time_picker.dart b/packages/flet/lib/src/controls/time_picker.dart index 115c4c3033..59007c17c9 100644 --- a/packages/flet/lib/src/controls/time_picker.dart +++ b/packages/flet/lib/src/controls/time_picker.dart @@ -1,3 +1,4 @@ +import 'package:flet/src/utils/icons.dart'; import 'package:flutter/material.dart'; import '../models/control.dart'; @@ -26,6 +27,10 @@ class _TimePickerControlState extends State { var open = widget.control.getBool("open", false)!; var value = widget.control.getTimeOfDay("value", TimeOfDay.now())!; var hourFormat = widget.control.getString("hour_format"); + var switchToTimerEntryModeIcon = + widget.control.getIconData("switch_to_timer_icon"); + var switchToInputEntryModeIcon = + widget.control.getIconData("switch_to_input_icon"); void onClosed(TimeOfDay? timeValue) { widget.control.updateProperties({"_open": false}, python: false); @@ -48,6 +53,12 @@ class _TimePickerControlState extends State { initialEntryMode: widget.control .getTimePickerEntryMode("entry_mode", TimePickerEntryMode.dial)!, orientation: widget.control.getOrientation("orientation"), + switchToTimerEntryModeIcon: switchToTimerEntryModeIcon != null + ? Icon(switchToTimerEntryModeIcon) + : null, + switchToInputEntryModeIcon: switchToInputEntryModeIcon != null + ? Icon(switchToInputEntryModeIcon) + : null, onEntryModeChanged: (TimePickerEntryMode mode) { widget.control.updateProperties({"entry_mode": mode.name}); widget.control diff --git a/sdk/python/examples/controls/bottom_sheet/__init__.py b/sdk/python/examples/controls/bottom_sheet/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sdk/python/examples/controls/bottom_sheet/basic.py b/sdk/python/examples/controls/bottom_sheet/basic.py index 076fad7954..4663461b2a 100644 --- a/sdk/python/examples/controls/bottom_sheet/basic.py +++ b/sdk/python/examples/controls/bottom_sheet/basic.py @@ -22,7 +22,7 @@ def handle_sheet_dismissal(e: ft.Event[ft.BottomSheet]): ), ), ) - page.overlay.append(sheet) + page.add( ft.Button( content="Display bottom sheet", @@ -31,4 +31,5 @@ def handle_sheet_dismissal(e: ft.Event[ft.BottomSheet]): ) -ft.run(main) +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/bottom_sheet/fullscreen.py b/sdk/python/examples/controls/bottom_sheet/fullscreen.py new file mode 100644 index 0000000000..b3696825f9 --- /dev/null +++ b/sdk/python/examples/controls/bottom_sheet/fullscreen.py @@ -0,0 +1,35 @@ +import flet as ft + + +def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + def handle_switch_change(e: ft.Event[ft.Switch]): + sheet.fullscreen = e.control.value + + sheet = ft.BottomSheet( + fullscreen=True, + show_drag_handle=True, + content=ft.Container( + padding=ft.Padding.all(10), + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + ft.Text("This is bottom sheet's content!"), + ft.Button("Close bottom sheet", on_click=lambda: page.pop_dialog()), + ], + ), + ), + ) + + page.add( + ft.Button( + content="Display bottom sheet", + on_click=lambda e: page.show_dialog(sheet), + ), + ft.Switch(value=True, label="Fullscreen", on_change=handle_switch_change), + ) + + +if __name__ == "__main__": + ft.run(main) diff --git a/sdk/python/examples/controls/bottom_sheet/media/basic.gif b/sdk/python/examples/controls/bottom_sheet/media/basic.gif deleted file mode 100644 index c075b03e5f..0000000000 Binary files a/sdk/python/examples/controls/bottom_sheet/media/basic.gif and /dev/null differ diff --git a/sdk/python/examples/controls/cupertino_activity_indicator/progress.py b/sdk/python/examples/controls/cupertino_activity_indicator/progress.py new file mode 100644 index 0000000000..da74c76db3 --- /dev/null +++ b/sdk/python/examples/controls/cupertino_activity_indicator/progress.py @@ -0,0 +1,26 @@ +import flet as ft + + +async def main(page: ft.Page): + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + page.spacing = 20 + + def handle_progress_change(e: ft.Event[ft.Slider]): + indicator.progress = e.control.value + + page.add( + indicator := ft.CupertinoActivityIndicator(progress=1.0, radius=40), + ft.Slider( + min=0.0, + value=indicator.progress, + max=1.0, + divisions=10, + round=1, + label="Progress = {value}", + width=400, + on_change=handle_progress_change, + ), + ) + + +ft.run(main) diff --git a/sdk/python/examples/controls/date_range_picker/basic.py b/sdk/python/examples/controls/date_range_picker/basic.py index 7be0a5476b..97585d0bd2 100644 --- a/sdk/python/examples/controls/date_range_picker/basic.py +++ b/sdk/python/examples/controls/date_range_picker/basic.py @@ -14,7 +14,7 @@ def handle_change(e: ft.Event[ft.DateRangePicker]): ft.Text(f"End Date changed: {e.control.end_value.strftime('%m/%d/%Y')}"), ) - def handle_dismissal(e: ft.Event[ft.DialogControl]): + def handle_dismissal(e: ft.Event[ft.DateRangePicker]): page.add(ft.Text("DatePicker dismissed")) page.add( diff --git a/sdk/python/packages/flet/docs/controls/bottomsheet.md b/sdk/python/packages/flet/docs/controls/bottomsheet.md index 7f9bd64de6..8792affaf5 100644 --- a/sdk/python/packages/flet/docs/controls/bottomsheet.md +++ b/sdk/python/packages/flet/docs/controls/bottomsheet.md @@ -2,7 +2,6 @@ class_name: flet.BottomSheet examples: ../../examples/controls/bottom_sheet example_images: ../test-images/examples/material/golden/macos/bottom_sheet -example_media: ../examples/controls/bottom_sheet/media --- {{ class_summary(class_name, example_images + "/image_for_docs.png", image_caption="Basic BottomSheet") }} @@ -17,7 +16,14 @@ example_media: ../examples/controls/bottom_sheet/media --8<-- "{{ examples }}/basic.py" ``` -{{ image(example_media + "/basic.gif", alt="basic", width="80%") }} +{{ image(example_images + "/basic.gif", width="60%") }} + +### Fullscreen + +```python +--8<-- "{{ examples }}/fullscreen.py" +``` +{{ image(example_images + "/fullscreen.gif", width="60%") }} {{ class_members(class_name) }} diff --git a/sdk/python/packages/flet/integration_tests/controls/cupertino/golden/macos/cupertino_activity_indicator/basic.png b/sdk/python/packages/flet/integration_tests/controls/cupertino/golden/macos/cupertino_activity_indicator/basic.png new file mode 100644 index 0000000000..91aea19f4b Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/cupertino/golden/macos/cupertino_activity_indicator/basic.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/cupertino/golden/macos/cupertino_activity_indicator/progress.png b/sdk/python/packages/flet/integration_tests/controls/cupertino/golden/macos/cupertino_activity_indicator/progress.png new file mode 100644 index 0000000000..023df1ee41 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/cupertino/golden/macos/cupertino_activity_indicator/progress.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/cupertino/test_cupertino_activity_indicator.py b/sdk/python/packages/flet/integration_tests/controls/cupertino/test_cupertino_activity_indicator.py new file mode 100644 index 0000000000..99f5e1b76c --- /dev/null +++ b/sdk/python/packages/flet/integration_tests/controls/cupertino/test_cupertino_activity_indicator.py @@ -0,0 +1,28 @@ +import pytest + +import flet as ft +import flet.testing as ftt + + +@pytest.mark.asyncio(loop_scope="module") +async def test_basic(flet_app: ftt.FletTestApp, request): + await flet_app.assert_control_screenshot( + request.node.name, + ft.CupertinoActivityIndicator( + radius=30, + color=ft.CupertinoColors.DARK_BACKGROUND_GRAY, + animating=False, + ), + ) + + +@pytest.mark.asyncio(loop_scope="module") +async def test_progress(flet_app: ftt.FletTestApp, request): + await flet_app.assert_control_screenshot( + request.node.name, + ft.CupertinoActivityIndicator( + radius=30, + color=ft.CupertinoColors.DARK_BACKGROUND_GRAY, + progress=0.5, + ), + ) diff --git a/sdk/python/packages/flet/integration_tests/controls/material/golden/macos/bottom_sheet/bottom_sheet_basic.png b/sdk/python/packages/flet/integration_tests/controls/material/golden/macos/bottom_sheet/basic.png similarity index 54% rename from sdk/python/packages/flet/integration_tests/controls/material/golden/macos/bottom_sheet/bottom_sheet_basic.png rename to sdk/python/packages/flet/integration_tests/controls/material/golden/macos/bottom_sheet/basic.png index 0e160d165c..9b965ade23 100644 Binary files a/sdk/python/packages/flet/integration_tests/controls/material/golden/macos/bottom_sheet/bottom_sheet_basic.png and b/sdk/python/packages/flet/integration_tests/controls/material/golden/macos/bottom_sheet/basic.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/material/golden/macos/bottom_sheet/fullscreen.png b/sdk/python/packages/flet/integration_tests/controls/material/golden/macos/bottom_sheet/fullscreen.png new file mode 100644 index 0000000000..a75ece1c2c Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/controls/material/golden/macos/bottom_sheet/fullscreen.png differ diff --git a/sdk/python/packages/flet/integration_tests/controls/material/test_bottom_sheet.py b/sdk/python/packages/flet/integration_tests/controls/material/test_bottom_sheet.py index 9e57fe9cd0..53b6404d60 100644 --- a/sdk/python/packages/flet/integration_tests/controls/material/test_bottom_sheet.py +++ b/sdk/python/packages/flet/integration_tests/controls/material/test_bottom_sheet.py @@ -5,8 +5,11 @@ @pytest.mark.asyncio(loop_scope="module") -async def test_bottom_sheet_basic(flet_app: ftt.FletTestApp, request): - bs = ft.BottomSheet( +async def test_basic(flet_app: ftt.FletTestApp, request): + flet_app.page.enable_screenshots = True + flet_app.resize_page(400, 600) + + sheet = ft.BottomSheet( content=ft.Container( padding=50, content=ft.Column( @@ -19,14 +22,43 @@ async def test_bottom_sheet_basic(flet_app: ftt.FletTestApp, request): ), ), ) + flet_app.page.show_dialog(sheet) + flet_app.page.update() + await flet_app.tester.pump_and_settle() + + flet_app.assert_screenshot( + request.node.name, + await flet_app.page.take_screenshot( + pixel_ratio=flet_app.screenshots_pixel_ratio + ), + ) + + +@pytest.mark.asyncio(loop_scope="module") +async def test_fullscreen(flet_app: ftt.FletTestApp, request): flet_app.page.enable_screenshots = True flet_app.resize_page(400, 600) - flet_app.page.show_dialog(bs) + + sheet = ft.BottomSheet( + fullscreen=True, + content=ft.Container( + padding=50, + content=ft.Column( + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + tight=True, + controls=[ + ft.Text("Here is a bottom sheet!"), + ft.Button("Dismiss", on_click=lambda _: flet_app.page.pop_dialog()), + ], + ), + ), + ) + flet_app.page.show_dialog(sheet) flet_app.page.update() await flet_app.tester.pump_and_settle() flet_app.assert_screenshot( - "bottom_sheet_basic", + request.node.name, await flet_app.page.take_screenshot( pixel_ratio=flet_app.screenshots_pixel_ratio ), diff --git a/sdk/python/packages/flet/integration_tests/controls/material/test_date_range_picker.py b/sdk/python/packages/flet/integration_tests/controls/material/test_date_range_picker.py index 783f893498..35897d6698 100644 --- a/sdk/python/packages/flet/integration_tests/controls/material/test_date_range_picker.py +++ b/sdk/python/packages/flet/integration_tests/controls/material/test_date_range_picker.py @@ -60,7 +60,7 @@ async def test_properties1(flet_app: ftt.FletTestApp, request): modal=False, barrier_color=ft.Colors.RED, keyboard_type=ft.KeyboardType.EMAIL, - # date_picker_entry_mode=ft.DatePickerEntryMode.CALENDAR, + # entry_mode=ft.DatePickerEntryMode.CALENDAR, ) flet_app.page.enable_screenshots = True flet_app.resize_page(400, 600) diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/basic.gif b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/basic.gif new file mode 100644 index 0000000000..cb51a04769 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/basic.gif differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/basic_1.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/basic_1.png new file mode 100644 index 0000000000..e1c22043e1 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/basic_1.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/basic_2.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/basic_2.png new file mode 100644 index 0000000000..0e794ab116 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/basic_2.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen.gif b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen.gif new file mode 100644 index 0000000000..71f2d5846d Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen.gif differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_1.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_1.png new file mode 100644 index 0000000000..1467fb94a6 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_1.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_2.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_2.png new file mode 100644 index 0000000000..592c628759 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_2.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_3.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_3.png new file mode 100644 index 0000000000..1467fb94a6 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_3.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_4.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_4.png new file mode 100644 index 0000000000..46c5ca0be3 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_4.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_5.png b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_5.png new file mode 100644 index 0000000000..5c502af9c6 Binary files /dev/null and b/sdk/python/packages/flet/integration_tests/examples/material/golden/macos/bottom_sheet/fullscreen_5.png differ diff --git a/sdk/python/packages/flet/integration_tests/examples/material/test_bottom_sheet.py b/sdk/python/packages/flet/integration_tests/examples/material/test_bottom_sheet.py index 0d4a43a29a..b2dd3fc901 100644 --- a/sdk/python/packages/flet/integration_tests/examples/material/test_bottom_sheet.py +++ b/sdk/python/packages/flet/integration_tests/examples/material/test_bottom_sheet.py @@ -2,11 +2,15 @@ import flet as ft import flet.testing as ftt +from examples.controls.bottom_sheet import basic, fullscreen @pytest.mark.asyncio(loop_scope="function") async def test_image_for_docs(flet_app_function: ftt.FletTestApp, request): + flet_app_function.page.enable_screenshots = True + flet_app_function.resize_page(200, 200) flet_app_function.page.theme_mode = ft.ThemeMode.LIGHT + sheet = ft.BottomSheet( content=ft.Column( width=150, @@ -18,14 +22,125 @@ async def test_image_for_docs(flet_app_function: ftt.FletTestApp, request): ], ) ) - flet_app_function.page.enable_screenshots = True - flet_app_function.resize_page(200, 200) flet_app_function.page.show_dialog(sheet) flet_app_function.page.update() await flet_app_function.tester.pump_and_settle() + flet_app_function.assert_screenshot( request.node.name, await flet_app_function.page.take_screenshot( pixel_ratio=flet_app_function.screenshots_pixel_ratio ), ) + + +@pytest.mark.parametrize( + "flet_app_function", + [{"flet_app_main": basic.main}], + indirect=True, +) +@pytest.mark.asyncio(loop_scope="function") +async def test_basic(flet_app_function: ftt.FletTestApp): + flet_app_function.page.enable_screenshots = True + flet_app_function.resize_page(350, 450) + flet_app_function.page.update() + await flet_app_function.tester.pump_and_settle() + flet_app_function.assert_screenshot( + "basic_1", + await flet_app_function.page.take_screenshot( + pixel_ratio=flet_app_function.screenshots_pixel_ratio + ), + ) + + # open bottom sheet + await flet_app_function.tester.tap( + await flet_app_function.tester.find_by_text("Display bottom sheet") + ) + await flet_app_function.tester.pump_and_settle() + flet_app_function.assert_screenshot( + "basic_2", + await flet_app_function.page.take_screenshot( + pixel_ratio=flet_app_function.screenshots_pixel_ratio + ), + ) + + # create gif + flet_app_function.create_gif( + [f"basic_{i}" for i in range(1, 3)], + output_name="basic", + duration=2000, + ) + + +@pytest.mark.parametrize( + "flet_app_function", + [{"flet_app_main": fullscreen.main}], + indirect=True, +) +@pytest.mark.asyncio(loop_scope="function") +async def test_fullscreen(flet_app_function: ftt.FletTestApp): + flet_app_function.page.enable_screenshots = True + flet_app_function.resize_page(350, 450) + flet_app_function.page.update() + await flet_app_function.tester.pump_and_settle() + flet_app_function.assert_screenshot( + "fullscreen_1", + await flet_app_function.page.take_screenshot( + pixel_ratio=flet_app_function.screenshots_pixel_ratio + ), + ) + + # open bottom sheet + await flet_app_function.tester.tap( + await flet_app_function.tester.find_by_text("Display bottom sheet") + ) + await flet_app_function.tester.pump_and_settle() + flet_app_function.assert_screenshot( + "fullscreen_2", + await flet_app_function.page.take_screenshot( + pixel_ratio=flet_app_function.screenshots_pixel_ratio + ), + ) + + # close bottom sheet + await flet_app_function.tester.tap( + await flet_app_function.tester.find_by_text("Close bottom sheet") + ) + await flet_app_function.tester.pump_and_settle() + flet_app_function.assert_screenshot( + "fullscreen_3", + await flet_app_function.page.take_screenshot( + pixel_ratio=flet_app_function.screenshots_pixel_ratio + ), + ) + + # toggle switch + await flet_app_function.tester.tap( + await flet_app_function.tester.find_by_text("Fullscreen") + ) + await flet_app_function.tester.pump_and_settle() + flet_app_function.assert_screenshot( + "fullscreen_4", + await flet_app_function.page.take_screenshot( + pixel_ratio=flet_app_function.screenshots_pixel_ratio + ), + ) + + # open bottom sheet + await flet_app_function.tester.tap( + await flet_app_function.tester.find_by_text("Display bottom sheet") + ) + await flet_app_function.tester.pump_and_settle() + flet_app_function.assert_screenshot( + "fullscreen_5", + await flet_app_function.page.take_screenshot( + pixel_ratio=flet_app_function.screenshots_pixel_ratio + ), + ) + + # create gif + flet_app_function.create_gif( + [f"fullscreen_{i}" for i in range(1, 6)], + output_name="fullscreen", + duration=2000, + ) diff --git a/sdk/python/packages/flet/src/flet/controls/cupertino/cupertino_activity_indicator.py b/sdk/python/packages/flet/src/flet/controls/cupertino/cupertino_activity_indicator.py index 80770808b6..8f3519dcf0 100644 --- a/sdk/python/packages/flet/src/flet/controls/cupertino/cupertino_activity_indicator.py +++ b/sdk/python/packages/flet/src/flet/controls/cupertino/cupertino_activity_indicator.py @@ -24,11 +24,8 @@ class CupertinoActivityIndicator(LayoutControl): """ The radius of this indicator. - Note: - Must be strictly greater than `0`. - Raises: - ValueError: If [`radius`][(c).] is not strictly greater than `0`. + ValueError: If it is not strictly greater than `0`. """ color: Optional[ColorValue] = None @@ -39,6 +36,24 @@ class CupertinoActivityIndicator(LayoutControl): animating: bool = True """ Whether this indicator is running its animation. + + Note: + Has no effect if [`progress`][(c).] is not `None`. + """ + + progress: Optional[Number] = None + """ + Determines the percentage of spinner ticks that will be shown. + + Typical usage would display all ticks, however, this allows for more fine-grained + control such as during pull-to-refresh when the drag-down action shows one tick at + a time as the user continues to drag down. + + Note: + If not `None`, then [`animating`][(c).] will be ignored. + + Raises: + ValueError: If it is not between `0.0` and `1.0`, inclusive. """ def before_update(self): @@ -47,3 +62,7 @@ def before_update(self): raise ValueError( f"radius must be strictly greater than 0.0, got {self.radius}" ) + if self.progress is not None and not 0.0 <= self.progress <= 1.0: + raise ValueError( + f"progress must be between 0.0 and 1.0 inclusive, got {self.progress}" + ) diff --git a/sdk/python/packages/flet/src/flet/controls/material/bottom_sheet.py b/sdk/python/packages/flet/src/flet/controls/material/bottom_sheet.py index 1225d0c273..1cf9bc3f38 100644 --- a/sdk/python/packages/flet/src/flet/controls/material/bottom_sheet.py +++ b/sdk/python/packages/flet/src/flet/controls/material/bottom_sheet.py @@ -14,11 +14,13 @@ @control("BottomSheet") class BottomSheet(DialogControl): """ - Displays a modal bottom sheet. + A modal bottom sheet. - A bottom sheet is an alternative to a menu or dialog and prevents the user - from interacting with the rest of the app. + Displays a temporary surface anchored to the bottom of the + screen that presents supplemental content or actions. + Prevents interaction with the underlying app while visible. + Example: ```python sheet = ft.BottomSheet( content=ft.Column( @@ -58,7 +60,7 @@ class BottomSheet(DialogControl): Specifies whether this bottom sheet will be dismissed when user taps on the scrim. """ - enable_drag: bool = False + draggable: bool = False """ Specifies whether this bottom sheet can be dragged up and down and dismissed by swiping downwards. @@ -75,16 +77,28 @@ class BottomSheet(DialogControl): right. """ - scroll_controlled: bool = False + scrollable: bool = False """ - Specifies if this bottom sheet contains scrollable content, such as ListView or - GridView. + Removes the half-height cap so the sheet can grow with its content. + + Set this to `True` whenever the sheet body contains scrollable controls + (e.g., [`ListView`][flet.], [`GridView`][flet.]) or you plan to `expand` the + [`content`][(c).] or give it a custom height, else the bottom sheet might + ignore the custom height and stop around mid-screen. + """ + + fullscreen: bool = False + """ + Expands the sheet to fill the window/page height. + + If set to `True`, [`scrollable`][(c).] is internally set to `True` equally, + so the sheet can grow freely to fill the page. """ maintain_bottom_view_insets_padding: bool = True """ - Adds a padding at the bottom to avoid obstructing the bottom sheet's content with - on-screen keyboard or other system elements. + Adds a padding at the bottom to avoid obstructing this + bottom sheet's [`content`][(c).] with on-screen keyboard or other system elements. """ animation_style: Optional[AnimationStyle] = None diff --git a/sdk/python/packages/flet/src/flet/controls/material/date_picker.py b/sdk/python/packages/flet/src/flet/controls/material/date_picker.py index 5af18bae7c..80dc545655 100644 --- a/sdk/python/packages/flet/src/flet/controls/material/date_picker.py +++ b/sdk/python/packages/flet/src/flet/controls/material/date_picker.py @@ -12,6 +12,7 @@ from flet.controls.dialog_control import DialogControl from flet.controls.duration import DateTimeValue from flet.controls.material.textfield import KeyboardType +from flet.controls.padding import Padding, PaddingValue from flet.controls.types import ( ColorValue, IconData, @@ -26,20 +27,53 @@ class DatePickerMode(Enum): + """Initial display of a calendar date picker.""" + DAY = "day" + """Choosing a month and day.""" + YEAR = "year" + """Choosing a year.""" class DatePickerEntryMode(Enum): + """Mode of date entry method for the date picker dialog.""" + CALENDAR = "calendar" + """ + User picks a date from calendar grid. + + Can switch to [`INPUT`][(c).] by activating a mode button in the dialog. + """ + INPUT = "input" + """ + User can input the date by typing it into a text field. + + Can switch to [`CALENDAR`][(c).] by activating a mode button in the dialog. + """ + CALENDAR_ONLY = "calendarOnly" + """ + User can only pick a date from calendar grid. + + There is no user interface to switch to another mode. + """ + INPUT_ONLY = "inputOnly" + """ + User can only input the date by typing it into a text field. + + There is no user interface to switch to another mode. + """ @dataclass class DatePickerEntryModeChangeEvent(Event["DatePicker"]): - entry_mode: Optional[DatePickerEntryMode] + """Event fired when the [`DatePicker`][flet.] entry mode is changed.""" + + entry_mode: DatePickerEntryMode + """The new date picker entry mode.""" @control("DatePicker") @@ -49,7 +83,7 @@ class DatePicker(DialogControl): It can be opened by calling [`Page.show_dialog()`][flet.Page.show_dialog] method. - Depending on the [`date_picker_entry_mode`][(c).], it will show either a Calendar + Depending on the [`entry_mode`][(c).], it will show either a Calendar or an Input (TextField) for picking a date. """ @@ -69,14 +103,18 @@ class DatePicker(DialogControl): default_factory=lambda: datetime(year=1900, month=1, day=1) ) """ - The earliest allowable date that the user can select. Defaults to `January 1, 1900`. + The earliest allowable date that the user can select. + + Defaults to `January 1, 1900`. """ last_date: DateTimeValue = field( default_factory=lambda: datetime(year=2050, month=1, day=1) ) """ - The latest allowable date that the user can select. Defaults to `January 1, 2050`. + The latest allowable date that the user can select. + + Defaults to `January 1, 2050`. """ current_date: DateTimeValue = field(default_factory=lambda: datetime.now()) @@ -94,7 +132,7 @@ class DatePicker(DialogControl): Initial display of a calendar date picker. """ - date_picker_entry_mode: DatePickerEntryMode = DatePickerEntryMode.CALENDAR + entry_mode: DatePickerEntryMode = DatePickerEntryMode.CALENDAR """ The initial mode of date entry method for the date picker dialog. """ @@ -110,17 +148,21 @@ class DatePicker(DialogControl): cancel_text: Optional[str] = None """ - The text that is displayed on the cancel button. Defaults to `"Cancel"`. + The text that is displayed on the cancel button. + + Defaults to `"Cancel"`. """ confirm_text: Optional[str] = None """ - The text that is displayed on the confirm button. Defaults to `"OK"`. + The text that is displayed on the confirm button. + + Defaults to `"OK"`. """ error_format_text: Optional[str] = None """ - The error message displayed below the TextField if the entered date is not in the + The error message displayed below the text field if the entered date is not in the correct format. Defaults to `"Invalid format"`. @@ -128,8 +170,8 @@ class DatePicker(DialogControl): error_invalid_text: Optional[str] = None """ - The error message displayed below the TextField if the date is earlier than - `first_date` or later than `last_date`. + The error message displayed below the text field if the date is earlier than + [`first_date`][(c).] or later than [`last_date`][(c).]. Defaults to `"Out of range"`. """ @@ -138,49 +180,58 @@ class DatePicker(DialogControl): """ The hint text displayed in the text field. - The default value is the date format string that depends on your locale. For - example, 'mm/dd/yyyy' for en_US. + The default value is the date format string that depends on your locale. + For example, `'mm/dd/yyyy'` for en_US. """ field_label_text: Optional[str] = None """ - The label text displayed in the TextField. + The label text displayed in the `TextField`. + + If `None`, defaults to the words representing the date format string. + For example, `'Month, Day, Year'` for en_US. Defaults to `"Enter Date"`. """ switch_to_calendar_icon: Optional[IconData] = None """ - The name of the icon displayed in the corner of the dialog when - [`date_picker_entry_mode`][(c).] - is [`DatePickerEntryMode.INPUT`][flet.]. + The icon displayed in the corner of this picker's dialog when + [`entry_mode`][(c).] is [`DatePickerEntryMode.INPUT`][flet.]. - Clicking on this icon changes the `date_picker_entry_mode` to + Clicking on this icon changes the [`entry_mode`][(c).] to [`DatePickerEntryMode.CALENDAR`][flet.]. - If `None`, [`Icons.CALENDAR_TODAY`][flet.] is used. + If `None`, defaults to [`Icons.CALENDAR_TODAY`][flet.]. """ switch_to_input_icon: Optional[IconData] = None """ - The name of the icon displayed in the corner of the dialog when - [`date_picker_entry_mode`][(c).] - is [`DatePickerEntryMode.CALENDAR`][flet.]. + The icon displayed in the corner of this picker's dialog when + [`entry_mode`][(c).] is [`DatePickerEntryMode.CALENDAR`][flet.]. - Clicking on icon changes the `DatePickerEntryMode` to + Clicking on icon changes the [`entry_mode`][(c).] to [`DatePickerEntryMode.INPUT`][flet.]. - If `None`, [`Icons.EDIT_OUTLINED`][flet.] is used. + If `None`, defaults to [`Icons.EDIT_OUTLINED`][flet.]. """ barrier_color: Optional[ColorValue] = None """ - The color of the modal barrier that - darkens everything below the date picker. + The color of the modal barrier that darkens everything below this picker's dialog. + + If `None`, the [`DialogTheme.barrier_color`][flet.] is used. + If it is also `None`, then [`Colors.BLACK_54`][flet.] is used. + """ + + inset_padding: PaddingValue = field( + default_factory=lambda: Padding.symmetric(horizontal=16.0, vertical=24.0) + ) + """ + The amount of padding added to [`view_insets`][flet.PageMediaData.] of the + [`Page.media`][flet.] on the outside of this picker's dialog. - If `None`, the [`DialogTheme.barrier_color`][flet.] - is used. - If it is also `None`, then `Colors.BLACK_54` is used. + This defines the minimum space between the screen's edges and the dialog. """ on_change: Optional[ControlEventHandler["DatePicker"]] = None @@ -188,11 +239,11 @@ class DatePicker(DialogControl): Called when user clicks confirm button. [`value`][(c).] is updated with selected date. - The `data` property of the event handler argument contains the selected date. + The [`data`][flet.Event.] property of the event handler argument + contains the selected date. """ on_entry_mode_change: Optional[EventHandler[DatePickerEntryModeChangeEvent]] = None """ - Called when the [`date_picker_entry_mode`][(c).] - is changed. + Called when the [`entry_mode`][(c).] is changed from the user interface. """ diff --git a/sdk/python/packages/flet/src/flet/controls/material/date_range_picker.py b/sdk/python/packages/flet/src/flet/controls/material/date_range_picker.py index bc8c372cf0..6e99d36422 100644 --- a/sdk/python/packages/flet/src/flet/controls/material/date_range_picker.py +++ b/sdk/python/packages/flet/src/flet/controls/material/date_range_picker.py @@ -27,7 +27,7 @@ class DateRangePicker(DialogControl): It can be opened by calling [`Page.show_dialog()`][flet.Page.show_dialog] method. - Depending on the [`date_picker_entry_mode`][(c).], it will show either a Calendar + Depending on the [`entry_mode`][(c).], it will show either a Calendar or an Input (TextField) for picking a date range. """ @@ -84,7 +84,7 @@ class DateRangePicker(DialogControl): The type of keyboard to use for editing the text. """ - date_picker_entry_mode: DatePickerEntryMode = DatePickerEntryMode.CALENDAR + entry_mode: DatePickerEntryMode = DatePickerEntryMode.CALENDAR """ The initial mode of date entry method for the date picker dialog. """ @@ -142,10 +142,10 @@ class DateRangePicker(DialogControl): switch_to_calendar_icon: Optional[IconData] = None """ The name of the icon displayed in the corner of the dialog when - [`date_picker_entry_mode`][(c).] + [`entry_mode`][(c).] is [`DatePickerEntryMode.INPUT`][flet.DatePickerEntryMode.INPUT]. - Clicking on this icon changes the `date_picker_entry_mode` to + Clicking on this icon changes the `entry_mode` to [`DatePickerEntryMode.CALENDAR`][flet.DatePickerEntryMode.CALENDAR]. If `None`, [`Icons.CALENDAR_TODAY`][flet.Icons.CALENDAR_TODAY] is used. @@ -154,10 +154,10 @@ class DateRangePicker(DialogControl): switch_to_input_icon: Optional[IconData] = None """ The name of the icon displayed in the corner of the dialog when - [`date_picker_entry_mode`][(c).] + [`entry_mode`][(c).] is [`DatePickerEntryMode.CALENDAR`][flet.DatePickerEntryMode.CALENDAR]. - Clicking on this icon changes the `date_picker_entry_mode` to + Clicking on this icon changes the `entry_mode` to [`DatePickerEntryMode.INPUT`][flet.DatePickerEntryMode.INPUT]. If `None`, [`Icons.EDIT_OUTLINED`][flet.Icons.EDIT_OUTLINED] is used. diff --git a/sdk/python/packages/flet/src/flet/controls/material/time_picker.py b/sdk/python/packages/flet/src/flet/controls/material/time_picker.py index c990baaec6..a860dff7a8 100644 --- a/sdk/python/packages/flet/src/flet/controls/material/time_picker.py +++ b/sdk/python/packages/flet/src/flet/controls/material/time_picker.py @@ -10,6 +10,7 @@ EventHandler, ) from flet.controls.dialog_control import DialogControl +from flet.controls.material.icons import IconData from flet.controls.types import ( ColorValue, Orientation, @@ -117,10 +118,10 @@ class TimePicker(DialogControl): modal: bool = False """ - TBD + Whether this picker cannot be dismissed by clicking the area outside of it. """ - entry_mode: Optional[TimePickerEntryMode] = None + entry_mode: TimePickerEntryMode = TimePickerEntryMode.DIAL """ The initial mode of time entry method for this picker. @@ -178,7 +179,32 @@ class TimePicker(DialogControl): barrier_color: Optional[ColorValue] = None """ - TBD + The color of the modal barrier that darkens everything below this picker's dialog. + + If `None`, the [`DialogTheme.barrier_color`][flet.] is used. + If it is also `None`, then [`Colors.BLACK_54`][flet.] is used. + """ + + switch_to_timer_icon: Optional[IconData] = None + """ + The icon displayed in the corner of this picker's dialog when + [`entry_mode`][(c).] is [`TimePickerEntryMode.INPUT`][flet.]. + + Clicking on this icon changes the [`entry_mode`][(c).] to + [`TimePickerEntryMode.DIAL`][flet.]. + + If `None`, defaults to [`Icons.ACCESS_TIME`][flet.]. + """ + + switch_to_input_icon: Optional[IconData] = None + """ + The icon displayed in the corner of this picker's dialog when + [`entry_mode`][(c).] is [`TimePickerEntryMode.DIAL`][flet.]. + + Clicking on icon changes the [`entry_mode`][(c).] to + [`TimePickerEntryMode.INPUT`][flet.]. + + If `None`, defaults to [`Icons.KEYBOARD_OUTLINED`][flet.]. """ on_change: Optional[ControlEventHandler["TimePicker"]] = None