Flutter. Material 3 Expressive Shapes
FlutterPulseЭта статья переведена специально для канала FlutterPulse. В этом канале вы найдёте много интересных вещей, связанных с Flutter. Не забывайте подписываться! 🚀

И как использовать их в вашем Flutter-приложении.
Material 3 Expressive — это расширение системы дизайна Material от Google, которое добавляет новые компоненты, функции и тактики дизайна для создания более эмоционально насыщенных и динамичных пользовательских интерфейсов.
Одним из таких новых компонентов дизайна является Shape library.

К сожалению, в Flutter пока нет поддержки, но, к счастью, активный участник сообщества уже создал пакет для этого.
Если вы участник, пожалуйста, продолжайте,иначе, читайте полную историю здесь.
Установка
flutter pub add flutter_m3shapes
Использование
На практике мы можем придавать форму чему угодно с его помощью.
Простой пример с цветными M3Container.

M3Container.pixelTriangle(
color: Colors.deepOrange,
width: 100,
height: 100,
child: const Center(child: Text("pixelTriangle")),
),
Очень легко использовать, как мы видим.
Также есть конструктор, который принимает форму в качестве позиционного аргумента, поэтому приведенное выше можно переписать как:
M3Container(
Shapes.pixel_triangle,
color: Colors.deepOrange,
width: 100,
height: 100,
child: const Center(child: Text("pixelTriangle")),
),
Этот конструктор позволяет создавать пользовательские компоненты, которые принимают форму в качестве аргумента, или изменять форму динамически.
Изображения профилей

M3Container(
Shapes.flower,
clipBehavior: Clip.antiAlias,
width: 100,
height: 100,
child: Image.network(
'https://i.pravatar.cc/150?img=5',
),
),
M3Container — это что-то вроде смеси Container с ClipRect или ClipOval, но проще в использовании и с большим количеством предопределенных форм.
Кнопки с формой
Я вспомнил себя как творческого члена сообщества и создал класс EzShapedButton.
Вот как это выглядит:

Выглядит забавно, правда?
Мы можем выбрать любую форму и передать её в конструктор:
EzShapedButton(
shape: Shapes.ghostish,
color: Colors.cyan.shade900,
width: 150,
height: 60,
child: Ez3dText(
'Нажми меня!',
fontSize: 18,
),
),
Вот сам класс кнопки:
final class EzShapedButton extends StatelessWidget {
final Widget? child;
final GestureTapCallback? onTap;
final double? width;
final double? height;
final Color? color;
final Shapes shape;
const EzShapedButton(
{super.key,
this.child,
this.onTap,
this.width = double.infinity,
this.height = 60,
this.color = Colors.green,
required this.shape,});
@override
Widget build(Object context) {
return EzScaleOnTap(
onTap: onTap,
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: M3Container(
shape,
width: width,
height: height,
color: color,
clipBehavior: Clip.antiAlias,
child: Center(child: child),
),
));
}
}Вам понадобится EzScaleOnTap:
final class EzScaleOnTap extends StatefulWidget {
final double? diff;
final Widget child;
final GestureTapCallback? onTap;
const EzScaleOnTap({super.key, this.diff = -0.05,
required this.child, this.onTap});
@override
State<StatefulWidget> createState() {
return _ScaleState();
}
}
class _ScaleState extends State<EzScaleOnTap> {
double _scale = 1;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: widget.onTap,
onTapDown: (_) => setState(() => _scale += widget.diff!),
onTapUp: (_) => setState(() => _scale = 1.0),
child: Transform.scale(
scale: _scale,
child: widget.child,
),
);
}
}
Ez3dText не обязателен, можно использовать обычный Text, но в любом случае:
class Ez3dText extends StatelessWidget {
final String text;
final double fontSize;
final Color color;
final Color shadowColor;
final int depth;
const Ez3dText(this.text, {
super.key,
this.fontSize = 18,
this.color = Colors.white,
this.shadowColor = Colors.black,
this.depth = 3,
});
@override
Widget build(BuildContext context) {
return Stack(
children: [
// Создание слоёв теней
for (int i = depth; i > 0; i--)
Positioned(
left: i.toDouble(),
top: i.toDouble(),
child: Text(
text,
style: TextStyle(
fontSize: fontSize,
color: shadowColor.withValues(alpha: 0.3),
fontWeight: FontWeight.bold,
),
),
),
// Основной текст сверху
Text(
text,
style: TextStyle(
fontSize: fontSize,
color: color,
fontWeight: FontWeight.bold,
),
),
],
);
}
}Заключение
flutter_m3shapes — это хороший пакет, простой в использовании и полезный.
Спасибо за чтение и удачного кодинга!