From 45a9356128054313b58028d1551482662ae01096 Mon Sep 17 00:00:00 2001 From: InesaFitsner Date: Mon, 6 Nov 2023 08:59:30 -0800 Subject: [PATCH] Badge control (#2037) * Badge initial commit * label and content properties * offset property * alignment property * bgcolor property * label_visible property * fixed label bug; small_size, large_size, text_color properties * text_style property * removed commented code * example for Badge * removed comments --- package/lib/src/controls/badge.dart | 70 ++++++ package/lib/src/controls/create_control.dart | 9 + .../flet-core/src/flet_core/__init__.py | 1 + .../packages/flet-core/src/flet_core/badge.py | 212 ++++++++++++++++++ 4 files changed, 292 insertions(+) create mode 100644 package/lib/src/controls/badge.dart create mode 100644 sdk/python/packages/flet-core/src/flet_core/badge.py diff --git a/package/lib/src/controls/badge.dart b/package/lib/src/controls/badge.dart new file mode 100644 index 000000000..3ffe7caba --- /dev/null +++ b/package/lib/src/controls/badge.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import '../models/control.dart'; +import 'create_control.dart'; +import '../utils/transforms.dart'; +import '../utils/alignment.dart'; +import '../utils/colors.dart'; +import '../utils/edge_insets.dart'; +import '../utils/text.dart'; + +class BadgeControl extends StatelessWidget { + final Control? parent; + final Control control; + final List children; + final bool parentDisabled; + + const BadgeControl( + {Key? key, + required this.parent, + required this.control, + required this.children, + required this.parentDisabled}) + : super(key: key); + + @override + Widget build(BuildContext context) { + debugPrint("Badge build: ${control.id}"); + + String? label = control.attrString("labelText"); + + var contentCtrls = + children.where((c) => c.name == "content" && c.isVisible); + bool disabled = control.isDisabled || parentDisabled; + + Widget? child = contentCtrls.isNotEmpty + ? createControl(control, contentCtrls.first.id, disabled) + : null; + + var offsetDetails = parseOffset(control, "offset"); + + var bgColor = HexColor.fromString( + Theme.of(context), control.attrString("bgColor", "")!); + + var textColor = HexColor.fromString( + Theme.of(context), control.attrString("textColor", "")!); + + bool isLabelVisible = control.attrBool("isLabelVisible", true)!; + var largeSize = control.attrDouble("largeSize"); + var smallSize = control.attrDouble("smallSize"); + + return baseControl( + context, + Badge( + label: label != null ? Text(label) : null, + isLabelVisible: isLabelVisible, + offset: offsetDetails != null + ? Offset(offsetDetails.x, offsetDetails.y) + : null, + alignment: parseAlignment(control, "alignment"), + backgroundColor: bgColor, + largeSize: largeSize, + padding: parseEdgeInsets(control, "padding"), + smallSize: smallSize, + textColor: textColor, + textStyle: parseTextStyle(Theme.of(context), control, "textStyle"), + child: child, + ), + parent, + control); + } +} diff --git a/package/lib/src/controls/create_control.dart b/package/lib/src/controls/create_control.dart index 7c11c0acc..084070edf 100644 --- a/package/lib/src/controls/create_control.dart +++ b/package/lib/src/controls/create_control.dart @@ -76,6 +76,7 @@ import 'transparent_pointer.dart'; import 'vertical_divider.dart'; import 'window_drag_area.dart'; import 'range_slider.dart'; +import 'badge.dart'; Widget createControl(Control? parent, String id, bool parentDisabled, {Widget? nextChild}) { @@ -194,6 +195,14 @@ Widget createWidget(Key? key, ControlViewModel controlView, Control? parent, case "divider": return DividerControl( key: key, parent: parent, control: controlView.control); + case "badge": + return BadgeControl( + key: key, + parent: parent, + control: controlView.control, + children: controlView.children, + parentDisabled: parentDisabled, + ); case "clipboard": return ClipboardControl( parent: parent, control: controlView.control, nextChild: nextChild); 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 44cfdc762..1fd981e8d 100644 --- a/sdk/python/packages/flet-core/src/flet_core/__init__.py +++ b/sdk/python/packages/flet-core/src/flet_core/__init__.py @@ -205,3 +205,4 @@ from flet_core.view import View from flet_core.window_drag_area import WindowDragArea from flet_core.range_slider import RangeSlider +from flet_core.badge import Badge diff --git a/sdk/python/packages/flet-core/src/flet_core/badge.py b/sdk/python/packages/flet-core/src/flet_core/badge.py new file mode 100644 index 000000000..2755d15c2 --- /dev/null +++ b/sdk/python/packages/flet-core/src/flet_core/badge.py @@ -0,0 +1,212 @@ +from typing import Any, Optional + +from flet_core.control import Control, OptionalNumber +from flet_core.alignment import Alignment +from flet_core.ref import Ref +from flet_core.types import OffsetValue, PaddingValue +from flet_core.text_style import TextStyle + + +class Badge(Control): + """ + A Material Design "badge". + + Badges are used to show notifications, counts, or status information on navigation items such as NavigationBar or NavigationRail destinations + or a button's icon. + + Example: + ``` + import flet as ft + + def main(page: ft.Page): + page.title = "Badges in NavigationBar icons" + page.navigation_bar = ft.NavigationBar( + destinations=[ + ft.NavigationDestination( + icon_content=ft.Badge( + content=ft.Icon(ft.icons.EXPLORE), + small_size=10, + ), + label="Explore", + ), + ft.NavigationDestination(icon=ft.icons.COMMUTE, label="Commute"), + ft.NavigationDestination( + icon_content=ft.Badge(content=ft.Icon(ft.icons.PHONE), text="10") + ), + ] + ) + page.add(ft.Text("Body!")) + + + ft.app(target=main) + + + + ``` + + ----- + + Online docs: https://flet.dev/docs/controls/badge + """ + + def __init__( + self, + content: Optional[Control] = None, + ref: Optional[Ref] = None, + opacity: OptionalNumber = None, + visible: Optional[bool] = None, + data: Any = None, + # + # Specific + # + text: Optional[str] = None, + offset: OffsetValue = None, + alignment: Optional[Alignment] = None, + bgcolor: Optional[str] = None, + label_visible: Optional[bool] = None, + large_size: OptionalNumber = None, + padding: Optional[PaddingValue] = None, + small_size: OptionalNumber = None, + text_color: Optional[str] = None, + text_style: Optional[TextStyle] = None, + ): + Control.__init__( + self, + ref=ref, + opacity=opacity, + visible=visible, + data=data, + ) + + self.text = text + self.content = content + self.offset = offset + self.alignment = alignment + self.bgcolor = bgcolor + self.label_visible = label_visible + self.large_size = large_size + self.padding = padding + self.small_size = small_size + self.text_color = text_color + self.text_style = text_style + + def _get_control_name(self): + return "badge" + + def _before_build_command(self): + super()._before_build_command() + self._set_attr_json("offset", self.__offset) + self._set_attr_json("alignment", self.__alignment) + self._set_attr_json("padding", self.__padding) + self._set_attr_json("textStyle", self.__text_style) + + def _get_children(self): + children = [] + if self.__content is not None: + self.__content._set_attr_internal("n", "content") + children.append(self.__content) + return children + + # alignment + @property + def alignment(self) -> Optional[Alignment]: + """:obj:`Alignment`, optional: Align the child control within the container. + + Alignment is an instance of `alignment.Alignment` class object with `x` and `y` properties + representing the distance from the center of a rectangle. + """ + return self.__alignment + + @alignment.setter + def alignment(self, value: Optional[Alignment]): + self.__alignment = value + + # text + @property + def text(self) -> Optional[str]: + return self._get_attr("labelText") + + @text.setter + def text(self, value: Optional[str]): + self._set_attr("labelText", value) + + # content + @property + def content(self) -> Optional[Control]: + return self.__content + + @content.setter + def content(self, value: Optional[Control]): + self.__content = value + + # offset + @property + def offset(self) -> OffsetValue: + return self.__offset + + @offset.setter + def offset(self, value: OffsetValue): + self.__offset = value + + # bgcolor + @property + def bgcolor(self): + return self._get_attr("bgColor") + + @bgcolor.setter + def bgcolor(self, value): + self._set_attr("bgColor", value) + + # label_visible + @property + def label_visible(self) -> Optional[bool]: + return self._get_attr("isLabelVisible") + + @label_visible.setter + def label_visible(self, value: Optional[bool]): + self._set_attr("isLabelVisible", value) + + # large_size + @property + def large_size(self) -> OptionalNumber: + return self._get_attr("largeSize") + + @large_size.setter + def large_size(self, value: OptionalNumber): + self._set_attr("largeSize", value) + + # padding + @property + def padding(self) -> PaddingValue: + return self.__padding + + @padding.setter + def padding(self, value: PaddingValue): + self.__padding = value + + # small_size + @property + def small_size(self) -> OptionalNumber: + return self._get_attr("smallSize") + + @small_size.setter + def small_size(self, value: OptionalNumber): + self._set_attr("smallSize", value) + + # text_color + @property + def text_color(self): + return self._get_attr("textColor") + + @text_color.setter + def text_color(self, value): + self._set_attr("textColor", value) + + # text_style + @property + def text_style(self): + return self.__text_style + + @text_style.setter + def text_style(self, value: Optional[TextStyle]): + self.__text_style = value