Карточки, иконки, кнопки и изображения

Flutter поставляет богатый набор готовых Material-виджетов: карточки, иконки, кнопки и изображения.

Суть: Card — приподнятая панель с тенью, Icon — векторные значки, кнопки (ElevatedButton, TextButton, OutlinedButton) реагируют на нажатия через onPressed, а Image показывает картинки из сети или ассетов.

Кнопки в Material 3 получили обновлённый набор: помимо привычных, появилась FilledButton — заполненная кнопка средней выразительности между яркой ElevatedButton и скромной TextButton. Выбор типа кнопки — это не вкусовщина, а часть визуальной иерархии: пользователь по внешнему виду должен мгновенно понимать, какое действие на экране главное. Одна заметная кнопка действия на экран — хорошее правило.

Не нужно рисовать кнопки и тени вручную — Flutter даёт готовые виджеты по Material Design. Card создаёт визуально приподнятую карточку. Icon берёт значки из встроенного набора Icons. Кнопки различаются по визуальному весу: ElevatedButton — главное действие, OutlinedButton — второстепенное, TextButton — наименее заметное.

Card(
  elevation: 4,
  child: Padding(
    padding: const EdgeInsets.all(16),
    child: Column(
      children: [
        const Icon(Icons.favorite, color: Colors.red, size: 40),
        const SizedBox(height: 8),
        const Text('Любимое'),
        ElevatedButton(
          onPressed: () => print('Нажато'),
          child: const Text('Открыть'),
        ),
      ],
    ),
  ),
)

Image.network('https://example.com/cat.png')   // картинка из сети
Image.asset('assets/logo.png')                  // из ассетов проекта

Свойство onPressed принимает функцию, которая вызовется при нажатии. Если передать null, кнопка станет неактивной (серой) — это удобный способ блокировать действие.

Как работают кнопки и картинки под капотом

Все кнопки внутри используют InkWell — он рисует эффект «чернильной волны» при нажатии и ловит касание. onPressed: null отключает кнопку. Image.network загружает картинку асинхронно: пока идёт загрузка, можно показать заглушку через loadingBuilder, а при ошибке — errorBuilder. Image.asset берёт файл, заранее объявленный в pubspec.yaml.

  Кнопка
  +---------------------------+
  | InkWell (ловит касание)   |
  |   эффект волны при нажатии |
  |        |                   |
  |        v onPressed()       |  <- ваша функция-обработчик
  |   child: Text('Открыть')  |
  +---------------------------+

  onPressed: null  ->  кнопка неактивна (серая)
  onPressed: () {} ->  активна, реагирует на тап
# Модель состояния кнопки: активна или нет в зависимости от onPressed
def button(label, on_pressed):
    if on_pressed is None:
        return f'[ {label} ] (неактивна)'
    return f'[ {label} ] -> нажатие вызовет обработчик'

def handle():
    print('Нажато!')

print(button('Открыть', handle))
print(button('Недоступно', None))

# Имитация загрузки картинки из сети (асинхронной)
def load_image(url):
    print('показываем заглушку...')
    print(f'загрузили: {url}')

load_image('https://example.com/cat.png')

Частые ошибки

  • Забыть объявить ассет в pubspec.yamlImage.asset не найдёт файл.
  • Не обработать загрузку и ошибку Image.network — пользователь увидит пустоту или красный значок.
  • Путать onPressed и onTap — у кнопок это onPressed, у InkWell/GestureDetectoronTap.

Best practices

  • Выбирайте тип кнопки по важности действия: ElevatedButton для главного, TextButton для второстепенного.
  • Для сетевых картинок задавайте заглушку и обработку ошибки, а лучше используйте кэширование (cached_network_image).
  • Берите иконки из набора Icons — они векторные и масштабируются без потери качества.

Для интерактивности за пределами стандартных кнопок есть универсальные GestureDetector и InkWell. Первый ловит любые жесты — тап, долгое нажатие, свайп — на любом виджете, второй вдобавок рисует материальный эффект волны. Обернув в них хоть картинку, хоть карточку, вы делаете кликабельным что угодно. Это возвращает нас к принципу композиции: интерактивность — тоже виджет-обёртка, а не свойство.

Итог: готовые Material-виджеты ускоряют сборку интерфейса в разы. Карточки, кнопки с onPressed и асинхронные картинки — это повседневный инструментарий. Теперь, когда вёрстка освоена, перейдём к тому, как заставить интерфейс меняться.

Проверьте себя
1. Что произойдёт, если передать кнопке onPressed: null?
AКнопка нажмётся сама
BКнопка станет неактивной (серой)
CПроизойдёт ошибка
DКнопка исчезнет
2. Какой виджет используют для приподнятой панели с тенью в Material Design?
AContainer
BCard
CRow
DPadding