Как добиться такого же поведения, как в CSS, когда поля отрицательны? Является ли это возможным? Как это сделать в пользовательском интерфейсе Flutter?

Около 2 лет назад я создал образец виджета карты для веб-панели с помощью React JS. Меня вдохновил исходный код на Codepen. Я хочу воссоздать его с помощью кода Flutter. Поскольку он создан для веб-интерфейса, стиль использует код CSS. В исходном коде создатель использует отрицательное поле для поля значка.

 .card
    margin: 0 -8px 32px -8px
    color: rgba(0,0,0,.87)
    box-shadow: 2px 2px 8px rgba(0,0,0,.05)

Я воссоздал аналогичную карту в соответствии с потребностями моей панели инструментов с помощью React JS. Я использую абсолютную позицию и отрицательное значение в свойстве top. Посмотрите на значки, сложенные поверх карточек.

boxicon: {
    position: "absolute",
    zIndex: 1,
    top: -20,
    left: 12,

Это простой виджет карты, который довольно часто можно найти в веб-представлении панели инструментов. С момента первой загрузки в CodeSandbox, теперь у него 4,3 тыс. просмотров и 49 разветвлений (последнее обновление 17 апреля 2023 г.) 😄 . Вы можете найти его здесь: (React) карточка в адаптивном стиле с материальным пользовательским интерфейсом — CodeSandbox

А во Флаттере?

Во Flutter мы можем использовать margin для некоторых виджетов. Например, Контейнер, Карточка и т. д. А свойство margin реализует класс EdgeInsetsGeometry. Этот класс также используется для установки отступов во Flutter.

пример того, как мы используем padding и margin во Flutter:

Container(
   padding: EdgeInsets.all(8),
   margin: EdgeInsets.fromLRTB(4,8,4,2))

EdgeInsetsGeometry во Flutter имеет метод isNonNegative. Так….

Значения поля должны быть положительными, мы не можем присвоить отрицательные значения EdgeInsetsGeometry.

Мы получим неудачное утверждение, если укажем отрицательные значения свойств margins или padding.

════════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown building Card(dirty, dependencies: [_InheritedTheme, _LocalizationsScope-[GlobalKey#95d0d]]):
'package:flutter/src/widgets/container.dart': Failed assertion: line 267 pos 15: 'margin == null || margin.isNonNegative': is not true.

Чтобы создать виджет карты, мы можем сделать аналогичное поведение или создать аналогичную форму. Вот несколько вариантов достижения этого.

  • Виджет Позиционированный | В CSS, если вы знаете о position: absolute. Во Flutter мы можем добиться аналогичного поведения с помощью виджета Positioned.
  • Виджет Преобразование | Это похоже на свойство преобразования в CSS. Он применяет преобразование перед визуализацией виджета.
  • CustomPainter | Я не пробовал, но думаю, что с помощью класса CustomPainter можно создать индивидуальную фигуру, подобную карточке выше.

В этой статье я буду использовать класс Transform для создания виджетов карточек, как на изображении выше. Поскольку отрицательное поле применить нельзя, мы переместим виджет значка с матричным переводом из класса Transform.

Преобразование матрицы: тип преобразования, который происходит, когда фигура перемещается из одного места в другое на координатной плоскости без изменения ее размера, формы или ориентации… [ref]

с матричным преобразованием мы можем перемещать виджеты, не меняя ничего, кроме их положения. Мы можем доказать это, сравнив положение координат виджета. Если вам интересно, как получить координаты виджета, вы можете попробовать метод расширения globalPaintBound. Читайте мою статью об этом здесь:



Изготовление карт

Прежде чем применить виджет преобразования, давайте создадим исходную карту. (Смотрите картинку перед переводом).

Теперь мы хотим разместить иконку над картой. Мы будем использовать матричный перевод. Чтобы добавить перевод, есть 2 варианта.

  • Если родитель виджета не является виджетом Контейнер, вы можете обернуть его Transfrom виджетом. Например:
Transform(
  transform: Matrix4.translationValues(x, y, z),
  child: const SomeWidget(),
)
  • Если это Контейнер, то мы можем присвоить его непосредственно свойству преобразования:
Container(
  transform: Matrix4.translationValues(x, y, z),
  .....// other props
)

После добавления значения перевода в виджет значка теперь у нас есть сложенный значок на виджете карточки (Посмотрите на изображение после перевода).

См. виджет карты кодов здесь: https://gist.github.com/pmatatias/6076a587a0dc5f505bca991f0ecda190

Дополнительно: виджет неоморфизма

Мы закончили с формой виджета карты. Но если сравнить текущий результат, мы пропустили тень. Чтобы добавить эффект тени, мы можем вручную добавить его в свойство BoxDecoration из виджета ontainer. Мы также можем использовать метод расширения с именем addNeumorphism. Для получения более подробной информации вы можете прочитать мою статью, объясняющую больше об этом по этой ссылке:



  • Ручная тень для окна значка
Container(
  decoration: BoxDecoration(
      borderRadius: BorderRadius.circular(20),
      boxShadow: const [
        BoxShadow(
          offset: Offset(3, 3),
          blurRadius: 8,
          color: Color(0xFFffa726),
        ),
        BoxShadow(
          offset: Offset(-1, -1),
          blurRadius: 10,
          color: Colors.white,
        ),
....
  • Используйте неоморфизм метода расширения
    
@override
Widget build(BuildContext context) {
  return Container(
  ..../// rest of code
  ).addNeumorphism(
      bottomShadowColor: Colors.black26,
);

и теперь у нас есть лучшая карта с реализованным эффектом тени:

Отделка

Последний шаг, который нам нужно сделать, — это создать адаптивный макет виджета карты. Я также поделился статьей об адаптивных макетах во Flutter. Более подробно вы можете прочитать здесь:



Карточки будут отображаться с виджетом GridView.count(). Затем мы применяем адаптивный метод для определения значений crossAxisCount и childAspectRatio.

...
body: Padding(
  padding: const EdgeInsets.symmetric(horizontal: 18.0),
  child: GridView.count(
    crossAxisSpacing: 20,
    // mainAxisSpacing: 20,
    childAspectRatio: size.width /
        size.height /
        context.responsive(0.3,
            md: 0.6,
            lg: 0.59,
            xl: 1.2), // this ratio is hard code, you may need to change it base on your need
    crossAxisCount: context.responsive(1, md: 2, xl: 4),
    children: widge,
  ),
),
...

и, наконец, у нас есть виджет карты со сложенными значками, написанный в коде Flutter.

Полный код и демонстрацию можно найти на Dartpad:

https://dartpad.dev/?id=be8f84a08f9e2347f20be108ac030484

Разветвите репозиторий gist и пометьте его на GitHub по ссылке ниже: 😎😁 dashboard_card_responsive_flutter.dart (github.com)

🙏Спасибо, что читаете. Хлопайте 👏, если вам понравилась эта статья 😃 😄

Ссылка: