DataTable2
Enhanced data table for Flet that adds sticky headers, fixed rows/columns, and other UX improvements via the flet-datatable2 extension.
It wraps the Flutter data_table_2 package.
Platform Support
| Platform | Windows | macOS | Linux | iOS | Android | Web |
|---|---|---|---|---|---|---|
| Supported | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Usage
Add flet-datatable2 to your project dependencies:
- uv
- pip
uv add flet-datatable2
pip install flet-datatable2
Examples
Example 1
import flet as ft
import flet_datatable2 as fdt
def main(page: ft.Page):
page.add(
ft.SafeArea(
content=fdt.DataTable2(
empty=ft.Text("This table is empty."),
columns=[
fdt.DataColumn2(label=ft.Text("First name")),
fdt.DataColumn2(label=ft.Text("Last name")),
fdt.DataColumn2(label=ft.Text("Age"), numeric=True),
],
),
)
)
if __name__ == "__main__":
ft.run(main)
Example 2
from data import desserts
import flet as ft
import flet_datatable2 as ftd
def main(page: ft.Page):
page.vertical_alignment = ft.MainAxisAlignment.CENTER
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
sorted_desserts = list(desserts)
data_table: ftd.DataTable2 | None = None
def handle_row_selection_change(e: ft.Event[ftd.DataRow2]) -> None:
e.control.selected = not e.control.selected
e.control.update()
def sort_column(e: ft.DataColumnSortEvent) -> None:
if data_table is None:
return
sorters = [
lambda d: d.name.lower(),
lambda d: d.calories,
lambda d: d.fat,
lambda d: d.carbs,
lambda d: d.protein,
lambda d: d.sodium,
lambda d: d.calcium,
lambda d: d.iron,
]
sorted_desserts.sort(key=sorters[e.column_index], reverse=not e.ascending)
data_table.rows = get_data_rows(sorted_desserts)
data_table.sort_column_index = e.column_index
data_table.sort_ascending = e.ascending
data_table.update()
def get_data_columns() -> list[ftd.DataColumn2]:
return [
ftd.DataColumn2(
label=ft.Text("Name"),
size=ftd.DataColumnSize.L,
on_sort=sort_column,
heading_row_alignment=ft.MainAxisAlignment.START,
),
ftd.DataColumn2(
label=ft.Text("Calories"),
on_sort=sort_column,
numeric=True,
heading_row_alignment=ft.MainAxisAlignment.END,
),
ftd.DataColumn2(label=ft.Text("Fat"), on_sort=sort_column, numeric=True),
ftd.DataColumn2(label=ft.Text("Carbs"), on_sort=sort_column, numeric=True),
ftd.DataColumn2(
label=ft.Text("Protein"),
on_sort=sort_column,
numeric=True,
),
ftd.DataColumn2(label=ft.Text("Sodium"), on_sort=sort_column, numeric=True),
ftd.DataColumn2(
label=ft.Text("Calcium"),
on_sort=sort_column,
numeric=True,
),
ftd.DataColumn2(label=ft.Text("Iron"), on_sort=sort_column, numeric=True),
]
def get_data_rows(items: list) -> list[ftd.DataRow2]:
return [
ftd.DataRow2(
specific_row_height=50,
on_select_change=handle_row_selection_change,
cells=[
ft.DataCell(content=ft.Text(dessert.name)),
ft.DataCell(content=ft.Text(dessert.calories)),
ft.DataCell(content=ft.Text(dessert.fat)),
ft.DataCell(content=ft.Text(dessert.carbs)),
ft.DataCell(content=ft.Text(dessert.protein)),
ft.DataCell(content=ft.Text(dessert.sodium)),
ft.DataCell(content=ft.Text(dessert.calcium)),
ft.DataCell(content=ft.Text(dessert.iron)),
],
)
for dessert in items
]
data_table = ftd.DataTable2(
show_checkbox_column=True,
expand=True,
column_spacing=0,
heading_row_color=ft.Colors.SECONDARY_CONTAINER,
horizontal_margin=12,
sort_ascending=True,
bottom_margin=10,
min_width=600,
on_select_all=lambda _: print("All selected"),
columns=get_data_columns(),
rows=get_data_rows(sorted_desserts),
)
page.add(
ft.SafeArea(
content=data_table,
)
)
if __name__ == "__main__":
ft.run(main)

Description
Provides sticky header row, scrollable data rows, and additional layout flexibility with DataColumn2 and DataRow2.
DataTable2 doesn't support
flet.DataTable.data_row_min_height
and flet.DataTable.data_row_max_height
properties present in the parent DataTable.
Use data_row_height instead.
Inherits: DataTable
Properties
bottom_margin- Adds space after the last row if set.checkbox_alignment- Alignment of the checkbox.columns- A list of table columns.data_row_checkbox_theme- Overrides theme of checkboxes in each data row.data_row_height- Height of each data row.data_row_max_heightdata_row_min_heightempty- Placeholder control shown when there are no data rows.fixed_columns_color- Background color for sticky left columns.fixed_corner_color- Background color of the fixed top-left corner cell.fixed_left_columns- Number of sticky columns on the left.fixed_top_rows- Number of sticky rows from the top.heading_checkbox_theme- Overrides theme of the heading checkbox.lm_ratio- Ratio of Large column width to Medium.min_width- Minimum table width before horizontal scrolling kicks in.rows- A list of table rows.show_heading_checkbox- Controls visibility of the heading checkbox.sm_ratio- Ratio of Small column width to Medium.sort_arrow_animation_duration- Duration of sort arrow animation.sort_arrow_icon- Icon shown when sorting is applied.sort_arrow_icon_color- When set always overrides/preceeds default arrow icon color.visible_horizontal_scroll_bar- Determines visibility of the horizontal scrollbar.visible_vertical_scroll_bar- Determines visibility of the vertical scrollbar.
Properties
bottom_marginclass-attributeinstance-attribute
bottom_margin: Optional[Number] = NoneAdds space after the last row if set.
checkbox_alignmentclass-attributeinstance-attribute
checkbox_alignment: Alignment = field(default_factory=(lambda: Alignment.CENTER))Alignment of the checkbox.
data_row_checkbox_themeclass-attributeinstance-attribute
data_row_checkbox_theme: Optional[CheckboxTheme] = NoneOverrides theme of checkboxes in each data row.
data_row_heightclass-attributeinstance-attribute
data_row_height: Optional[Number] = NoneHeight of each data row.
data_row_max_heightclass-attributeinstance-attribute
data_row_max_height: None = field(init=False, repr=False, compare=False, metadata={'skip': True})data_row_min_heightclass-attributeinstance-attribute
data_row_min_height: None = field(init=False, repr=False, compare=False, metadata={'skip': True})emptyclass-attributeinstance-attribute
empty: Optional[Control] = NonePlaceholder control shown when there are no data rows.
fixed_columns_colorclass-attributeinstance-attribute
fixed_columns_color: Optional[ColorValue] = NoneBackground color for sticky left columns.
fixed_corner_colorclass-attributeinstance-attribute
fixed_corner_color: Optional[ColorValue] = NoneBackground color of the fixed top-left corner cell.
fixed_left_columnsclass-attributeinstance-attribute
fixed_left_columns: int = 0Number of sticky columns on the left. Includes checkbox column, if present.
fixed_top_rowsclass-attributeinstance-attribute
fixed_top_rows: int = 1Number of sticky rows from the top. Includes heading row by default.
heading_checkbox_themeclass-attributeinstance-attribute
heading_checkbox_theme: Optional[CheckboxTheme] = NoneOverrides theme of the heading checkbox.
lm_ratioclass-attributeinstance-attribute
lm_ratio: Number = 1.2Ratio of Large column width to Medium.
min_widthclass-attributeinstance-attribute
min_width: Optional[Number] = NoneMinimum table width before horizontal scrolling kicks in.
rowsclass-attributeinstance-attribute
A list of table rows.
show_heading_checkboxclass-attributeinstance-attribute
show_heading_checkbox: bool = TrueControls visibility of the heading checkbox.
sm_ratioclass-attributeinstance-attribute
sm_ratio: Number = 0.67Ratio of Small column width to Medium.
sort_arrow_animation_durationclass-attributeinstance-attribute
sort_arrow_animation_duration: DurationValue = field(default_factory=(lambda: Duration(milliseconds=150)))Duration of sort arrow animation.
sort_arrow_iconclass-attributeinstance-attribute
sort_arrow_icon: IconData = Icons.ARROW_UPWARDIcon shown when sorting is applied.
sort_arrow_icon_colorclass-attributeinstance-attribute
sort_arrow_icon_color: Optional[ColorValue] = NoneWhen set always overrides/preceeds default arrow icon color.
visible_horizontal_scroll_barclass-attributeinstance-attribute
visible_horizontal_scroll_bar: Optional[bool] = NoneDetermines visibility of the horizontal scrollbar.
visible_vertical_scroll_barclass-attributeinstance-attribute
visible_vertical_scroll_bar: Optional[bool] = NoneDetermines visibility of the vertical scrollbar.