Skip to content

Customize lists and forms

Starting from a model registered with defaults, you will tune its list page (columns, search, order, page size, sidebar group) and its forms (read-only, hidden, password and timestamp fields).

import 'package:gisila_studio/gisila_studio.dart';
studio.register<Product>(ProductTable.metadata);

Defaults: the label is the table name title-cased, the list shows the first six columns (skipping password-like columns), search does nothing, rows are ordered by primary key, 25 per page, and every column except the primary key is editable.

studio.register<Product>(
ProductTable.metadata,
displayName: 'Product',
displayNamePlural: 'Products',
group: 'Catalog',
);

Models with the same group share a sidebar section. Models without one appear under “General”.

studio.register<Product>(
ProductTable.metadata,
listDisplay: ['sku', 'name', 'priceCents', 'isActive', 'updatedAt'],
searchFields: ['sku', 'name'],
ordering: ['-updatedAt', 'name'],
pageSize: 50,
);
  • Use the Dart field names (priceCents) or the column names (price_cents); both work.
  • Search matches each searchFields column as text with ILIKE '%term%', joined with OR. Pick indexed or short columns on big tables, since %term% cannot use a normal B-tree index.
  • ordering is not validated; a misspelled column makes the list page fail with a database error.
  • There is no click-to-sort on column headers and no filter sidebar; ordering is the only sort.
studio.register<Product>(
ProductTable.metadata,
readonlyFields: ['id', 'createdAt', 'updatedAt'],
excludeFields: ['searchVector', 'legacyData'],
passwordFields: const [], // this table has a "secret_sauce" column that is not a secret
);
Option On forms Written on save In list
readonlyFields Shown as text No If in listDisplay
excludeFields Not shown No If in listDisplay
passwordFields Masked, never pre-filled Yes; blank on edit keeps the stored value Hidden from the default list

passwordFields defaults to columns whose name contains password, passwd or secret. Pass your own list to change that, or const [] to turn it off.

studio.register<Product>(
ProductTable.metadata,
readonlyFields: ['id', 'createdAt', 'updatedAt'],
autoNowOnUpdateFields: ['updatedAt'],
);

updatedAt is set to the current UTC time on every add and edit made in the studio. Keep it in readonlyFields so nobody edits it by hand. For a createdAt set only once, give the column a database default (DEFAULT now()) and make it read-only; on add, read-only columns are left to the database.

Field types come from Postgres, not from Dart: integer columns get number inputs, boolean checkboxes, timestamp columns a date-time picker, text/json/jsonb a textarea, everything else a text input. A field is required when the column is NOT NULL without a default. See How the studio works for the full mapping and parsing rules.

Column types are cached for the life of the process; restart after migrations.

Restart the server and open /admin/<table>/:

  • the list shows your columns in order, sorted by ordering;
  • typing in the search box filters rows (?q=... in the URL);
  • the edit page shows read-only fields as plain text and leaves excluded fields out;
  • saving updates updated_at (check with SELECT updated_at FROM product WHERE ...).