diff --git a/client/lib/controls/create_control.dart b/client/lib/controls/create_control.dart index 16adf9a3d..f9556aa2f 100644 --- a/client/lib/controls/create_control.dart +++ b/client/lib/controls/create_control.dart @@ -12,6 +12,7 @@ import 'circle_avatar.dart'; import 'clipboard.dart'; import 'column.dart'; import 'container.dart'; +import 'divider.dart'; import 'dropdown.dart'; import 'elevated_button.dart'; import 'floating_action_button.dart'; @@ -35,6 +36,7 @@ import 'tabs.dart'; import 'text.dart'; import 'text_button.dart'; import 'textfield.dart'; +import 'vertical_divider.dart'; // abstract class ControlWidget extends Widget { // const ControlWidget( @@ -75,6 +77,10 @@ Widget createControl(Control? parent, String id, bool parentDisabled) { return ClipboardControl(control: controlView.control); case ControlType.image: return ImageControl(parent: parent, control: controlView.control); + case ControlType.divider: + return DividerControl(control: controlView.control); + case ControlType.verticalDivider: + return VerticalDividerControl(control: controlView.control); case ControlType.circleAvatar: return CircleAvatarControl( parent: parent, diff --git a/client/lib/controls/divider.dart b/client/lib/controls/divider.dart new file mode 100644 index 000000000..30a4767b9 --- /dev/null +++ b/client/lib/controls/divider.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +import '../models/control.dart'; +import '../utils/colors.dart'; +import 'create_control.dart'; + +class DividerControl extends StatelessWidget { + final Control? parent; + final Control control; + + const DividerControl({Key? key, this.parent, required this.control}) + : super(key: key); + + @override + Widget build(BuildContext context) { + debugPrint("Divider build: ${control.id}"); + + var height = control.attrDouble("height"); + var thickness = control.attrDouble("thickness"); + var color = HexColor.fromString(context, control.attrString("color", "")!); + + return baseControl( + Divider( + height: height, + thickness: thickness, + color: color, + ), + parent, + control); + } +} diff --git a/client/lib/controls/elevated_button.dart b/client/lib/controls/elevated_button.dart index 2f62b5dbd..0a64f539f 100644 --- a/client/lib/controls/elevated_button.dart +++ b/client/lib/controls/elevated_button.dart @@ -28,10 +28,13 @@ class ElevatedButtonControl extends StatelessWidget { IconData? icon = getMaterialIcon(control.attrString("icon", "")!); Color? iconColor = HexColor.fromString(context, control.attrString("iconColor", "")!); + Color? color = + HexColor.fromString(context, control.attrString("color", "")!); + Color? bgcolor = + HexColor.fromString(context, control.attrString("bgcolor", "")!); + var elevation = control.attrDouble("elevation"); var contentCtrls = children.where((c) => c.name == "content"); bool autofocus = control.attrBool("autofocus", false)!; - bool filled = control.attrBool("filled", false)!; - bool filledTonal = control.attrBool("filledTonal", false)!; bool disabled = control.isDisabled || parentDisabled; Function()? onPressed = disabled @@ -47,20 +50,13 @@ class ElevatedButtonControl extends StatelessWidget { ElevatedButton? button; ButtonStyle? style; - if (filled) { + if (color != null || bgcolor != null || elevation != null) { style = ElevatedButton.styleFrom( // Foreground color - onPrimary: Theme.of(context).colorScheme.onPrimary, + onPrimary: color, // Background color - primary: Theme.of(context).colorScheme.primary, - ).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)); - } else if (filledTonal) { - style = ElevatedButton.styleFrom( - // Foreground color - onPrimary: Theme.of(context).colorScheme.onSecondaryContainer, - // Background color - primary: Theme.of(context).colorScheme.secondaryContainer, - ).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)); + primary: bgcolor, + ).copyWith(elevation: ButtonStyleButton.allOrNull(elevation)); } if (icon != null) { diff --git a/client/lib/controls/vertical_divider.dart b/client/lib/controls/vertical_divider.dart new file mode 100644 index 000000000..95b8533ed --- /dev/null +++ b/client/lib/controls/vertical_divider.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +import '../models/control.dart'; +import '../utils/colors.dart'; +import 'create_control.dart'; + +class VerticalDividerControl extends StatelessWidget { + final Control? parent; + final Control control; + + const VerticalDividerControl({Key? key, this.parent, required this.control}) + : super(key: key); + + @override + Widget build(BuildContext context) { + debugPrint("VerticalDivider build: ${control.id}"); + + var width = control.attrDouble("width"); + var thickness = control.attrDouble("thickness"); + var color = HexColor.fromString(context, control.attrString("color", "")!); + + return baseControl( + VerticalDivider( + width: width, + thickness: thickness, + color: color, + ), + parent, + control); + } +} diff --git a/client/lib/models/control_type.dart b/client/lib/models/control_type.dart index 0fa0afe46..1e7fee5f2 100644 --- a/client/lib/models/control_type.dart +++ b/client/lib/models/control_type.dart @@ -6,6 +6,7 @@ enum ControlType { clipboard, column, container, + divider, dropdown, dropdownOption, elevatedButton, @@ -32,5 +33,6 @@ enum ControlType { tab, text, textButton, - textField + textField, + verticalDivider } diff --git a/docs/roadmap.md b/docs/roadmap.md index db845ad27..10ef169c9 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -13,8 +13,13 @@ * [x] Stack * [x] ListView * [x] GridView + * [ ] Divider + * [ ] VerticalDivider * App structure and navigation * [x] Tabs + * [ ] AppBar + * [ ] NavigationRail + * [ ] NavigationBar * Basic controls * [x] Text * [x] Icon @@ -24,10 +29,13 @@ * [x] ProgressRing * Buttons * [x] ElevatedButton + * [x] FilledButton + * [x] FilledTonalButton * [x] OutlinedButton * [x] TextButton * [x] IconButton * [x] FloatingActionButton + * [ ] PopupMenuButton * Input and selections * [x] TextField * [x] Dropdown @@ -59,12 +67,14 @@ ## Sprint 2 +* Authentication * Controls + * Navigation + * NavigationDrawer * Layout * Row (responsive) * Column (responsive) * Behavior - * Complex embeddable values for `padding`, `marging`, etc, e.g. `.padding = { 'left': 10, 'right': 20 }` * Visual Density ([more](https://api.flutter.dev/flutter/material/VisualDensity-class.html)) * Early detection of layout issues (like enabling scrolling in unbounded controls) with [Layout Builder](https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html). * Scroll speed on Windows Desktop [The issue](https://github.com/flutter/flutter/issues/67985) @@ -75,6 +85,17 @@ * [ ] Windows ("host" mode with hot reload) * [ ] macOS ("host" mode with hot reload) +## Year 2022 + +* Grids +* Charts +* Navigation controls and Routing +* Responsive layout +* Adaptive controls +* Animations +* PubSub +* DB + ## Controls diff --git a/sdk/python/flet/__init__.py b/sdk/python/flet/__init__.py index 75189e28e..d875aaa24 100644 --- a/sdk/python/flet/__init__.py +++ b/sdk/python/flet/__init__.py @@ -5,8 +5,11 @@ from flet.column import Column from flet.container import Container from flet.control import Control +from flet.divider import Divider from flet.dropdown import Dropdown from flet.elevated_button import ElevatedButton +from flet.filled_button import FilledButton +from flet.filled_tonal_button import FilledTonalButton from flet.flet import * from flet.floating_action_button import FloatingActionButton from flet.grid_view import GridView @@ -31,3 +34,4 @@ from flet.text_button import TextButton from flet.textfield import TextField from flet.theme import Theme +from flet.vertical_divider import VerticalDivider diff --git a/sdk/python/flet/divider.py b/sdk/python/flet/divider.py new file mode 100644 index 000000000..072b07f22 --- /dev/null +++ b/sdk/python/flet/divider.py @@ -0,0 +1,66 @@ +from typing import Union + +import beartype + +from flet.control import Control, OptionalNumber +from flet.ref import Ref + + +class Divider(Control): + def __init__( + self, + ref: Ref = None, + opacity: OptionalNumber = None, + visible: bool = None, + data: any = None, + # + # Specific + # + height: OptionalNumber = None, + thickness: OptionalNumber = None, + color: str = None, + ): + + Control.__init__( + self, + ref=ref, + opacity=opacity, + visible=visible, + data=data, + ) + + self.height = height + self.thickness = thickness + self.color = color + + def _get_control_name(self): + return "divider" + + # height + @property + def height(self): + return self._get_attr("height") + + @height.setter + @beartype + def height(self, value: OptionalNumber): + self._set_attr("height", value) + + # thickness + @property + def thickness(self): + return self._get_attr("thickness") + + @thickness.setter + @beartype + def thickness(self, value: OptionalNumber): + self._set_attr("thickness", value) + + # color + @property + def color(self): + return self._get_attr("color") + + @color.setter + def color(self, value): + self._set_attr("color", value) diff --git a/sdk/python/flet/elevated_button.py b/sdk/python/flet/elevated_button.py index c48c03e18..9df7ec0e3 100644 --- a/sdk/python/flet/elevated_button.py +++ b/sdk/python/flet/elevated_button.py @@ -23,8 +23,9 @@ def __init__( # # Specific # - filled: bool = None, - filled_tonal: bool = None, + color: str = None, + bgcolor: str = None, + elevation: OptionalNumber = None, icon: str = None, icon_color: str = None, content: Control = None, @@ -45,8 +46,9 @@ def __init__( ) self.text = text - self.filled = filled - self.filled_tonal = filled_tonal + self.color = color + self.bgcolor = bgcolor + self.elevation = elevation self.icon = icon self.icon_color = icon_color self.content = content @@ -71,25 +73,33 @@ def text(self): def text(self, value): self._set_attr("text", value) - # filled + # color @property - def filled(self): - return self._get_attr("filled", data_type="bool", def_value=False) + def color(self): + return self._get_attr("color") - @filled.setter - @beartype - def filled(self, value: Optional[bool]): - self._set_attr("filled", value) + @color.setter + def color(self, value): + self._set_attr("color", value) + + # bgcolor + @property + def bgcolor(self): + return self._get_attr("bgcolor") + + @bgcolor.setter + def bgcolor(self, value): + self._set_attr("bgcolor", value) - # filled_tonal + # elevation @property - def filled_tonal(self): - return self._get_attr("filledTonal", data_type="bool", def_value=False) + def elevation(self) -> OptionalNumber: + return self._get_attr("elevation") - @filled_tonal.setter + @elevation.setter @beartype - def filled_tonal(self, value: Optional[bool]): - self._set_attr("filledTonal", value) + def elevation(self, value: OptionalNumber): + self._set_attr("elevation", value) # icon @property diff --git a/sdk/python/flet/filled_button.py b/sdk/python/flet/filled_button.py new file mode 100644 index 000000000..f55b76a10 --- /dev/null +++ b/sdk/python/flet/filled_button.py @@ -0,0 +1,53 @@ +from typing import Union + +from flet.control import Control, OptionalNumber +from flet.elevated_button import ElevatedButton +from flet.ref import Ref + + +class FilledButton(ElevatedButton): + def __init__( + self, + text: str = None, + ref: Ref = None, + width: OptionalNumber = None, + height: OptionalNumber = None, + expand: Union[bool, int] = None, + opacity: OptionalNumber = None, + tooltip: str = None, + visible: bool = None, + disabled: bool = None, + data: any = None, + # + # Specific + # + icon: str = None, + icon_color: str = None, + content: Control = None, + autofocus: bool = None, + on_click=None, + ): + ElevatedButton.__init__( + self, + ref=ref, + width=width, + height=height, + expand=expand, + opacity=opacity, + tooltip=tooltip, + visible=visible, + disabled=disabled, + data=data, + # + # Specific + # + color="onPrimary", + bgcolor="primary", + elevation=0, + text=text, + icon=icon, + icon_color=icon_color, + content=content, + autofocus=autofocus, + on_click=on_click, + ) diff --git a/sdk/python/flet/filled_tonal_button.py b/sdk/python/flet/filled_tonal_button.py new file mode 100644 index 000000000..33c242321 --- /dev/null +++ b/sdk/python/flet/filled_tonal_button.py @@ -0,0 +1,53 @@ +from typing import Union + +from flet.control import Control, OptionalNumber +from flet.elevated_button import ElevatedButton +from flet.ref import Ref + + +class FilledTonalButton(ElevatedButton): + def __init__( + self, + text: str = None, + ref: Ref = None, + width: OptionalNumber = None, + height: OptionalNumber = None, + expand: Union[bool, int] = None, + opacity: OptionalNumber = None, + tooltip: str = None, + visible: bool = None, + disabled: bool = None, + data: any = None, + # + # Specific + # + icon: str = None, + icon_color: str = None, + content: Control = None, + autofocus: bool = None, + on_click=None, + ): + ElevatedButton.__init__( + self, + ref=ref, + width=width, + height=height, + expand=expand, + opacity=opacity, + tooltip=tooltip, + visible=visible, + disabled=disabled, + data=data, + # + # Specific + # + color="onSecondaryContainer", + bgcolor="secondaryContainer", + elevation=0, + text=text, + icon=icon, + icon_color=icon_color, + content=content, + autofocus=autofocus, + on_click=on_click, + ) diff --git a/sdk/python/flet/icon.py b/sdk/python/flet/icon.py index a687d97de..f04e92f3e 100644 --- a/sdk/python/flet/icon.py +++ b/sdk/python/flet/icon.py @@ -1,5 +1,7 @@ from typing import Union +from beartype import beartype + from flet.control import Control, OptionalNumber from flet.ref import Ref @@ -19,7 +21,7 @@ def __init__( # Specific # color: str = None, - size: float = None, + size: OptionalNumber = None, ): Control.__init__( @@ -64,5 +66,6 @@ def size(self): return self._get_attr("size") @size.setter - def size(self, value): + @beartype + def size(self, value: OptionalNumber): self._set_attr("size", value) diff --git a/sdk/python/flet/vertical_divider.py b/sdk/python/flet/vertical_divider.py new file mode 100644 index 000000000..bb15cf30f --- /dev/null +++ b/sdk/python/flet/vertical_divider.py @@ -0,0 +1,66 @@ +from typing import Union + +import beartype + +from flet.control import Control, OptionalNumber +from flet.ref import Ref + + +class VerticalDivider(Control): + def __init__( + self, + ref: Ref = None, + opacity: OptionalNumber = None, + visible: bool = None, + data: any = None, + # + # Specific + # + width: OptionalNumber = None, + thickness: OptionalNumber = None, + color: str = None, + ): + + Control.__init__( + self, + ref=ref, + opacity=opacity, + visible=visible, + data=data, + ) + + self.width = width + self.thickness = thickness + self.color = color + + def _get_control_name(self): + return "verticaldivider" + + # width + @property + def width(self): + return self._get_attr("width") + + @width.setter + @beartype + def width(self, value: OptionalNumber): + self._set_attr("width", value) + + # thickness + @property + def thickness(self): + return self._get_attr("thickness") + + @thickness.setter + @beartype + def thickness(self, value: OptionalNumber): + self._set_attr("thickness", value) + + # color + @property + def color(self): + return self._get_attr("color") + + @color.setter + def color(self, value): + self._set_attr("color", value) diff --git a/sdk/python/playground/buttons.py b/sdk/python/playground/buttons.py index cd5519370..8bbe34599 100644 --- a/sdk/python/playground/buttons.py +++ b/sdk/python/playground/buttons.py @@ -6,6 +6,8 @@ from flet import ( Column, ElevatedButton, + FilledButton, + FilledTonalButton, FloatingActionButton, Icon, IconButton, @@ -14,6 +16,7 @@ Row, Text, TextButton, + colors, icons, theme, ) @@ -24,6 +27,7 @@ def main(page: Page): page.title = "Buttons Example" page.theme_mode = "light" + # page.theme = theme.Theme(color_scheme_seed="green", use_material3=True) page.padding = 50 page.floating_action_button = FloatingActionButton(icon=icons.ADD) @@ -56,70 +60,46 @@ def main(page: Page): alignment="spaceAround", ), ), + Row( + [ + ElevatedButton( + "Red button", + color=colors.WHITE, + bgcolor=colors.RED, + icon=icons.PALETTE, + ), + ElevatedButton( + "Green button", + color=colors.WHITE, + bgcolor=colors.GREEN, + icon=icons.PALETTE, + ), + ElevatedButton( + "Yellow button", + color=colors.BLACK, + bgcolor=colors.YELLOW, + icon=icons.PALETTE, + ), + ] + ), # # # - Text("Elevated Filled button", style="headlineMedium"), - ElevatedButton("Filled button", filled=True), - ElevatedButton("Disabled button", filled=True, disabled=True), - ElevatedButton( - "Button with icon and tooltip", - filled=True, - icon="chair_outlined", - tooltip="Hey, click me!", - ), - ElevatedButton( - "Button with colorful icon", - filled=True, - icon="park_rounded", - icon_color="green400", - ), - ElevatedButton( - width=150, - filled=True, - content=Row( - [ - Icon(name="favorite", color="pink"), - Icon(name="audiotrack", color="green"), - Icon(name="beach_access", color="blue"), - ], - alignment="spaceAround", - ), - ), + Text("Filled button", style="headlineMedium"), + FilledButton("Filled button"), + FilledButton("Disabled button", disabled=True), + FilledButton("Filled with icon", icon=icons.ADD), # # # - Text("Elevated Filled Tonal button", style="headlineMedium"), - ElevatedButton("Tonal button", filled_tonal=True), - ElevatedButton("Disabled button", filled_tonal=True, disabled=True), - ElevatedButton( - "Button with icon and tooltip", - filled_tonal=True, - icon="chair_outlined", - tooltip="Hey, click me!", - ), - ElevatedButton( - "Button with colorful icon", - filled_tonal=True, - icon="park_rounded", - icon_color="green400", - ), - ElevatedButton( - width=150, - filled_tonal=True, - content=Row( - [ - Icon(name="favorite", color="pink"), - Icon(name="audiotrack", color="green"), - Icon(name="beach_access", color="blue"), - ], - alignment="spaceAround", - ), - ), + Text("Filled tonal button", style="headlineMedium"), + FilledTonalButton("Filled tonal button"), + FilledTonalButton("Disabled button", disabled=True), + FilledTonalButton("Filled tonal with icon", icon=icons.ADD), # # # - Text("Outlined buttons", style="headlineMedium"), + Text("Outlined button", style="headlineMedium"), OutlinedButton("Normal button"), OutlinedButton("Disabled button", disabled=True), OutlinedButton("Button with icon", icon="chair_outlined"), @@ -139,7 +119,7 @@ def main(page: Page): alignment="spaceAround", ), ), - Text("Text buttons", style="headlineMedium"), + Text("Text button", style="headlineMedium"), TextButton("Normal button"), TextButton("Disabled button", disabled=True), TextButton("Button with icon", icon="chair_outlined"), @@ -159,7 +139,7 @@ def main(page: Page): alignment="spaceAround", ), ), - Text("Icon buttons", style="headlineMedium"), + Text("Icon button", style="headlineMedium"), Row( [ IconButton(