From 5cde674938887a1158d2543d2228710bfa1df623 Mon Sep 17 00:00:00 2001 From: InesaFitsner Date: Fri, 10 Nov 2023 09:59:45 -0800 Subject: [PATCH] NavigationDrawer control (#2059) * Create navigation_drawer.py * added end_drawer to view and page * navigation_drawer.dart * NavigationDrawer prototype * NavigationDrawerDestination icon property * icon_content for destination * destinations changed to controls * removed bgColor property for NavigationDrawerDestination as it doesn't work in flutter https://github.com/flutter/flutter/issues/138105 * Update navigation_drawer.dart * selected_icon and selected_icon_content * elevation property * indicator_color property * indicator_shape property * shadow_color property * surface_tint_color property * refactor * added elevation and shadow_color to NavigationBar * indicator_color, indicator_shape, surface_tint_color, shadow_color, elevation for NavigationBar * show_drawer, show_end_drawer, close_drawer, close_end_drawer for page * on_dismiss for NavigationDrawer * updated comments --------- Co-authored-by: Feodor Fitsner --- package/lib/src/controls/navigation_bar.dart | 9 + .../lib/src/controls/navigation_drawer.dart | 128 +++++++ package/lib/src/controls/page.dart | 127 ++++++- .../flet-core/src/flet_core/__init__.py | 1 + .../flet-core/src/flet_core/navigation_bar.py | 77 +++- .../src/flet_core/navigation_drawer.py | 354 ++++++++++++++++++ .../packages/flet-core/src/flet_core/page.py | 63 ++++ .../packages/flet-core/src/flet_core/view.py | 29 ++ 8 files changed, 771 insertions(+), 17 deletions(-) create mode 100644 package/lib/src/controls/navigation_drawer.dart create mode 100644 sdk/python/packages/flet-core/src/flet_core/navigation_drawer.py diff --git a/package/lib/src/controls/navigation_bar.dart b/package/lib/src/controls/navigation_bar.dart index df88a1d2d..48172c86e 100644 --- a/package/lib/src/controls/navigation_bar.dart +++ b/package/lib/src/controls/navigation_bar.dart @@ -11,6 +11,7 @@ import '../protocol/update_control_props_payload.dart'; import '../utils/colors.dart'; import '../utils/icons.dart'; import 'create_control.dart'; +import '../utils/borders.dart'; class NavigationBarControl extends StatefulWidget { final Control? parent; @@ -79,6 +80,14 @@ class _NavigationBarControlState extends State { labelBehavior: labelBehavior, height: widget.control.attrDouble("height"), elevation: widget.control.attrDouble("elevation"), + shadowColor: HexColor.fromString(Theme.of(context), + widget.control.attrString("shadowColor", "")!), + surfaceTintColor: HexColor.fromString(Theme.of(context), + widget.control.attrString("surfaceTintColor", "")!), + indicatorColor: HexColor.fromString(Theme.of(context), + widget.control.attrString("indicatorColor", "")!), + indicatorShape: + parseOutlinedBorder(widget.control, "indicatorShape"), backgroundColor: HexColor.fromString( Theme.of(context), widget.control.attrString("bgColor", "")!), selectedIndex: _selectedIndex, diff --git a/package/lib/src/controls/navigation_drawer.dart b/package/lib/src/controls/navigation_drawer.dart new file mode 100644 index 000000000..ecae4e811 --- /dev/null +++ b/package/lib/src/controls/navigation_drawer.dart @@ -0,0 +1,128 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_redux/flutter_redux.dart'; + +import '../actions.dart'; +import '../flet_app_services.dart'; +import '../models/app_state.dart'; +import '../models/control.dart'; +import '../models/controls_view_model.dart'; +import '../protocol/update_control_props_payload.dart'; +import '../utils/colors.dart'; +import '../utils/icons.dart'; +import 'create_control.dart'; +import '../utils/borders.dart'; +import '../utils/edge_insets.dart'; + +class NavigationDrawerControl extends StatefulWidget { + final Control? parent; + final Control control; + final List children; + final bool parentDisabled; + final dynamic dispatch; + + const NavigationDrawerControl( + {Key? key, + this.parent, + required this.control, + required this.children, + required this.parentDisabled, + required this.dispatch}) + : super(key: key); + + @override + State createState() => + _NavigationDrawerControlState(); +} + +class _NavigationDrawerControlState extends State { + int _selectedIndex = 0; + + void _destinationChanged(int index) { + _selectedIndex = index; + debugPrint("Selected index: $_selectedIndex"); + List> props = [ + {"i": widget.control.id, "selectedindex": _selectedIndex.toString()} + ]; + widget.dispatch( + UpdateControlPropsAction(UpdateControlPropsPayload(props: props))); + final server = FletAppServices.of(context).server; + server.updateControlProps(props: props); + server.sendPageEvent( + eventTarget: widget.control.id, + eventName: "change", + eventData: _selectedIndex.toString()); + } + + @override + Widget build(BuildContext context) { + debugPrint("NavigationDrawerControl build: ${widget.control.id}"); + + bool disabled = widget.control.isDisabled || widget.parentDisabled; + var selectedIndex = widget.control.attrInt("selectedIndex", 0)!; + + if (_selectedIndex != selectedIndex) { + _selectedIndex = selectedIndex; + } + + var navDrawer = StoreConnector( + distinct: true, + converter: (store) => ControlsViewModel.fromStore( + store, + widget.children + .where((c) => c.isVisible && c.name == null) + .map((c) => c.id)), + builder: (content, viewModel) { + List children = viewModel.controlViews.map((destView) { + if (destView.control.type == "navigationdrawerdestination") { + var icon = + getMaterialIcon(destView.control.attrString("icon", "")!); + var iconContentCtrls = + destView.children.where((c) => c.name == "icon_content"); + var selectedIcon = getMaterialIcon( + destView.control.attrString("selectedIcon", "")!); + var selectedIconContentCtrls = destView.children + .where((c) => c.name == "selected_icon_content"); + return NavigationDrawerDestination( + // backgroundColor: HexColor.fromString(Theme.of(context), + // destView.control.attrString("bgColor", "")!), + // flutter issue https://github.com/flutter/flutter/issues/138105 + icon: iconContentCtrls.isNotEmpty + ? createControl( + destView.control, iconContentCtrls.first.id, disabled) + : Icon(icon), + label: Text(destView.control.attrString("label", "")!), + selectedIcon: selectedIconContentCtrls.isNotEmpty + ? createControl(destView.control, + selectedIconContentCtrls.first.id, disabled) + : selectedIcon != null + ? Icon(selectedIcon) + : null, + ); + } else { + return createControl( + widget.control, destView.control.id, disabled); + } + }).toList(); + return NavigationDrawer( + elevation: widget.control.attrDouble("elevation"), + indicatorColor: HexColor.fromString(Theme.of(context), + widget.control.attrString("indicatorColor", "")!), + indicatorShape: + parseOutlinedBorder(widget.control, "indicatorShape"), + backgroundColor: HexColor.fromString( + Theme.of(context), widget.control.attrString("bgColor", "")!), + selectedIndex: _selectedIndex, + shadowColor: HexColor.fromString(Theme.of(context), + widget.control.attrString("shadowColor", "")!), + surfaceTintColor: HexColor.fromString(Theme.of(context), + widget.control.attrString("surfaceTintColor", "")!), + tilePadding: parseEdgeInsets(widget.control, "tilePadding") ?? + const EdgeInsets.symmetric(horizontal: 12.0), + onDestinationSelected: _destinationChanged, + children: children, + ); + }); + + return navDrawer; + } +} diff --git a/package/lib/src/controls/page.dart b/package/lib/src/controls/page.dart index bbdc23234..7d5beb5b8 100644 --- a/package/lib/src/controls/page.dart +++ b/package/lib/src/controls/page.dart @@ -17,6 +17,7 @@ import '../models/page_args_model.dart'; import '../models/page_media_view_model.dart'; import '../models/routes_view_model.dart'; import '../protocol/keyboard_event.dart'; +import '../protocol/update_control_props_payload.dart'; import '../routing/route_parser.dart'; import '../routing/route_state.dart'; import '../routing/router_delegate.dart'; @@ -34,6 +35,7 @@ import '../widgets/page_media.dart'; import '../widgets/window_media.dart'; import 'app_bar.dart'; import 'create_control.dart'; +import 'navigation_drawer.dart'; import 'scroll_notification_control.dart'; import 'scrollable_control.dart'; @@ -90,6 +92,8 @@ class _PageControlState extends State { late final RouteParser _routeParser; String? _prevViewRoutes; bool _keyboardHandlerSubscribed = false; + bool? _drawerOpened; + bool? _endDrawerOpened; @override void initState() { @@ -598,6 +602,8 @@ class _PageControlState extends State { Control? appBar; Control? fab; Control? navBar; + Control? drawer; + Control? endDrawer; List controls = []; bool firstControl = true; @@ -611,6 +617,13 @@ class _PageControlState extends State { } else if (ctrl.type == "navigationbar") { navBar = ctrl; continue; + } else if (ctrl.type == "navigationdrawer" && + ctrl.name == "start") { + drawer = ctrl; + continue; + } else if (ctrl.type == "navigationdrawer" && ctrl.name == "end") { + endDrawer = ctrl; + continue; } // spacer between displayed controls else if (spacing > 0 && @@ -626,10 +639,8 @@ class _PageControlState extends State { controls.add(createControl(control, ctrl.id, control.isDisabled)); } - List childIds = []; - if (appBar != null) { - childIds.add(appBar.id); - } + List childIds = + [appBar?.id, drawer?.id, endDrawer?.id].whereNotNull().toList(); final textDirection = parent.attrBool("rtl", false)! ? TextDirection.rtl @@ -651,10 +662,12 @@ class _PageControlState extends State { builder: (context, childrenViews) { debugPrint("Route view StoreConnector build: $viewId"); - var appBarView = - appBar != null && childrenViews.controlViews.isNotEmpty - ? childrenViews.controlViews.last - : null; + var appBarView = childrenViews.controlViews.firstWhereOrNull( + (v) => v.control.id == (appBar?.id ?? "")); + var drawerView = childrenViews.controlViews.firstWhereOrNull( + (v) => v.control.id == (drawer?.id ?? "")); + var endDrawerView = childrenViews.controlViews.firstWhereOrNull( + (v) => v.control.id == (endDrawer?.id ?? "")); var column = Column( mainAxisAlignment: mainAlignment, @@ -673,7 +686,55 @@ class _PageControlState extends State { ScrollNotificationControl(control: control, child: child); } + GlobalKey? scaffoldKey = + FletAppServices.of(context).globalKeys["_scaffold"] + as GlobalKey?; + if (scaffoldKey == null) { + scaffoldKey = GlobalKey(); + FletAppServices.of(context).globalKeys["_scaffold"] = + scaffoldKey; + } + + WidgetsBinding.instance.addPostFrameCallback((_) { + if (drawerView != null) { + if (drawerView.control.attrBool("open", false)! && + _drawerOpened != true) { + if (scaffoldKey?.currentState?.isEndDrawerOpen == true) { + scaffoldKey?.currentState?.closeEndDrawer(); + } + Future.delayed(const Duration(milliseconds: 1)) + .then((value) { + scaffoldKey?.currentState?.openDrawer(); + _drawerOpened = true; + }); + } else if (!drawerView.control.attrBool("open", false)! && + _drawerOpened == true) { + scaffoldKey?.currentState?.closeDrawer(); + _drawerOpened = false; + } + } + if (endDrawerView != null) { + if (endDrawerView.control.attrBool("open", false)! && + _endDrawerOpened != true) { + if (scaffoldKey?.currentState?.isDrawerOpen == true) { + scaffoldKey?.currentState?.closeDrawer(); + } + Future.delayed(const Duration(milliseconds: 1)) + .then((value) { + scaffoldKey?.currentState?.openEndDrawer(); + _endDrawerOpened = true; + }); + } else if (!endDrawerView.control + .attrBool("open", false)! && + _endDrawerOpened == true) { + scaffoldKey?.currentState?.closeEndDrawer(); + _endDrawerOpened = false; + } + } + }); + var scaffold = Scaffold( + key: scaffoldKey, backgroundColor: HexColor.fromString( Theme.of(context), control.attrString("bgcolor", "")!), appBar: appBarView != null @@ -685,6 +746,56 @@ class _PageControlState extends State { height: appBarView.control .attrDouble("toolbarHeight", kToolbarHeight)!) : null, + drawer: drawerView != null + ? NavigationDrawerControl( + control: drawerView.control, + children: drawerView.children, + parentDisabled: control.isDisabled, + dispatch: widget.dispatch, + ) + : null, + onDrawerChanged: (opened) { + if (drawerView != null && !opened) { + _drawerOpened = false; + List> props = [ + {"i": drawerView.control.id, "open": "false"} + ]; + widget.dispatch(UpdateControlPropsAction( + UpdateControlPropsPayload(props: props))); + FletAppServices.of(context) + .server + .updateControlProps(props: props); + FletAppServices.of(context).server.sendPageEvent( + eventTarget: drawerView.control.id, + eventName: "dismiss", + eventData: ""); + } + }, + endDrawer: endDrawerView != null + ? NavigationDrawerControl( + control: endDrawerView.control, + children: endDrawerView.children, + parentDisabled: control.isDisabled, + dispatch: widget.dispatch, + ) + : null, + onEndDrawerChanged: (opened) { + if (endDrawerView != null && !opened) { + _endDrawerOpened = false; + List> props = [ + {"i": endDrawerView.control.id, "open": "false"} + ]; + widget.dispatch(UpdateControlPropsAction( + UpdateControlPropsPayload(props: props))); + FletAppServices.of(context) + .server + .updateControlProps(props: props); + FletAppServices.of(context).server.sendPageEvent( + eventTarget: endDrawerView.control.id, + eventName: "dismiss", + eventData: ""); + } + }, body: Stack(children: [ SizedBox.expand( child: Container( diff --git a/sdk/python/packages/flet-core/src/flet_core/__init__.py b/sdk/python/packages/flet-core/src/flet_core/__init__.py index 288871bd9..3e9b51769 100644 --- a/sdk/python/packages/flet-core/src/flet_core/__init__.py +++ b/sdk/python/packages/flet-core/src/flet_core/__init__.py @@ -207,3 +207,4 @@ from flet_core.webview import WebView from flet_core.range_slider import RangeSlider from flet_core.badge import Badge +from flet_core.navigation_drawer import NavigationDrawer, NavigationDrawerDestination diff --git a/sdk/python/packages/flet-core/src/flet_core/navigation_bar.py b/sdk/python/packages/flet-core/src/flet_core/navigation_bar.py index 51a5f1119..6eafa86d7 100644 --- a/sdk/python/packages/flet-core/src/flet_core/navigation_bar.py +++ b/sdk/python/packages/flet-core/src/flet_core/navigation_bar.py @@ -12,6 +12,8 @@ ScaleValue, ) +from flet_core.buttons import OutlinedBorder + try: from typing import Literal except ImportError: @@ -182,6 +184,10 @@ def __init__( bgcolor: Optional[str] = None, label_behavior: Optional[NavigationBarLabelBehavior] = None, elevation: OptionalNumber = None, + shadow_color: Optional[str] = None, + indicator_color: Optional[str] = None, + indicator_shape: Optional[OutlinedBorder] = None, + surface_tint_color: Optional[str] = None, on_change=None, ): ConstrainedControl.__init__( @@ -217,11 +223,19 @@ def __init__( self.label_behavior = label_behavior self.bgcolor = bgcolor self.elevation = elevation + self.shadow_color = shadow_color + self.indicator_color = indicator_color + self.indicator_shape = indicator_shape + self.surface_tint_color = surface_tint_color self.on_change = on_change def _get_control_name(self): return "navigationbar" + def _before_build_command(self): + super()._before_build_command() + self._set_attr_json("indicatorShape", self.__indicator_shape) + def _get_children(self): children = [] children.extend(self.__destinations) @@ -236,15 +250,6 @@ def destinations(self) -> Optional[List[NavigationDestination]]: def destinations(self, value: Optional[List[NavigationDestination]]): self.__destinations = value if value is not None else [] - # on_change - @property - def on_change(self): - return self._get_event_handler("change") - - @on_change.setter - def on_change(self, handler): - self._add_event_handler("change", handler) - # selected_index @property def selected_index(self) -> Optional[int]: @@ -278,3 +283,57 @@ def bgcolor(self): @bgcolor.setter def bgcolor(self, value): self._set_attr("bgcolor", value) + + # elevation + @property + def elevation(self) -> OptionalNumber: + return self._get_attr("elevation") + + @elevation.setter + def elevation(self, value: OptionalNumber): + self._set_attr("elevation", value) + + # shadow_color + @property + def shadow_color(self): + return self._get_attr("shadowColor") + + @shadow_color.setter + def shadow_color(self, value): + self._set_attr("shadowColor", value) + + # indicator_color + @property + def indicator_color(self): + return self._get_attr("indicatorColor") + + @indicator_color.setter + def indicator_color(self, value): + self._set_attr("indicatorColor", value) + + # indicator_shape + @property + def indicator_shape(self) -> Optional[OutlinedBorder]: + return self.__indicator_shape + + @indicator_shape.setter + def indicator_shape(self, value: Optional[OutlinedBorder]): + self.__indicator_shape = value + + # surface_tint_color + @property + def surface_tint_color(self): + return self._get_attr("surfaceTintColor") + + @surface_tint_color.setter + def surface_tint_color(self, value): + self._set_attr("surfaceTintColor", value) + + # on_change + @property + def on_change(self): + return self._get_event_handler("change") + + @on_change.setter + def on_change(self, handler): + self._add_event_handler("change", handler) diff --git a/sdk/python/packages/flet-core/src/flet_core/navigation_drawer.py b/sdk/python/packages/flet-core/src/flet_core/navigation_drawer.py new file mode 100644 index 000000000..3e1c1563b --- /dev/null +++ b/sdk/python/packages/flet-core/src/flet_core/navigation_drawer.py @@ -0,0 +1,354 @@ +from enum import Enum +from typing import Any, List, Optional, Union + +from flet_core.constrained_control import ConstrainedControl +from flet_core.control import Control, OptionalNumber +from flet_core.ref import Ref +from flet_core.types import ( + PaddingValue, +) + +from flet_core.buttons import OutlinedBorder + + +class NavigationDrawerDestination(Control): + """ + Displays an icon with a label, for use in NavigationDrawer destinations. + + """ + + def __init__( + self, + ref: Optional[Ref] = None, + # bgcolor: Optional[str] = None, + icon: Optional[str] = None, + icon_content: Optional[Control] = None, + label: Optional[str] = None, + selected_icon: Optional[str] = None, + selected_icon_content: Optional[Control] = None, + ): + Control.__init__(self, ref=ref) + self.label = label + # self.bgcolor = bgcolor + self.icon = icon + self.__icon_content: Optional[Control] = None + self.icon_content = icon_content + self.selected_icon = selected_icon + self.__selected_icon_content: Optional[Control] = None + self.selected_icon_content = selected_icon_content + + def _get_control_name(self): + return "navigationdrawerdestination" + + def _get_children(self): + children = [] + if self.__icon_content: + self.__icon_content._set_attr_internal("n", "icon_content") + children.append(self.__icon_content) + if self.__selected_icon_content: + self.__selected_icon_content._set_attr_internal( + "n", "selected_icon_content" + ) + children.append(self.__selected_icon_content) + return children + + # # bgcolor + # @property + # def bgcolor(self): + # return self._get_attr("bgColor") + + # @bgcolor.setter + # def bgcolor(self, value): + # self._set_attr("bgColor", value) + + # icon + @property + def icon(self): + return self._get_attr("icon") + + @icon.setter + def icon(self, value): + self._set_attr("icon", value) + + # icon_content + @property + def icon_content(self) -> Optional[Control]: + return self.__icon_content + + @icon_content.setter + def icon_content(self, value: Optional[Control]): + self.__icon_content = value + + # selected_icon + @property + def selected_icon(self): + return self._get_attr("selectedIcon") + + @selected_icon.setter + def selected_icon(self, value): + self._set_attr("selectedIcon", value) + + # selected_icon_content + @property + def selected_icon_content(self) -> Optional[Control]: + return self.__selected_icon_content + + @selected_icon_content.setter + def selected_icon_content(self, value: Optional[Control]): + self.__selected_icon_content = value + + # label + @property + def label(self): + return self._get_attr("label") + + @label.setter + def label(self, value): + self._set_attr("label", value) + + +class NavigationDrawer(Control): + """ + Material Design Navigation Drawer component. + + Navigation Drawer is a panel slides in horizontally from the left or right edge of a page to show primary destinations in an app. + + Example: + + ``` + import flet as ft + + + def main(page: ft.Page): + def item_selected_left(e): + print(e.control.selected_index) + + page.drawer = ft.NavigationDrawer( + elevation=40, + indicator_color=ft.colors.GREEN_200, + indicator_shape=ft.StadiumBorder(), + shadow_color=ft.colors.GREEN_900, + surface_tint_color=ft.colors.GREEN, + selected_index=-1, + on_change=item_selected_left, + controls=[ + ft.Container(height=12), + ft.NavigationDrawerDestination( + label="Item 1", + icon=ft.icons.ABC, + selected_icon_content=ft.Icon(ft.icons.ACCESS_ALARM), + ), + ft.Divider(thickness=2), + ft.NavigationDrawerDestination( + icon_content=ft.Icon(ft.icons.MAIL), + label="Item 2", + selected_icon=ft.icons.PHISHING, + ), + ft.NavigationDrawerDestination( + icon_content=ft.Icon(ft.icons.PHONE), + label="Item 3", + selected_icon=ft.icons.PHISHING, + ), + ], + ) + + end_drawer = ft.NavigationDrawer( + controls=[ + ft.NavigationDrawerDestination( + icon=ft.icons.ADD_TO_HOME_SCREEN_SHARP, label="Item 1" + ), + ft.NavigationDrawerDestination(icon=ft.icons.ADD_COMMENT, label="Item 2"), + ], + ) + + def show_drawer(e): + page.drawer.open = True + page.drawer.update() + + def show_end_drawer(e): + page.show_end_drawer(end_drawer) + + page.add( + ft.Row( + alignment=ft.MainAxisAlignment.SPACE_BETWEEN, + controls=[ + ft.ElevatedButton("Show drawer", on_click=show_drawer), + ft.ElevatedButton("Show end drawer", on_click=show_end_drawer), + ], + ) + ) + + + ft.app(main) + + ``` + + ----- + + Online docs: https://flet.dev/docs/controls/navigationdrawer + """ + + def __init__( + self, + ref: Optional[Ref] = None, + disabled: Optional[bool] = None, + visible: Optional[bool] = None, + data: Any = None, + # + # NavigationDrawer-specific + # + open: bool = False, + controls: Optional[List[Control]] = None, + selected_index: Optional[int] = None, + bgcolor: Optional[str] = None, + elevation: OptionalNumber = None, + indicator_color: Optional[str] = None, + indicator_shape: Optional[OutlinedBorder] = None, + shadow_color: Optional[str] = None, + surface_tint_color: Optional[str] = None, + tile_padding: PaddingValue = None, + on_change=None, + on_dismiss=None, + ): + Control.__init__( + self, + ref=ref, + visible=visible, + disabled=disabled, + data=data, + ) + + self.open = open + self.controls = controls + self.selected_index = selected_index + self.bgcolor = bgcolor + self.elevation = elevation + self.indicator_color = indicator_color + self.indicator_shape = indicator_shape + self.shadow_color = shadow_color + self.surface_tint_color = surface_tint_color + self.tile_padding = tile_padding + + self.on_change = on_change + self.on_dismiss = on_dismiss + + def _get_control_name(self): + return "navigationdrawer" + + def _before_build_command(self): + super()._before_build_command() + self._set_attr_json("indicatorShape", self.__indicator_shape) + self._set_attr_json("tilePadding", self.__tile_padding) + + def _get_children(self): + children = [] + children.extend(self.__controls) + return children + + # open + @property + def open(self) -> Optional[bool]: + return self._get_attr("open", data_type="bool", def_value=False) + + @open.setter + def open(self, value: Optional[bool]): + self._set_attr("open", value) + + # controls + @property + def controls(self) -> Optional[List[Control]]: + return self.__controls + + @controls.setter + def controls(self, value: Optional[List[Control]]): + self.__controls = value if value is not None else [] + + # selected_index + @property + def selected_index(self) -> Optional[int]: + return self._get_attr("selectedIndex", data_type="int", def_value=0) + + @selected_index.setter + def selected_index(self, value: Optional[int]): + self._set_attr("selectedIndex", value) + + # bgcolor + @property + def bgcolor(self): + return self._get_attr("bgcolor") + + @bgcolor.setter + def bgcolor(self, value): + self._set_attr("bgcolor", value) + + # elevation + @property + def elevation(self) -> OptionalNumber: + return self._get_attr("elevation") + + @elevation.setter + def elevation(self, value: OptionalNumber): + self._set_attr("elevation", value) + + # indicator_color + @property + def indicator_color(self): + return self._get_attr("indicatorColor") + + @indicator_color.setter + def indicator_color(self, value): + self._set_attr("indicatorColor", value) + + # indicator_shape + @property + def indicator_shape(self) -> Optional[OutlinedBorder]: + return self.__indicator_shape + + @indicator_shape.setter + def indicator_shape(self, value: Optional[OutlinedBorder]): + self.__indicator_shape = value + + # shadow_color + @property + def shadow_color(self): + return self._get_attr("shadowColor") + + @shadow_color.setter + def shadow_color(self, value): + self._set_attr("shadowColor", value) + + # surface_tint_color + @property + def surface_tint_color(self): + return self._get_attr("surfaceTintColor") + + @surface_tint_color.setter + def surface_tint_color(self, value): + self._set_attr("surfaceTintColor", value) + + # tile_padding + @property + def tile_padding(self) -> PaddingValue: + return self.__tile_padding + + @tile_padding.setter + def tile_padding(self, value: PaddingValue): + self.__tile_padding = value + + # on_change + @property + def on_change(self): + return self._get_event_handler("change") + + @on_change.setter + def on_change(self, handler): + self._add_event_handler("change", handler) + + # on_dismiss + @property + def on_dismiss(self): + return self._get_event_handler("dismiss") + + @on_dismiss.setter + def on_dismiss(self, handler): + self._add_event_handler("dismiss", handler) diff --git a/sdk/python/packages/flet-core/src/flet_core/page.py b/sdk/python/packages/flet-core/src/flet_core/page.py index 551db09cb..ff46f3628 100644 --- a/sdk/python/packages/flet-core/src/flet_core/page.py +++ b/sdk/python/packages/flet-core/src/flet_core/page.py @@ -25,6 +25,7 @@ from flet_core.floating_action_button import FloatingActionButton from flet_core.locks import AsyncNopeLock, NopeLock from flet_core.navigation_bar import NavigationBar +from flet_core.navigation_drawer import NavigationDrawer from flet_core.protocol import Command from flet_core.querystring import QueryString from flet_core.session_storage import SessionStorage @@ -1052,6 +1053,50 @@ async def close_bottom_sheet_async(self): self.__offstage.bottom_sheet.open = False await self.__offstage.update_async() + # Drawer + # + def show_drawer(self, drawer: NavigationDrawer): + self.drawer = drawer + self.drawer.open = True + self.update() + + async def show_drawer_async(self, drawer: NavigationDrawer): + self.drawer = drawer + self.drawer.open = True + await self.update_async() + + def close_drawer(self): + if self.drawer is not None: + self.drawer.open = False + self.update() + + async def close_drawer_async(self): + if self.drawer is not None: + self.drawer.open = False + await self.drawer.update_async() + + # End_drawer + # + def show_end_drawer(self, end_drawer: NavigationDrawer): + self.end_drawer = end_drawer + self.end_drawer.open = True + self.update() + + async def show_end_drawer_async(self, end_drawer: NavigationDrawer): + self.end_drawer = end_drawer + self.end_drawer.open = True + await self.update_async() + + def close_end_drawer(self): + if self.end_drawer is not None: + self.end_drawer.open = False + self.update() + + async def close_end_drawer_async(self): + if self.end_drawer is not None: + self.end_drawer.open = False + await self.end_drawer.update_async() + def window_destroy(self): self._set_attr("windowDestroy", "true") self.update() @@ -1243,6 +1288,24 @@ def navigation_bar(self) -> Optional[NavigationBar]: def navigation_bar(self, value: Optional[NavigationBar]): self.__default_view.navigation_bar = value + # drawer + @property + def drawer(self) -> Optional[NavigationDrawer]: + return self.__default_view.drawer + + @drawer.setter + def drawer(self, value: Optional[NavigationDrawer]): + self.__default_view.drawer = value + + # end_drawer + @property + def end_drawer(self) -> Optional[NavigationDrawer]: + return self.__default_view.end_drawer + + @end_drawer.setter + def end_drawer(self, value: Optional[NavigationDrawer]): + self.__default_view.end_drawer = value + # floating_action_button @property def floating_action_button(self) -> Optional[FloatingActionButton]: diff --git a/sdk/python/packages/flet-core/src/flet_core/view.py b/sdk/python/packages/flet-core/src/flet_core/view.py index 62bc6c3be..91c069763 100644 --- a/sdk/python/packages/flet-core/src/flet_core/view.py +++ b/sdk/python/packages/flet-core/src/flet_core/view.py @@ -5,6 +5,7 @@ from flet_core.control import OptionalNumber from flet_core.floating_action_button import FloatingActionButton from flet_core.navigation_bar import NavigationBar +from flet_core.navigation_drawer import NavigationDrawer from flet_core.scrollable_control import ScrollableControl from flet_core.types import ( CrossAxisAlignment, @@ -35,6 +36,8 @@ def __init__( appbar: Optional[AppBar] = None, floating_action_button: Optional[FloatingActionButton] = None, navigation_bar: Optional[NavigationBar] = None, + drawer: Optional[NavigationDrawer] = None, + end_drawer: Optional[NavigationDrawer] = None, vertical_alignment: MainAxisAlignment = MainAxisAlignment.NONE, horizontal_alignment: CrossAxisAlignment = CrossAxisAlignment.NONE, spacing: OptionalNumber = None, @@ -63,6 +66,8 @@ def __init__( self.route = route self.appbar = appbar self.navigation_bar = navigation_bar + self.drawer = drawer + self.end_drawer = end_drawer self.floating_action_button = floating_action_button self.vertical_alignment = vertical_alignment self.horizontal_alignment = horizontal_alignment @@ -88,6 +93,12 @@ def _get_children(self): children.append(self.__fab) if self.__navigation_bar: children.append(self.__navigation_bar) + if self.__drawer: + self.__drawer._set_attr_internal("n", "start") + children.append(self.__drawer) + if self.__end_drawer: + self.__end_drawer._set_attr_internal("n", "end") + children.append(self.__end_drawer) children.extend(self.__controls) return children @@ -136,6 +147,24 @@ def navigation_bar(self) -> Optional[NavigationBar]: def navigation_bar(self, value: Optional[NavigationBar]): self.__navigation_bar = value + # drawer + @property + def drawer(self) -> Optional[NavigationDrawer]: + return self.__drawer + + @drawer.setter + def drawer(self, value: Optional[NavigationDrawer]): + self.__drawer = value + + # end_drawer + @property + def end_drawer(self) -> Optional[NavigationDrawer]: + return self.__end_drawer + + @end_drawer.setter + def end_drawer(self, value: Optional[NavigationDrawer]): + self.__end_drawer = value + # horizontal_alignment @property def horizontal_alignment(self) -> CrossAxisAlignment: