Filament Schemas 布局组件完全指南:Grid 栅格系统、Flex、Fieldset 与容器查询
【免费下载链接】filamentA powerful open-source UI framework for Laravel • Build and ship apps & admin panels fast with Livewire项目地址: https://gitcode.com/GitHub_Trending/fi/filament
本篇技术指南以 Filament 的 Schemas 布局体系为核心,系统讲解如何利用columns()、columnSpan()、columnStart()、columnOrder()构建响应式多列布局,并深入剖析Grid、Flex、Fieldset三个基础布局组件的用法与源码实现。读完本文,你将掌握 Filament 布局组件的全部核心 API、基于 Tailwind 断点与容器查询(container queries)的响应式方案,以及延迟加载、间距控制、自定义 HTML 属性等进阶技巧,可直接用于表单、Infolist、Panel 页面等场景。
布局组件全景
Filament 的栅格系统允许你使用任意布局组件创建响应式、多列布局。packages/schemas/docs/02-layouts.md中给出了内置布局组件的完整清单,它们都位于Filament\Schemas\Components命名空间:
- Grid
- Flex
- Fieldset
- Section
- Tabs
- Wizard
- Callout
- Empty state
你也可以创建自定义布局组件以按任意方式展示组件。布局组件可无限嵌套子 schema,配合表单字段、Infolist 条目与 Prime 组件 组合出任意复杂的界面。
栅格系统:columns()方法
所有布局组件都提供columns()方法,支持两种传参方式:
- 整数:
columns(2)表示在lg断点及以上使用 2 列,所有更小设备只显示 1 列。 - 数组:以断点为键、列数为值,例如
columns(['md' => 2, 'xl' => 4])会在中等设备显示 2 列、超宽设备显示 4 列。更小设备的默认断点为 1 列,除非你显式提供default键。
断点(sm、md、lg、xl、2xl)由 Tailwind 定义,对应视口宽度依次约为 640px、768px、1024px、1280px、1536px。
从源码看,columns()定义于 packages/schemas/src/Concerns/HasColumns.php:传入整数时会被自动包装为['lg' => $columns],传入数组时与原配置合并;它同时接受Closure,运行时通过evaluate()解析动态值。getAllColumns()会基于默认值['default' => 1, 'sm' => null, 'md' => null, 'lg' => null, 'xl' => null, '2xl' => null]填充未指定的断点。若当前对象是Schema且存在父组件,还会继承父组件的列配置(见HasColumns.php第 64-66 行)。
除了静态值,columns()也接受函数动态计算列数,并可向函数注入各种工具参数(如Get、$record、$operation、$livewire、$component等,详见 Schema 总览)。例如根据当前用户权限动态决定列数:
use Filament\Schemas\Components\Grid; Grid::make(fn (): array => [ 'lg' => auth()->user()->isAdmin() ? 4 : 6, ])->schema([ // ... ]);列跨度:columnSpan()
除了指定布局组件自身有多少列,还可以用columnSpan()指定某个组件在父栅格中占据多少列:
- 整数:
columnSpan(2)表示在lg断点及以上占据 2 列,更小设备只占 1 列。 - 数组:
columnSpan(['md' => 2, 'xl' => 4])表示中等设备占 2 列、超宽设备占 4 列;更小设备默认 1 列,除非提供default键。 'full':columnSpan('full')表示在lg断点及以上占满父栅格整行,更小设备占 1 列。columnSpanFull():在所有设备上占满父栅格整行,与父栅格列数无关。
源码位于 packages/support/src/Concerns/CanSpanColumns.php:整数跨度会被包装为['default' => 1, 'lg' => $span];columnSpanFull()实际等价于columnSpan(['default' => 'full'])。由于columnSpan()是Filament\Support\Concerns\CanSpanColumnstrait 提供的,所有组件(而不只是布局组件)都天然具备该方法——这正是文档示例中表单字段可以直接调用columnSpan()的原因。
列起始:columnStart()
若希望组件从栅格的指定列开始,使用columnStart():
- 整数:
columnStart(2)表示在lg断点及以上从第 2 列开始,更小设备从第 1 列开始。 - 数组:
columnStart(['md' => 2, 'xl' => 4])表示中等设备从第 2 列开始、超宽设备从第 4 列开始;更小设备默认第 1 列,除非提供default键。
例如,让文本框始终从栅格中间开始(无论栅格有多少列):
use Filament\Forms\Components\TextInput; use Filament\Schemas\Components\Grid; Grid::make() ->columns([ 'sm' => 3, 'xl' => 6, '2xl' => 8, ]) ->schema([ TextInput::make('name') ->columnStart([ 'sm' => 2, 'xl' => 3, '2xl' => 4, ]), // ... ])此例中,栅格在小屏为 3 列、超宽为 6 列、超超宽为 8 列;文本框分别从第 2、3、4 列开始,效果上始终位于栅格中点。
columnStart()的实现同样位于 CanSpanColumns.php,整数会包装为['lg' => $start]。方法同样接受Closure动态计算。
列排序:columnOrder()
在不改变 HTML 结构中组件顺序的前提下,可用columnOrder()控制组件在栅格中的视觉顺序:
- 整数:
columnOrder(2)表示在lg断点及以上按顺序值 2 显示;更小设备使用默认顺序,除非提供default键。 - 数组:
columnOrder(['md' => 2, 'xl' => 4])分别设置中等、超宽设备上的顺序值。 - 闭包:
columnOrder(fn () => 1)动态计算顺序。
use Filament\Forms\Components\TextInput; use Filament\Schemas\Components\Grid; Grid::make() ->columns(3) ->schema([ TextInput::make('first') ->columnOrder(3), // This will appear last TextInput::make('second') ->columnOrder(1), // This will appear first TextInput::make('third') ->columnOrder(2), // This will appear second ])也可结合断点做响应式排序,在不同屏幕尺寸下呈现不同顺序:
use Filament\Forms\Components\TextInput; use Filament\Schemas\Components\Grid; Grid::make() ->columns([ 'sm' => 2, 'lg' => 3, ]) ->schema([ TextInput::make('title') ->columnOrder([ 'default' => 1, 'lg' => 3, ]), TextInput::make('description') ->columnOrder([ 'default' => 2, 'lg' => 1, ]), TextInput::make('category') ->columnOrder([ 'default' => 3, 'lg' => 2, ]), ])此例中,小屏顺序为 title、description、category;大屏顺序变为 description、category、title。
columnOrder()实现在 packages/support/src/Concerns/CanOrderColumns.php,整数包装为['lg' => $order],同样支持闭包动态计算。
一个完整的响应式栅格示例
由于所有布局组件都支持columns(),可以把栅格配置直接写到 Section 等布局组件上,在内部再配合columnSpan()、columnOrder()精细控制每个字段:
use Filament\Forms\Components\TextInput; use Filament\Schemas\Components\Section; Section::make() ->columns([ 'sm' => 3, 'xl' => 6, '2xl' => 8, ]) ->schema([ TextInput::make('name') ->columnSpan([ 'default' => 1, 'sm' => 2, 'xl' => 3, '2xl' => 4, ]) ->columnOrder([ 'default' => 2, 'xl' => 1, ]), TextInput::make('email') ->columnSpan([ 'default' => 1, 'xl' => 2, ]) ->columnOrder([ 'default' => 1, 'xl' => 2, ]), // ... ])行为解读:小于sm时每行 1 列,sm及以上 3 列,xl及以上 6 列,2xl及以上 8 列;name 字段在sm起占 2 列、xl起占 3 列、2xl起占 4 列,email 在xl起占 2 列。排序方面,小于xl时 email 在前、name 在后;xl及以上则相反。这正是「不改 DOM、纯配置化响应式」的典型用法,其中TextInput等文本输入组件作为表单字段天然继承了columnSpan()能力。
基础布局组件
Grid 组件
所有布局组件都支持columns(),但Grid是唯一一个显式栅格语法的布局组件:无需额外样式,直接把列配置传给Grid::make()即可:
use Filament\Schemas\Components\Grid; Grid::make([ 'default' => 1, 'sm' => 2, 'md' => 3, 'lg' => 4, 'xl' => 6, '2xl' => 8, ]) ->schema([ // ... ])从 packages/schemas/src/Components/Grid.php 的源码可见,Grid::make()的默认列数为2,其渲染逻辑(toEmbeddedHtml())仅仅输出一个包裹childSchema的<div>,因此没有任何额外的卡片、标题等视觉样式。测试用例 tests/src/Schemas/Components/GridTest.php 也验证了「默认 2 列」「可用整数构造」「可用响应式数组构造」三种行为。
Flex 组件
Flex基于 CSS Flexbox 实现弹性宽度布局,它不使用 Filament 的栅格系统:
use Filament\Forms\Components\Textarea; use Filament\Forms\Components\TextInput; use Filament\Forms\Components\Toggle; use Filament\Schemas\Components\Section; use Filament\Schemas\Components\Flex; Flex::make([ Section::make([ TextInput::make('title'), Textarea::make('content'), ]), Section::make([ Toggle::make('is_published'), Toggle::make('is_featured'), ])->grow(false), ])->from('md')此例中第一个 Section 会grow()吃掉剩余水平空间,第二个 Section 保持自身所需宽度,形成经典的「弹性宽度侧栏」效果。
from()控制切换到水平布局的 Tailwind 断点(sm、md、lg、xl、2xl):本例在md及以上水平并排,更小设备则垂直堆叠。其实现位于 packages/support/src/Concerns/HasFromBreakpoint.php,Flex 通过use HasFromBreakpoint引入。grow()定义于 packages/support/src/Concerns/CanGrow.php,默认true,可传布尔值或闭包动态控制。
grow()和from()同样支持闭包与工具注入。tests/src/Schemas/Components/FlexTest.php 覆盖了alignment()、verticalAlignment()、from()的静态值与闭包写法,以及多种渲染场景。
Fieldset 组件
Fieldset用于把字段分组:每个 fieldset 自带标签(label)、边框,默认是两列栅格:
use Filament\Schemas\Components\Fieldset; Fieldset::make('Label') ->columns([ 'default' => 1, 'md' => 2, 'xl' => 3, ]) ->schema([ // ... ])「默认两列」可以从源码证实:packages/schemas/src/Components/Fieldset.php 的setUp()方法中调用了$this->columns(2)。此外make()的标签参数可以是string | Htmlable | Closure | null,支持闭包动态生成。渲染时,组件输出原生<fieldset>/<legend>结构(见Fieldset.php第 68-71 行),语义良好。
移除 Fieldset 边框
用contained(false)去掉 fieldset 的容器边框:
use Filament\Schemas\Components\Fieldset; Fieldset::make('Label') ->contained(false) ->schema([ // ... ])该能力来自Filament\Support\Concerns\CanBeContainedtrait(Fieldset.php第 17 行引入),渲染时通过fi-fieldset-not-containedCSS 类控制样式(见Fieldset.php第 64 行)。
控制组件间距
紧凑模式:dense()
dense()将组件间间距缩减 50%,创建更紧凑的布局:
use Filament\Schemas\Components\Fieldset; Fieldset::make('Dense') ->dense() ->schema([ // ... ])去除间距:gap(false)
gap(false)完全移除组件间的间距:
use Filament\Schemas\Components\Fieldset; Fieldset::make('No gap') ->gap(false) ->schema([ // ... ])两者的底层实现在 packages/schemas/src/Concerns/HasGap.php:dense()/gap()都接受布尔值或闭包;hasGap()与isDense()具有继承语义——Schema会查询父组件的设置,组件则向容器逐级上溯(见第 23-31 行与第 42-51 行)。因此在一个 Section 上调用->dense(),其内部所有字段的间距都会收紧。
使用容器查询(Container Queries)
传统断点基于视口尺寸,而容器查询基于父容器的尺寸。当容器大小与视口无关时(例如内容区随可折叠侧栏动态伸缩),容器查询尤为有用。
第一步:指定容器。用gridContainer()把元素标记为容器,其宽度决定布局:
use Filament\Schemas\Components\Grid; Grid::make() ->gridContainer() ->columns([ // ... ]) ->schema([ // ... ])gridContainer()定义于 packages/schemas/src/Components/Concerns/CanBeGridContainer.php,默认值为false,可传闭包条件动态控制。
第二步:使用容器断点。标记为容器后,该元素及其子元素即可用容器断点替代标准断点——例如@md表示容器宽度至少 448px,@xl表示至少 576px:
use Filament\Schemas\Components\Grid; Grid::make() ->gridContainer() ->columns([ '@md' => 3, '@xl' => 4, ]) ->schema([ // ... ])容器断点同样可用于columnSpan()、columnStart()、columnOrder():
use Filament\Forms\Components\TextInput; use Filament\Schemas\Components\Grid; Grid::make() ->gridContainer() ->columns([ '@md' => 3, '@xl' => 4, ]) ->schema([ TextInput::make('name') ->columnSpan([ '@md' => 2, '@xl' => 3, ]) ->columnOrder([ 'default' => 2, '@xl' => 1, ]), TextInput::make('email') ->columnSpan([ 'default' => 1, '@xl' => 1, ]) ->columnOrder([ 'default' => 1, '@xl' => 2, ]), // ... ])本例中,容器宽度小于@xl(576px)时 email 在前、name 在后;宽度达到 576px 后顺序反转。
在旧浏览器上支持容器查询
容器查询的浏览器支持率不如传统断点。Filament 提供!@前缀的回退断点:当浏览器不支持容器查询时,自动应用回退值。例如栅格列数同时定义容器断点与回退断点:
use Filament\Schemas\Components\Grid; Grid::make() ->gridContainer() ->columns([ '@md' => 3, '@xl' => 4, '!@md' => 2, '!@xl' => 3, ]) ->schema([ // ... ])!@回退断点也可用于columnSpan()、columnStart()、columnOrder():
use Filament\Forms\Components\TextInput; use Filament\Schemas\Components\Grid; Grid::make() ->gridContainer() ->columns([ '@md' => 3, '@xl' => 4, '!@md' => 2, '!@xl' => 3, ]) ->schema([ TextInput::make('name') ->columnSpan([ '@md' => 2, '@xl' => 3, '!@md' => 2, '!@xl' => 2, ]) ->columnOrder([ 'default' => 2, '@xl' => 1, '!@xl' => 1, ]), TextInput::make('email') ->columnOrder([ 'default' => 1, '@xl' => 2, '!@xl' => 2, ]), // ... ])回退断点确保不支持容器查询的浏览器也能随视口尺寸响应,例如大屏下仍然保持 name 在前、email 在后。
延迟加载子 Schema
当布局包含渲染代价高昂的组件时,可向其schema()传入一个Schema对象并调用deferLoading()。子 schema 初始仅渲染一个加载指示器,待其进入视口后才真正加载:
use Filament\Forms\Components\TextInput; use Filament\Schemas\Components\Group; use Filament\Schemas\Schema; Group::make() ->key('customerDetails') ->schema( Schema::make() ->components([ TextInput::make('name'), TextInput::make('email') ->email(), ]) ->deferLoading(), )本例使用Group(它自身没有任何视觉样式),其他接受子 schema 的布局组件亦可采用同样写法。每个延迟加载的 schema 必须有唯一 key:上例中它继承了带 key 的父组件customerDetails;也可以直接在子 schema 上调用key()。更多细节见 Schema 总览(如验证错误自动提前加载、隐藏组件内不加载、加载后保持存活等行为)。deferLoading()定义于 packages/schemas/src/Schema.php,接受布尔值或闭包条件。
给布局组件添加额外 HTML 属性
通过extraAttributes()向组件最外层 HTML 元素合并额外属性,以「属性名 => 属性值」数组形式传入:
use Filament\Schemas\Components\Section; Section::make() ->extraAttributes(['class' => 'custom-section-style'])实现位于 packages/support/src/Concerns/HasExtraAttributes.php,签名默认extraAttributes(array | Closure $attributes, bool $merge = false):
- 默认情况下,多次调用
extraAttributes()会覆盖之前的属性; - 若希望合并而非覆盖,传入
merge: true:
Section::make() ->extraAttributes(['class' => 'a'], merge: true) ->extraAttributes(['data-id' => '123'], merge: true)该方法同样接受闭包,支持运行时动态计算与工具参数注入,常用于条件化样式、无障碍role/aria-*属性或自定义数据属性。
结语与延伸阅读
Filament 的布局体系以「所有布局组件支持columns()、所有组件支持columnSpan()」为核心,配合断点数组、闭包动态计算与容器查询,可在纯 PHP 声明式代码中构建完整、可复用的响应式界面,无需手写 CSS 与 JavaScript。相关主题可继续阅读:
- Schema 总览(含组件工具注入与延迟加载详解)
- Section 布局组件
- Tabs 布局组件
- Wizard 布局组件
- Callout 布局组件
- Empty state 布局组件
- 自定义布局组件
- 相关源码:HasColumns、HasGap、CanSpanColumns、CanOrderColumns、CanBeGridContainer
- 相关测试:GridTest、FlexTest
【免费下载链接】filamentA powerful open-source UI framework for Laravel • Build and ship apps & admin panels fast with Livewire项目地址: https://gitcode.com/GitHub_Trending/fi/filament
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考