{"id":32300198,"url":"https://github.com/tomasweigenast/paged-datatable","last_synced_at":"2026-02-20T09:31:33.492Z","repository":{"id":56836396,"uuid":"417133088","full_name":"tomasweigenast/paged-datatable","owner":"tomasweigenast","description":"A customizable DataTable with horizontal scrolling, cursor and offset pagination, sorting, filtering, editable columns, and expandable rows, perfect for creating rich and dynamic UIs.","archived":false,"fork":false,"pushed_at":"2025-01-02T19:07:41.000Z","size":1436,"stargazers_count":27,"open_issues_count":10,"forks_count":36,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-10-23T05:19:33.144Z","etag":null,"topics":["dart","datatable","flutter"],"latest_commit_sha":null,"homepage":"https://pub.dev/packages/paged_datatable","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tomasweigenast.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-10-14T13:04:14.000Z","updated_at":"2025-07-12T10:42:46.000Z","dependencies_parsed_at":"2023-02-01T14:45:51.042Z","dependency_job_id":"b8512bf8-b147-468c-ac92-431f86177d52","html_url":"https://github.com/tomasweigenast/paged-datatable","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/tomasweigenast/paged-datatable","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomasweigenast%2Fpaged-datatable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomasweigenast%2Fpaged-datatable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomasweigenast%2Fpaged-datatable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomasweigenast%2Fpaged-datatable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tomasweigenast","download_url":"https://codeload.github.com/tomasweigenast/paged-datatable/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomasweigenast%2Fpaged-datatable/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29647657,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-20T09:27:29.698Z","status":"ssl_error","status_checked_at":"2026-02-20T09:26:12.373Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dart","datatable","flutter"],"created_at":"2025-10-23T05:13:21.513Z","updated_at":"2026-02-20T09:31:33.487Z","avatar_url":"https://github.com/tomasweigenast.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PagedDataTable\n\n[![pub package](https://img.shields.io/pub/v/paged_datatable?label=pub.dev\u0026labelColor=333940\u0026logo=dart)](https://pub.dev/packages/paged_datatable)\n\nCompletely customisable data table which supports cursor and offset pagination, filters and horizontal scrolling out-of-the-box. It's written from scratch, no dependency from Flutter's `DataTable` nor `Table`.\nDesigned to follow Google's Material You style.\n\n## Online demo\n\n\u003ca  href=\"https://tomasweigenast.github.io/pageddatatable/#/.com\"  target=\"_blank\"\u003eCheck it out here\u003c/a\u003e\n\n## Features\n\n- **Horizontal scrolling**, allowing you to define columns wider than the viewport width.\n- **Fixed columns**, to scroll horizontally only a set of columns.\n- **Row updating on demand**, preventing you to create other views for updating fields of a class. Now you can update an object from the table directly.\n- **Cursor and offset pagination**, you decide how to paginate your data.\n- **Filtering** by date, text, number, whatever you want!\n- **Sorting** by predefined columns\n- **Page modification** using controller, to add or remove items to the current page without reloading the entire table.\n- **Themeable**, allowing you to change colors, fonts, text styles, and more!\n\n## Table Of Contents\n\n- [Setup](#setup)\n  - [Fetcher](#fetcher)\n  - [Header](#header)\n  - [Footer](#footer)\n    - [Custom footer](#custom-footer)\n  - [Columns](#columns)\n    - [TableColumn](#tablecolumnk-t)\n    - [EditableTableColumn](#editabletablecolumnk-t-v)\n    - [Custom columns](#custom-columns)\n  - [Filters](#filters)\n    - [Custom filters](#custom-filters)\n  - [Controller](#controller)\n- [Internationalization](#internationalization)\n- [Screenshots](#screenshots)\n- [Contributing](#contribute)\n\n## Setup\n\nEverything you need is a **PagedDataTable\u003cK, T\u003e** widget, which accepts two generic arguments, **K** and **T**, the type of object you will use as paging key and the type of object you will be showing in the table.\n\n\u003e Keep in mind that `K` must extends `Comparable`.\n\nThere are only two required parameters: `columns` and `fetcher`.\n\n```dart\nPagedDataTable\u003cString, Post\u003e(\n  fetcher: (int pageSize, SortModel? sortModel, FilterModel filterModel, String? pageToken) =\u003e ...,\n  columns: [...],\n)\n```\n\n### Fetcher\n\nThe fetcher is a function that gets called every time a page is requested. It gives you the current page size, sort and filter models and the page token that is requested. It must return\na `FutureOr\u003c(List\u003cT\u003e, K?)\u003e`, so you can convert it to a Future and do `async`-like requests or simply return the data.\n\nIt expects a tuple, where the first value is the list of items and the second the next page token:\n\n```dart\nPagedDataTable\u003cString, Post\u003e(\n  fetcher: (int pageSize, SortModel? sortModel, FilterModel filterModel, String? pageToken) {\n    final result = await FetchService.listPosts();\n    return (result.data, result.nextPageToken);\n  },\n  columns: [...],\n)\n```\n\nBy default, `PagedDataTable` does not copy the returned list, so, if it is a shared list, you or the table may modify the items. If you want, `PagedDataTable` can copy the list\nif you specify it in the `configuration` property:\n\n\u003e Note that `PagedDataTable` **DOES NOT** cache pages.\n\n```dart\nPagedDataTable\u003cString, Post\u003e(\n  configuration: PagedDataTableConfiguration(\n    copyItems: true,\n  ),\n)\n```\n\n### Header\n\nThe header renders the column names, but also the FilterBar exists, which is an additional header that renders the filter picker and, optionally, you can display additional widgets aligned at the right of the bar.\n\nJust pass your widget to the `filterBarChild` property. Naturally you would want to display a `PopupMenuButton` that will act as a menu.\n\n### Footer\n\nUsing the `footer` property you can render anything. If you don't pass it, it will render the `DefaultFooter` widget,\nwhich again, if not specified, will display, aligned to the right, the following widgets:\n\n- **Refresh button**: A button that can be used to refresh the current dataset.\n- **Page size selector**: A dropdown that can be used to select the current page size to use, based on the `pageSizes` property.\n- **Current page display**: Displays the current page number.\n- **Navigation buttons**: will display the previous and next buttons as `IconButton`s.\n\n#### Custom footer\n\nIf you want your own footer widget but reuse some of the already existing widgets, they are named: `RefreshButton`, `PageSizeSelector`, `CurrentPage` and `NavigationButtons`.\n\n### Columns\n\nThere are two types of columns in `PagedDataTable`:\n\n- **ReadOnlyTableColumn\u003cK, T\u003e**: renders a simple widget that does not allow edition.\n- **EditableTableColumn\u003cK, T, V\u003e**: renders a simple widget too, but this can be modified in place and modify the dataset.\n\n\u003e `K` and `T` are the same parameters defined in the `PagedDataTable` widget.\n\nEvery column type has:\n\n- **title**: the column's title. It is a widget but commonly it's a `Text` widget displaying the name.\n- **size**: configures the column's size. By default, it is a `FractionalColumnSize(.1)`, which means it will take 10% of the available width. You can use `FixedColumnSize`, `FractionalColumnSize`, `RemainingColumnSize` and `MaxColumnSize`.\n- **format**: applies a transformation to the cell's widget. You have `NumericColumnFormat`, which aligns content to the right and `AlignColumnFormat` which aligns cell's content to the `alignment` property and you can implement your own implemeting the `ColumnFormat` interface.\n- **sort** and **id**: both properties are used to indicate that a column can be used for sorting. The `id` is what you get in the Fetcher's `SortModel`. To sort, you click the column's header.\n- There are other properties that you can use to play around and modify your columns. Check out the `ReadOnlyTableColumn`'s documentation.\n\n\u003e If you want to fix columns at the left, you can specify the amount of columns to fix using the `fixedColumnCount` property.\n\n#### TableColumn\u003cK, T\u003e\n\nIs the default `ReadOnlyTableColumn` that renders a cell using the `cellBuilder` property.\n\n```dart\nPagedDataTable\u003cString, Post\u003e(\n  columns: [\n    TableColumn(\n      title: const Text(\"Author\"),\n      cellBuilder: (context, item, index) =\u003e Text(item.author),\n    ),\n  ],\n)\n```\n\n#### EditableTableColumn\u003cK, T, V\u003e\n\nThis abstract class provides two more properties, `getter` and `setter`. The first one is used to provide the value **V** to render and the second one\nis the function used to set the new value. It must return a boolean indicating if the operation succeeded or not. If is true, the cell will update its\nvalue, otherwise will keep the old one.\n\nThere are three built in editable columns, which are `DropdownTableColumn` which renders a dropdown; `TextTableColumn` which renders a `Text` until double-clicked, then it renders a `TextField` used to edit the cell's content; `LargeTextTableColumn`, which is the same as `TextTableColumn` but when\ndouble-clicked, it opens an overlay, designed to edit large text cells.\n\nYou can [create your own column](#custom-columns).\n\n#### Custom columns\n\nTo create your own columns, simply extend `ReadOnlyTableColumn` or `EditableTableColumn` depending on your needs.\n\nFor example:\n\n```dart\nclass MyColumnType\u003cK, T\u003e extends ReadOnlyTableColumn\u003cK, T\u003e {\n  @override\n  Widget build(BuildContext context, T item, int index) {\n    return MyCellWidget();\n  }\n}\n```\n\n\u003e If you want more examples, check out the implementation of the already existing column types.\n\n## Filters\n\n`PagedDataTable` allows you to define a set of filters that you can use to interactively select them using a\npopup overlay or a bottom sheet if you are in a small device.\n\nTo define filters, use the `filters` property:\n\n```dart\nPagedDataTable\u003cString, Post\u003e(\n  ...,\n  filters: [\n    TextTableFilter(\n      id: \"content\",\n      chipFormatter: (value) =\u003e 'Content has \"$value\"',\n      name: \"Content\",\n    ),\n    DropdownTableFilter\u003cGender\u003e(\n      items: Gender.values\n          .map((e) =\u003e\n              DropdownMenuItem(value: e, child: Text(e.name)))\n          .toList(growable: false),\n      chipFormatter: (value) =\u003e\n          'Author is ${value.name.toLowerCase()}',\n      id: \"authorGender\",\n      name: \"Author's Gender\",\n    ),\n  ],\n)\n```\n\nThere are five built-in filter types:\n\n- `TextTableFilter`: renders a `TextField` to filter by raw text.\n- `DropdownTableFilter`: renders a `DropdownButton` with a set of options.\n- `DateTimePickerTableFilter`: renders a `TextField` that, when tapped, opens the `DateTime` picker dialog.\n- `DateRangePickerTableFilter`: the same as `DateTimePickerTableFilter` but selects a `DateTimeRange`.\n- `ProgrammingTextFilter`: a filter that does not render nothing in the filter dialog but can be set using the controller.\n\nEvery filter type must define, at least, the `id`, the `name` and the `chipFormatter` properties. The first one is the identifier of the filter, used in the fetcher's `FilterMode` property. The name is the label displayed in the filter picker and the `chipFormatter` is a function that maps the actual selected value to a more user-friendly string that is displayed in the selected filter's chip.\n\n### Custom filters\n\nTo create your own filter, extend the `TableFilter\u003cT\u003e` abstract class, where `T` is the type of value the filter will handle. Then, implement the `buildPicker` function, which renders the actual filter picker.\n\nThe implementation of the `TextTableFilter` as an example:\n\n```dart\nfinal class TextTableFilter extends TableFilter\u003cString\u003e {\n  final InputDecoration? decoration;\n\n  const TextTableFilter({\n    this.decoration,\n    required super.chipFormatter,\n    required super.id,\n    required super.name,\n    super.initialValue,\n    super.enabled = true,\n  });\n\n  @override\n  Widget buildPicker(BuildContext context, FilterState\u003cString\u003e state) {\n    return TextFormField(\n      decoration: decoration ?? InputDecoration(labelText: name),\n      initialValue: state.value,\n      onSaved: (newValue) {\n        if (newValue != null \u0026\u0026 newValue.isNotEmpty) {\n          state.value = newValue;\n        }\n      },\n    );\n  }\n}\n```\n\n## Controller\n\nIf you want to control the table programatically, provide your own `PagedDataTableController\u003cK, V\u003e` instance passing it to the `controller` property. It provides methods to interact with rows (selecting, unselecting, removing, inserting, updating), filters, sorting, pagination, and more.\n\n## Internationalization\n\nUpdate your `MaterialApp` or `CupertinoApp` widget with the following:\n\n```dart\nlocalizationsDelegates:  const  [\n  PagedDataTableLocalization.delegate\n],\n\n```\n\nAnd you're done.\n\nAt the moment of writing this, the supported locales are:\n\n- **es**: Spanish\n- **en**: English\n- **de**: Deutsch\n\nIf you want more languages, you can [contribute](#contribute).\n\n## Screenshots\n\n![Screenshot 1](https://raw.githubusercontent.com/tomasweigenast/paged-datatable/d9c6b290d8effa1a5676db03720b3c866f44bb4c/resources/screenshot1.png)\n![Screenshot 2](https://raw.githubusercontent.com/tomasweigenast/paged-datatable/d9c6b290d8effa1a5676db03720b3c866f44bb4c/resources/screenshot2.png)\n![Screenshot 3](https://raw.githubusercontent.com/tomasweigenast/paged-datatable/d9c6b290d8effa1a5676db03720b3c866f44bb4c/resources/screenshot3.png)\n![Screenshot 4](https://raw.githubusercontent.com/tomasweigenast/paged-datatable/d9c6b290d8effa1a5676db03720b3c866f44bb4c/resources/screenshot4.png)\n![Screenshot 5](https://raw.githubusercontent.com/tomasweigenast/paged-datatable/d9c6b290d8effa1a5676db03720b3c866f44bb4c/resources/screenshot5.png)\n\n## Contribute\n\nAny suggestion to improve/add is welcome, if you want to make a PR, you are welcome :)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomasweigenast%2Fpaged-datatable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftomasweigenast%2Fpaged-datatable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomasweigenast%2Fpaged-datatable/lists"}