Во
Flutter 3.32 появился новый виджет -
Expansible. Он пришел на смену старому
ExpansionTile и его контроллеру, который больше не поддерживается. Давайте разберем на простом примере, как он работает.
Что такое Expansible:
Expansible - это
StatefulWidget, который умеет плавно раскрываться и сворачиваться. У него есть две части:
- header - заголовок, который всегда на виду.
- body - содержимое, которое появляется при раскрытии.
Анимацией управляет
AnimationController, поэтому все работает плавно и без дерганий, в отличие от старого
ExpansionTile.
Простой пример:
Самый частый сценарий - когда по тапу на заголовок раскрывается дополнительный контент. Вот как это выглядит:
class ExpansibleExample extends StatefulWidget {
@override
State<ExpansibleExample> createState() => _ExpansibleExampleState();
}
class _ExpansibleExampleState extends State<ExpansibleExample> {
final _controller = ExpansibleController();
@override
Widget build(BuildContext context) {
return Expansible(
controller: _controller,
headerBuilder: (context, animation) => ListTile(
title: Text('Нажми, чтобы раскрыть'),
onTap: () {
if (_controller.isExpanded) {
_controller.collapse();
} else {
_controller.expand();
}
},
trailing: RotationTransition(
turns: Tween<double>(begin: 0.0, end: 0.5).animate(animation),
child: Icon(Icons.arrow_drop_down),
),
),
bodyBuilder: (context, animation) => SizeTransition(
sizeFactor: animation,
child: Text('А вот и скрытый контент!'),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
Обратите внимание на несколько важных моментов. Во-первых,
ExpansibleController нужно создавать и не забывать вызывать
dispose(). Во-вторых, для плавного поворота стрелочки используется
animation, которая приходит в
headerBuilder - она уже содержит нужное значение от
0 до
0.5 оборота. В-третьих,
SizeTransition с
animation анимирует высоту
body. Все вместе дает аккуратный эффект без лишних телодвижений.
Важный нюанс про ListView:
Если вы используете
Expansible внутри
ListView или
SingleChildScrollView, обязательно передавайте
PageStorageKey. Иначе при скролле виджет будет терять свое состояние (раскрыт или свернут) и пользователь удивится.
Expansible(
key: PageStorageKey('unique_key_1'), // каждому Expansible свой ключ
controller: _controller,
headerBuilder: ...
bodyBuilder: ...
)
Гибкая настройка через expansibleBuilder:
В примере выше виджет сам собирает
header и
body в вертикальную колонку. Но если нужно изменить
layout - например, добавить отступы, фон или тень, можно использовать
expansibleBuilder:
Expansible(
expansibleBuilder: (context, header, body, animation) => Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [header, body],
),
),
)
Старый
ExpansionTile часто ломался при динамическом контенте - высота менялась рывками.
Expansible лечит эту проблему, потому что внутри честно анимирует
SizeTransition. Еще один плюс - контроллер дает полный контроль над состоянием, и вы можете раскрывать или сворачивать виджет откуда угодно, а не только из
headerBuilder.
Вывод:
Expansible - это не просто замена устаревшему виджету. Это инструмент, который решает давнюю боль Flutter-разработчиков: плавная анимация высоты, сохранение состояния при скролле и чистый
API через контроллер. Пример выше можно скопировать и вставить в любой проект - он работает из коробки. А если нужно больше гибкости, подключайте
expansibleBuilder и настраивайте под свои задачи. Если вы обновились до
Flutter 3.32, присмотритесь к этому виджету - он реально удобнее.