Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
91a980d
Refactor DataTable decoration and update tests
InesaFitsner Aug 25, 2025
3ddf0b5
Refactor theme color handling and update docs
InesaFitsner Aug 25, 2025
864d847
Update theme color scheme handling and docs
InesaFitsner Aug 25, 2025
18b8505
Improve divider theme handling in theme parsing
InesaFitsner Aug 25, 2025
78da9c2
Add Divider theme support and update tests
InesaFitsner Aug 26, 2025
f306cbb
Merge branch 'main' into inesa/integration-tests-p5
InesaFitsner Aug 26, 2025
531b0bf
Add default text to dropdown in test
InesaFitsner Aug 26, 2025
1303065
Refactor dropdown integration tests and add golden images
InesaFitsner Aug 26, 2025
ede5b0c
Enhance Dropdown menu style customization
InesaFitsner Aug 26, 2025
087627f
Merge branch 'main' into inesa/integration-tests-p5
InesaFitsner Sep 1, 2025
08e8366
Fixed integration tests: Remove tester service from page initialization
FeodorFitsner Sep 3, 2025
4296a10
Merge remote-tracking branch 'origin/v1-fix-tests' into inesa/integra…
InesaFitsner Sep 3, 2025
b433863
Add button theme integration test and docstring
InesaFitsner Sep 3, 2025
32531b8
Merge branch 'main' into inesa/integration-tests-p5
InesaFitsner Sep 4, 2025
0812cab
Enable text_style in button theme test
InesaFitsner Sep 4, 2025
65a450b
Add outlined button theme integration test and docstring
InesaFitsner Sep 4, 2025
c1e730c
Add TextButton theme integration test and docstring
InesaFitsner Sep 4, 2025
75e1691
Add FilledButton theme integration test and docstring
InesaFitsner Sep 4, 2025
e915c4e
Add IconButton theme support and integration test
InesaFitsner Sep 4, 2025
53c2fec
Update ExpansionTile tests and theme documentation
InesaFitsner Sep 4, 2025
9438119
Update ExpansionTile properties and golden image
InesaFitsner Sep 4, 2025
e2bd6fb
Update ExpansionTile integration tests and screenshots
InesaFitsner Sep 4, 2025
63b0d26
Add ExpansionTile theme tests and update screenshot names
InesaFitsner Sep 4, 2025
778c971
Comment out text_style in button theme tests
InesaFitsner Sep 4, 2025
6fffb87
Add tests and docs for FloatingActionButton location
InesaFitsner Sep 5, 2025
538837e
Add FloatingActionButton property tests and golden images
InesaFitsner Sep 6, 2025
5cc9b9b
Add hover state test for FloatingActionButton
InesaFitsner Sep 6, 2025
c3f9c87
Refactor FloatingActionButtonTheme property names
InesaFitsner Sep 8, 2025
c19ff7b
Refactor ListTile tests and update golden images
InesaFitsner Sep 8, 2025
8218a56
Enhance ListTile integration tests on macOS
InesaFitsner Sep 8, 2025
35eab8e
Refactor ListTile splash color property
InesaFitsner Sep 9, 2025
592242b
Refactor ListTile integration tests and update golden files
InesaFitsner Sep 9, 2025
0cb2b3a
Update ListTile hover behavior and golden images
InesaFitsner Sep 9, 2025
f45ecff
updated tests
InesaFitsner Sep 9, 2025
5be3c8a
Refactor ListTile optional properties and theme key
InesaFitsner Sep 9, 2025
0d1049a
Add ListTile theme integration test and golden image
InesaFitsner Sep 9, 2025
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
9 changes: 5 additions & 4 deletions packages/flet/lib/src/controls/datatable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,24 @@ class DataTableControl extends StatelessWidget {
var gradient = control.getGradient("gradient", theme);
var horizontalLines = control.getBorderSide("horizontal_lLines", theme);
var verticalLines = control.getBorderSide("vertical_lines", theme);
var defaultDecoration =
theme.dataTableTheme.decoration ?? const BoxDecoration();
var defaultDecoration = theme.dataTableTheme.decoration as BoxDecoration? ??
const BoxDecoration();

BoxDecoration? decoration;

if (bgcolor != null ||
border != null ||
borderRadius != null ||
gradient != null) {
decoration = (defaultDecoration as BoxDecoration).copyWith(
decoration = defaultDecoration.copyWith(
color: parseColor(bgcolor, theme),
border: border,
borderRadius: borderRadius,
gradient: gradient);
}

var datatable = DataTable(
decoration: decoration,
decoration: decoration ?? defaultDecoration,
border: (horizontalLines != null || verticalLines != null)
? TableBorder(
horizontalInside: horizontalLines ?? BorderSide.none,
Expand Down
41 changes: 33 additions & 8 deletions packages/flet/lib/src/controls/dropdown.dart
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ class _DropdownControlState extends State<DropdownControl> {
widget.control.getColor("focused_border_color", context);
var borderWidth = widget.control.getDouble("border_width");
var focusedBorderWidth = widget.control.getDouble("focused_border_width");
var menuWidth = widget.control.getDouble("menu_width", double.infinity)!;
var menuWidth = widget.control.getDouble("menu_width");
var bgColor = widget.control.getWidgetStateColor("bgcolor", theme);
var elevation = widget.control.getWidgetStateDouble("elevation");

FormFieldInputBorder inputBorder = widget.control
.getFormFieldInputBorder("border", FormFieldInputBorder.outline)!;
Expand Down Expand Up @@ -148,8 +150,30 @@ class _DropdownControlState extends State<DropdownControl> {

TextStyle? textStyle = widget.control.getTextStyle("text_style", theme);
if (textSize != null || color != null) {
textStyle = (textStyle ?? const TextStyle()).copyWith(
fontSize: textSize, color: color ?? theme.colorScheme.onSurface);
textStyle =
(textStyle ?? theme.dropdownMenuTheme.textStyle ?? const TextStyle())
.copyWith(
fontSize: textSize,
color: color ?? theme.colorScheme.onSurface);
}

MenuStyle? menuStyle = widget.control.getMenuStyle("menu_style", theme);
if (bgColor != null || elevation != null || menuWidth != null) {
menuStyle =
(menuStyle ?? theme.dropdownMenuTheme.menuStyle ?? const MenuStyle())
.copyWith(
backgroundColor: bgColor,
elevation: elevation,
fixedSize: WidgetStateProperty.all(
Size.fromWidth(menuWidth ?? double.infinity)));
}

if (textSize != null || color != null) {
textStyle =
(textStyle ?? theme.dropdownMenuTheme.textStyle ?? const TextStyle())
.copyWith(
fontSize: textSize,
color: color ?? theme.colorScheme.onSurface);
}

var items = widget.control
Expand Down Expand Up @@ -213,11 +237,12 @@ class _DropdownControlState extends State<DropdownControl> {
errorText: widget.control.getString("error_text"),
hintText: widget.control.getString("hint_text"),
helperText: widget.control.getString("helper_text"),
menuStyle: MenuStyle(
backgroundColor: widget.control.getWidgetStateColor("bgcolor", theme),
elevation: widget.control.getWidgetStateDouble("elevation"),
fixedSize: WidgetStateProperty.all(Size.fromWidth(menuWidth)),
),
// menuStyle: MenuStyle(
// backgroundColor: widget.control.getWidgetStateColor("bgcolor", theme),
// elevation: widget.control.getWidgetStateDouble("elevation"),
// fixedSize: WidgetStateProperty.all(Size.fromWidth(menuWidth)),
// ),
menuStyle: menuStyle,
inputDecorationTheme: inputDecorationTheme,
onSelected: widget.control.disabled
? null
Expand Down
3 changes: 1 addition & 2 deletions packages/flet/lib/src/controls/expansion_tile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ class ExpansionTileControl extends StatelessWidget {

var affinity = control.getListTileControlAffinity(
"affinity", ListTileControlAffinity.platform)!;
var clipBehavior =
parseClip(control.getString("clip_behavior"), Clip.none)!;
var clipBehavior = parseClip(control.getString("clip_behavior"));

var expandedCrossAxisAlignment = control.getCrossAxisAlignment(
"expanded_cross_axis_alignment", CrossAxisAlignment.center)!;
Expand Down
6 changes: 3 additions & 3 deletions packages/flet/lib/src/controls/list_tile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ class ListTileControl extends StatelessWidget with FletStoreMixin {
Widget tile = ListTile(
autofocus: control.getBool("autofocus", false)!,
contentPadding: control.getPadding("content_padding"),
isThreeLine: control.getBool("is_three_line", false)!,
isThreeLine: control.getBool("is_three_line"),
selected: control.getBool("selected", false)!,
dense: control.getBool("dense", false)!,
dense: control.getBool("dense"),
onTap: onPressed,
onLongPress: onLongPress,
enabled: !control.disabled,
Expand All @@ -89,7 +89,7 @@ class ListTileControl extends StatelessWidget with FletStoreMixin {
selectedColor: control.getColor("selected_color", context),
focusColor: control.getColor("focus_color", context),
tileColor: control.getColor("bgcolor", context),
splashColor: control.getColor("bgcolor_activated", context),
splashColor: control.getColor("splash_color", context),
hoverColor: control.getColor("hover_color", context),
iconColor: control.getColor("icon_color", context),
textColor: control.getColor("text_color", context),
Expand Down
38 changes: 15 additions & 23 deletions packages/flet/lib/src/utils/theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,25 +94,21 @@ ThemeData parseTheme(
{ThemeData? parentTheme}) {
ThemeData? theme = parentTheme;

var primarySwatch = parseColor(value?["primary_swatch"], theme);
var colorSchemeSeed = parseColor(value?["color_scheme_seed"], theme);

if (colorSchemeSeed != null) primarySwatch = null;

if (colorSchemeSeed == null && primarySwatch == null) {
colorSchemeSeed = Colors.blue;
}
var colorSchemeSeed =
parseColor(value?["color_scheme_seed"], theme) ?? Colors.blue;

// create new theme
theme ??= ThemeData(
primarySwatch:
primarySwatch != null ? primarySwatch as MaterialColor : null,
colorSchemeSeed: colorSchemeSeed,
fontFamily: value?["font_family"],
brightness: brightness,
useMaterial3: value?["use_material3"] ?? primarySwatch == null,
useMaterial3: value?["use_material3"],
);

ColorScheme? colorScheme = parseColorScheme(value?["color_scheme"], theme);
DividerThemeData? dividerTheme =
parseDividerTheme(value?["divider_theme"], theme);

theme = theme.copyWith(
extensions: {
SystemUiOverlayStyleTheme(value?["system_overlay_style"] != null
Expand All @@ -124,7 +120,7 @@ ThemeData parseTheme(
parseVisualDensity(value?["visual_density"], theme.visualDensity)!,
pageTransitionsTheme: parsePageTransitions(
value?["page_transitions"], theme.pageTransitionsTheme)!,
colorScheme: parseColorScheme(value?["color_scheme"], theme),
colorScheme: colorScheme,
textTheme: parseTextTheme(value?["text_theme"], theme, theme.textTheme),
primaryTextTheme: parseTextTheme(
value?["primary_text_theme"], theme, theme.primaryTextTheme),
Expand All @@ -140,13 +136,10 @@ ThemeData parseTheme(
canvasColor: parseColor(value?["canvas_color"], theme),
scaffoldBackgroundColor: parseColor(value?["scaffold_bgcolor"], theme),
cardColor: parseColor(value?["card_bgcolor"], theme),
dividerColor: parseColor(value?["divider_color"], theme),
dividerColor: dividerTheme?.color,
hintColor: parseColor(value?["hint_color"], theme),
shadowColor: parseColor(value?["shadow_color"], theme),
shadowColor: colorScheme?.shadow,
secondaryHeaderColor: parseColor(value?["secondary_header_color"], theme),
primaryColor: parseColor(value?["primary_color"], theme),
primaryColorLight: parseColor(value?["primary_color_light"], theme),
primaryColorDark: parseColor(value?["primary_color_dark"], theme),
dialogTheme: parseDialogTheme(value?["dialog_theme"], theme),
bottomSheetTheme:
parseBottomSheetTheme(value?["bottom_sheet_theme"], theme),
Expand All @@ -160,15 +153,14 @@ ThemeData parseTheme(
radioTheme: parseRadioTheme(value?["radio_theme"], theme),
badgeTheme: parseBadgeTheme(value?["badge_theme"], theme),
switchTheme: parseSwitchTheme(value?["switch_theme"], context),
dividerTheme: parseDividerTheme(value?["divider_theme"], theme),
dividerTheme: dividerTheme,
snackBarTheme: parseSnackBarTheme(value?["snackbar_theme"], theme),
bannerTheme: parseBannerTheme(value?["banner_theme"], theme),
datePickerTheme: parseDatePickerTheme(value?["date_picker_theme"], theme),
navigationRailTheme:
parseNavigationRailTheme(value?["navigation_rail_theme"], theme),
appBarTheme: parseAppBarTheme(value?["appbar_theme"], theme),
dropdownMenuTheme:
parseDropdownMenuTheme(value?["dropdown_menu_theme"], theme),
dropdownMenuTheme: parseDropdownMenuTheme(value?["dropdown_theme"], theme),
listTileTheme: parseListTileTheme(value?["list_tile_theme"], theme),
tooltipTheme: parseTooltipTheme(value?["tooltip_theme"], context),
expansionTileTheme:
Expand Down Expand Up @@ -538,8 +530,8 @@ FloatingActionButtonThemeData? parseFloatingActionButtonTheme(
shape: parseShape(value["shape"], theme),
enableFeedback: parseBool(value["enable_feedback"]),
extendedPadding: parsePadding(value["extended_padding"]),
extendedTextStyle: parseTextStyle(value["extended_text_style"], theme),
extendedIconLabelSpacing: parseDouble(value["extended_icon_label_spacing"]),
extendedTextStyle: parseTextStyle(value["text_style"], theme),
extendedIconLabelSpacing: parseDouble(value["icon_label_spacing"]),
mouseCursor: parseWidgetStateMouseCursor(value["mouse_cursor"]),
iconSize: parseDouble(value["icon_size"]),
extendedSizeConstraints:
Expand Down Expand Up @@ -858,7 +850,7 @@ ListTileThemeData? parseListTileTheme(
leadingAndTrailingTextStyle:
parseTextStyle(value["leading_and_trailing_text_style"], theme),
mouseCursor: parseWidgetStateMouseCursor(value["mouse_cursor"]),
minTileHeight: parseDouble(value["min_tile_height"]),
minTileHeight: parseDouble(value["min_height"]),
);
}

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Diff not rendered.
Diff not rendered.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import pytest
import pytest_asyncio

import flet as ft
import flet.testing as ftt


# Create a new flet_app instance for each test method
@pytest_asyncio.fixture(scope="function", autouse=True)
def flet_app(flet_app_function):
return flet_app_function


@pytest.mark.asyncio(loop_scope="function")
async def test_button_theme(flet_app: ftt.FletTestApp):
flet_app.page.theme = ft.Theme(
button_theme=ft.ButtonTheme(
style=ft.ButtonStyle(
bgcolor=ft.Colors.GREEN,
shape=ft.BeveledRectangleBorder(
radius=5,
),
side=ft.BorderSide(width=5, color=ft.Colors.GREEN_900),
padding=ft.Padding.all(10),
# text_style=ft.TextStyle(
# size=15,
# italic=True,
# color=ft.Colors.ORANGE, # color is not shown on the button text,
# # use style.color instead
# ),
),
)
)

flet_app.page.window.width = 400
flet_app.page.window.height = 600

scr_1 = ft.Screenshot(
ft.Button(content="Button"),
)
flet_app.page.add(scr_1)
# flet_app.page.update()
await flet_app.tester.pump_and_settle()

flet_app.assert_screenshot(
"theme_1",
await scr_1.capture(pixel_ratio=flet_app.screenshots_pixel_ratio),
)
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import pytest
import pytest_asyncio

import flet as ft
import flet.testing as ftt


@pytest.mark.asyncio(loop_scope="module")
async def test_data_table_basic(flet_app: ftt.FletTestApp, request):
# Create a new flet_app instance for each test method
@pytest_asyncio.fixture(scope="function", autouse=True)
def flet_app(flet_app_function):
return flet_app_function


@pytest.mark.asyncio(loop_scope="function")
async def test_basic(flet_app: ftt.FletTestApp, request):
await flet_app.assert_control_screenshot(
request.node.name,
ft.DataTable(
Expand All @@ -25,3 +32,76 @@ async def test_data_table_basic(flet_app: ftt.FletTestApp, request):
],
),
)


@pytest.mark.asyncio(loop_scope="function")
async def test_theme(flet_app: ftt.FletTestApp):
flet_app.page.theme = ft.Theme(
data_table_theme=ft.DataTableTheme(
checkbox_horizontal_margin=10,
column_spacing=50,
data_row_max_height=200,
data_row_min_height=0,
data_row_color=ft.Colors.GREEN_200,
data_text_style=ft.TextStyle(color=ft.Colors.GREEN_800),
divider_thickness=10,
horizontal_margin=20,
heading_text_style=ft.TextStyle(italic=True),
heading_row_color=ft.Colors.ORANGE_200,
heading_row_height=100,
data_row_cursor=ft.MouseCursor.FORBIDDEN, # doesn't show on screenshot
heading_row_alignment=ft.MainAxisAlignment.START,
heading_cell_cursor=ft.MouseCursor.HELP, # doesn't show on screenshot
decoration=ft.BoxDecoration(
shape=ft.BoxShape.RECTANGLE,
bgcolor=ft.Colors.PURPLE_100,
border=ft.Border.all(color=ft.Colors.RED),
),
),
divider_theme=ft.DividerTheme(
color=ft.Colors.GREEN,
thickness=5,
),
)
flet_app.page.window.width = 400
flet_app.page.window.height = 600

scr_1 = ft.Screenshot(
ft.DataTable(
# bgcolor=ft.Colors.BLUE_100,
show_checkbox_column=True,
# border=ft.Border.all(color=ft.Colors.RED),
# border_radius=30,
columns=[
ft.DataColumn(label="Column 1"),
ft.DataColumn(label=ft.Text("Column 2")),
ft.DataColumn(label=ft.Text("Column 3")),
],
rows=[
ft.DataRow(
on_select_change=lambda e: print(f"Selected row {e.data}"),
cells=[
ft.DataCell("Item 1"),
ft.DataCell(ft.Text("Item 2")),
ft.DataCell(ft.Text("Item 3")),
],
),
ft.DataRow(
on_select_change=lambda e: print(f"Selected row {e.data}"),
cells=[
ft.DataCell("Item 1"),
ft.DataCell(ft.Text("Item 2")),
ft.DataCell(ft.Text("Item 3")),
],
),
],
)
)
flet_app.page.add(scr_1)
flet_app.page.update()
await flet_app.tester.pump_and_settle()

flet_app.assert_screenshot(
"theme_1",
await scr_1.capture(pixel_ratio=flet_app.screenshots_pixel_ratio),
)
Loading