Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 47 additions & 34 deletions package/lib/src/controls/container.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:convert';
import 'dart:typed_data';

import 'package:collection/collection.dart';
import 'package:flet/src/utils/shadows.dart';
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';

Expand Down Expand Up @@ -64,6 +65,7 @@ class ContainerControl extends StatelessWidget {
: null;

var animation = parseAnimation(control, "animate");
var blur = parseBlur(control, "blur");

final server = FletAppServices.of(context).server;

Expand Down Expand Up @@ -107,15 +109,20 @@ class ContainerControl extends StatelessWidget {
control.attrString("shape", "")!.toLowerCase(),
orElse: () => BoxShape.rectangle);

var borderRadius = parseBorderRadius(control, "borderRadius");

var boxDecor = BoxDecoration(
color: bgColor,
gradient: gradient,
image: image,
backgroundBlendMode:
bgColor != null || gradient != null ? blendMode : null,
border: parseBorder(Theme.of(context), control, "border"),
borderRadius: parseBorderRadius(control, "borderRadius"),
shape: shape);
borderRadius: borderRadius,
shape: shape,
boxShadow: parseBoxShadow(Theme.of(context), control, "shadow"));

Widget? result;

if ((onClick || onLongPress || onHover) && ink && !disabled) {
var ink = Ink(
Expand Down Expand Up @@ -165,36 +172,33 @@ class ContainerControl extends StatelessWidget {
child: child,
),
));
return constrainedControl(
context,
animation == null
? Container(
width: control.attrDouble("width"),
height: control.attrDouble("height"),
margin: parseEdgeInsets(control, "margin"),
clipBehavior: clipBehavior,
child: ink,
)
: AnimatedContainer(
duration: animation.duration,
curve: animation.curve,
width: control.attrDouble("width"),
height: control.attrDouble("height"),
margin: parseEdgeInsets(control, "margin"),
clipBehavior: clipBehavior,
onEnd: control.attrBool("onAnimationEnd", false)!
? () {
server.sendPageEvent(
eventTarget: control.id,
eventName: "animation_end",
eventData: "container");
}
: null,
child: ink),
parent,
control);

result = animation == null
? Container(
width: control.attrDouble("width"),
height: control.attrDouble("height"),
margin: parseEdgeInsets(control, "margin"),
clipBehavior: clipBehavior,
child: ink,
)
: AnimatedContainer(
duration: animation.duration,
curve: animation.curve,
width: control.attrDouble("width"),
height: control.attrDouble("height"),
margin: parseEdgeInsets(control, "margin"),
clipBehavior: clipBehavior,
onEnd: control.attrBool("onAnimationEnd", false)!
? () {
server.sendPageEvent(
eventTarget: control.id,
eventName: "animation_end",
eventData: "container");
}
: null,
child: ink);
} else {
Widget container = animation == null
result = animation == null
? Container(
width: control.attrDouble("width"),
height: control.attrDouble("height"),
Expand Down Expand Up @@ -225,7 +229,7 @@ class ContainerControl extends StatelessWidget {
child: child);

if ((onClick || onLongPress || onHover) && !disabled) {
container = MouseRegion(
result = MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: onHover
? (value) {
Expand Down Expand Up @@ -271,12 +275,21 @@ class ContainerControl extends StatelessWidget {
eventData: "");
}
: null,
child: container,
child: result,
),
);
}
return constrainedControl(context, container, parent, control);
}

if (blur != null) {
result = borderRadius != null
? ClipRRect(
borderRadius: borderRadius,
child: BackdropFilter(filter: blur, child: result))
: ClipRect(child: BackdropFilter(filter: blur, child: result));
}

return constrainedControl(context, result, parent, control);
});
}
}
1 change: 1 addition & 0 deletions package/lib/src/utils/colors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ Map<String, MaterialColor> _materialColors = {
"deeporange": Colors.deepOrange,
"brown": Colors.brown,
"bluegrey": Colors.blueGrey,
"grey": Colors.grey
};

Map<String, MaterialAccentColor> _materialAccentColors = {
Expand Down
33 changes: 33 additions & 0 deletions package/lib/src/utils/images.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import 'dart:convert';
import 'dart:ui';

import 'package:collection/collection.dart';
import 'package:flet/src/utils/numbers.dart';
import 'package:flutter/material.dart';

import '../models/control.dart';
import 'gradient.dart';

export 'images_io.dart' if (dart.library.js) "images_web.dart";

Expand All @@ -17,3 +22,31 @@ BoxFit? parseBoxFit(Control control, String propName) {
return BoxFit.values.firstWhereOrNull((e) =>
e.name.toLowerCase() == control.attrString(propName, "")!.toLowerCase());
}

ImageFilter? parseBlur(Control control, String propName) {
var v = control.attrString(propName, null);
if (v == null) {
return null;
}

final j1 = json.decode(v);
return blurImageFilterFromJSON(j1);
}

ImageFilter blurImageFilterFromJSON(dynamic json) {
double sigmaX = 0.0;
double sigmaY = 0.0;
TileMode tileMode = TileMode.clamp;
if (json is int || json is double) {
sigmaX = sigmaY = parseDouble(json);
} else if (json is List && json.length > 1) {
sigmaX = parseDouble(json[0]);
sigmaY = parseDouble(json[1]);
} else {
sigmaX = parseDouble(json["sigma_x"]);
sigmaY = parseDouble(json["sigma_y"]);
tileMode = parseTileMode(json["tile_mode"]);
}

return ImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode);
}
45 changes: 45 additions & 0 deletions package/lib/src/utils/shadows.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import 'dart:convert';

import 'package:flutter/material.dart';

import '../models/control.dart';
import '../utils/numbers.dart';
import '../utils/transforms.dart';
import 'colors.dart';

List<BoxShadow> parseBoxShadow(
ThemeData theme, Control control, String propName) {
var v = control.attrString(propName, null);
if (v == null) {
return [];
}

final j1 = json.decode(v);
return boxShadowsFromJSON(theme, j1);
}

List<BoxShadow> boxShadowsFromJSON(ThemeData theme, dynamic json) {
if (json is List) {
return json.map((e) => boxShadowFromJSON(theme, e)).toList();
} else {
return [boxShadowFromJSON(theme, json)];
}
}

BoxShadow boxShadowFromJSON(ThemeData theme, dynamic json) {
var offset = json["offset"] != null ? offsetFromJSON(json["offset"]) : null;
return BoxShadow(
color: json["color"] != null
? HexColor.fromString(theme, json["color"]) ?? const Color(0xFF000000)
: const Color(0xFF000000),
offset: offset != null ? Offset(offset.x, offset.y) : Offset.zero,
blurStyle: json["blur_style"] != null
? BlurStyle.values
.firstWhere((e) => e.name.toLowerCase() == json["blur_style"])
: BlurStyle.normal,
blurRadius:
json["blur_radius"] != null ? parseDouble(json["blur_radius"]) : 0.0,
spreadRadius: json["spread_radius"] != null
? parseDouble(json["spread_radius"])
: 0.0);
}
9 changes: 8 additions & 1 deletion sdk/python/packages/flet-core/src/flet_core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,14 @@
from flet_core.checkbox import Checkbox
from flet_core.circle_avatar import CircleAvatar
from flet_core.column import Column
from flet_core.container import Container, ContainerTapEvent
from flet_core.container import (
Blur,
BlurTileMode,
BoxShadow,
Container,
ContainerTapEvent,
ShadowBlurStyle,
)
from flet_core.control import Control, OptionalNumber
from flet_core.control_event import ControlEvent
from flet_core.datatable import (
Expand Down
11 changes: 11 additions & 0 deletions sdk/python/packages/flet-core/src/flet_core/colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,14 @@
DEEP_ORANGE_ACCENT_200 = "deeporangeaccent200"
DEEP_ORANGE_ACCENT_400 = "deeporangeaccent400"
DEEP_ORANGE_ACCENT_700 = "deeporangeaccent700"
GREY = "grey"
GREY_50 = "grey50"
GREY_100 = "grey100"
GREY_200 = "grey200"
GREY_300 = "grey300"
GREY_400 = "grey400"
GREY_500 = "grey500"
GREY_600 = "grey600"
GREY_700 = "grey700"
GREY_800 = "grey800"
GREY_900 = "grey900"
66 changes: 65 additions & 1 deletion sdk/python/packages/flet-core/src/flet_core/container.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import dataclasses
import json
from typing import Any, Optional, Union
from dataclasses import field
from enum import Enum
from typing import Any, List, Optional, Tuple, Union

from flet_core.alignment import Alignment
from flet_core.border import Border
Expand Down Expand Up @@ -35,6 +38,36 @@
from typing_extensions import Literal


class BlurTileMode(Enum):
CLAMP = "clamp"
DECAL = "decal"
MIRROR = "mirror"
REPEATED = "repeated"


class ShadowBlurStyle(Enum):
NORMAL = "normal"
SOLID = "solid"
OUTER = "outer"
INNER = "inner"


@dataclasses.dataclass
class Blur:
sigma_x: float
sigma_y: float
Comment on lines +57 to +58
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why only float?
Union[int, float] won't be better?

tile_mode: BlurTileMode = field(default=BlurTileMode.CLAMP)


@dataclasses.dataclass
class BoxShadow:
spread_radius: Optional[float] = field(default=None)
blur_radius: Optional[float] = field(default=None)
Comment on lines +64 to +65
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. Union[int, float]

color: Optional[str] = field(default=None)
offset: OffsetValue = field(default=None)
tile_mode: ShadowBlurStyle = field(default=ShadowBlurStyle.NORMAL)


class Container(ConstrainedControl):
"""
Container allows to decorate a control with background color and border and position it with padding, margin and alignment.
Expand Down Expand Up @@ -110,6 +143,10 @@ def __init__(
clip_behavior: Optional[ClipBehavior] = None,
ink: Optional[bool] = None,
animate: AnimationValue = None,
blur: Union[
None, float, int, Tuple[Union[float, int], Union[float, int]], Blur
] = None,
shadow: Union[None, BoxShadow, List[BoxShadow]] = None,
on_click=None,
on_long_press=None,
on_hover=None,
Expand Down Expand Up @@ -168,6 +205,8 @@ def convert_container_tap_event_data(e):
self.clip_behavior = clip_behavior
self.ink = ink
self.animate = animate
self.blur = blur
self.shadow = shadow
self.on_click = on_click
self.on_long_press = on_long_press
self.on_hover = on_hover
Expand All @@ -184,6 +223,8 @@ def _before_build_command(self):
self._set_attr_json("alignment", self.__alignment)
self._set_attr_json("gradient", self.__gradient)
self._set_attr_json("animate", self.__animate)
self._set_attr_json("blur", self.__blur)
self._set_attr_json("shadow", self.__shadow if self.__shadow else None)

def _get_children(self):
children = []
Expand Down Expand Up @@ -258,6 +299,29 @@ def blend_mode(self, value: BlendMode):
def __set_blend_mode(self, value: BlendModeString):
self._set_attr("blendMode", value)

# blur
@property
def blur(self):
return self.__blur

@blur.setter
def blur(
self,
value: Union[
None, float, int, Tuple[Union[float, int], Union[float, int]], Blur
],
):
self.__blur = value

# shadow
@property
def shadow(self):
return self.__shadow

@shadow.setter
def shadow(self, value: Union[None, BoxShadow, List[BoxShadow]]):
self.__shadow = value if value is not None else []

# border
@property
def border(self) -> Optional[Border]:
Expand Down