Карточки, иконки, кнопки и изображения
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.yaml—Image.assetне найдёт файл. - Не обработать загрузку и ошибку
Image.network— пользователь увидит пустоту или красный значок. - Путать
onPressedиonTap— у кнопок этоonPressed, уInkWell/GestureDetector—onTap.
Best practices
- Выбирайте тип кнопки по важности действия:
ElevatedButtonдля главного,TextButtonдля второстепенного. - Для сетевых картинок задавайте заглушку и обработку ошибки, а лучше используйте кэширование (
cached_network_image). - Берите иконки из набора
Icons— они векторные и масштабируются без потери качества.
Для интерактивности за пределами стандартных кнопок есть универсальные GestureDetector и InkWell. Первый ловит любые жесты — тап, долгое нажатие, свайп — на любом виджете, второй вдобавок рисует материальный эффект волны. Обернув в них хоть картинку, хоть карточку, вы делаете кликабельным что угодно. Это возвращает нас к принципу композиции: интерактивность — тоже виджет-обёртка, а не свойство.
Итог: готовые Material-виджеты ускоряют сборку интерфейса в разы. Карточки, кнопки с onPressed и асинхронные картинки — это повседневный инструментарий. Теперь, когда вёрстка освоена, перейдём к тому, как заставить интерфейс меняться.