Flutter. Примеры использования виджета InkWell, которых вы не видели

Flutter. Примеры использования виджета InkWell, которых вы не видели

FlutterPulse

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

InkWell — один из самых полезных виджетов Flutter. Он используется для добавления эффектов риппла и обработки взаимодействий с иначе неинтерактивными…

ВИДЖЕТ МЕСЯЦА

InkWell — один из самых полезных виджетов Flutter. Он используется для добавления эффектов риппла и обработки взаимодействий с иначе неинтерактивными виджетами.

Содержание

· Ключевые особенности InkWell
·
Кастомизированные виджеты с InkWell
∘∘∘
Кастомизированные кнопки
∘∘∘
Интерактивные карточки
∘∘∘
Интерактивные элементы списка
∘∘∘
Кнопки с изображениями
∘∘∘
IconButton с обработчиком onDoubleTap
·
InkWell vs InkResponse
·
InkWell vs Ink

Если вы являетесь участником, пожалуйста, продолжайте;иначе, прочитайте полную статью здесь.

Ключевые особенности InkWell

  • Создает эффекты всплеска в стиле Material Design при нажатии
  • Обрабатывает различные жесты (нажатие, двойное нажатие, долгий тап)
  • Работает внутри виджетов Material для отображения правильной визуальной обратной связи
  • Может быть настроен с разными цветами и формами всплеска

Кастомизированные виджеты с InkWell

Кастомизированные кнопки

Кастомизированные кнопки с уникальным дизайном, которые поддерживают правильную тактильную обратную связь.


class EngageButton extends StatelessWidget {
final IconData icon;
final String label;
final Color color;
final VoidCallback onTap;

const EngageButton({
super.key,
required this.icon,
required this.label,
required this.color,
required this.onTap,
});

@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Material(
color: color.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(16),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(16),
splashColor: color.withValues(alpha: 0.3),
highlightColor: color.withValues(alpha: 0.1),
child: Container(
padding: const EdgeInsets.all(12),
child: Icon(icon, color: color, size: 28),
),
),
),
const SizedBox(height: 4),
Text(
label,
style: TextStyle(color: color, fontWeight: FontWeight.bold),
),
],
);
}
}

Используйте их из представления:

            Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
EngageButton(
icon: Icons.favorite,
label: 'Like',
color: Colors.red,
onTap: () =>(){},
),
EngageButton(
icon: Icons.share,
label: 'Share',
color: Colors.blue,
onTap: () => (){},
),
EngageButton(
icon: Icons.comment,
label: 'Comment',
color: Colors.green,
onTap: () =>(){},
),
],
),

Интерактивные карточки

Нажимаемые виджеты карточек с визуальной обратной связью


class InteractiveCard extends StatelessWidget {
final String title;
final String description;
final IconData icon;
final VoidCallback onTap;

const InteractiveCard({
super.key,
required this.title,
required this.description,
required this.icon,
required this.onTap,
});

@override
Widget build(BuildContext context) {
return Material(
color: Colors.white,
elevation: 2,
borderRadius: BorderRadius.circular(8),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.blue.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(icon, color: Colors.blue, size: 24),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
const SizedBox(height: 4),
Text(
description,
style: TextStyle(
color: Colors.grey[600],
fontSize: 14,
),
),
],
),
),
const Icon(Icons.chevron_right),
],
),
),
),
);
}
}

Используйте из представления:

           InteractiveCard(
title: 'Утренний ритуал',
description: 'Простые шаги, чтобы правильно начать день',
icon: Icons.wb_sunny,
onTap: () =>(){},
),
const SizedBox(height: 16),
InteractiveCard(
title: 'Вечернее размышление',
description: 'Оцените свой день и планируйте завтрашний',
icon: Icons.nightlight_round,
onTap: () => (){},
),

Интерактивные элементы списка

Список с пользовательскими макетами, но стандартными шаблонами взаимодействия.


class InteractiveListItem extends StatelessWidget {
final Widget leading;
final String title;
final String subtitle;
final Widget trailing;
final VoidCallback onTap;

const InteractiveListItem({
super.key,
required this.leading,
required this.title,
required this.subtitle,
required this.trailing,
required this.onTap,
});

@override
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
child: InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 8.0),
child: Row(
children: [
leading,
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
const SizedBox(height: 4),
Text(
subtitle,
style: TextStyle(
color: Colors.grey[600],
fontSize: 14,
),
),
],
),
),
trailing,
],
),
),
),
);
}
}

Используйте из представления:

            InteractiveListItem(
leading: const CircleAvatar(
backgroundColor: Colors.purple,
child: Icon(Icons.person, color: Colors.white),
),
title: 'Артем Новиков',
subtitle: 'Дизайнер UX',
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
onTap: () =>(){},
),
const Divider(),
InteractiveListItem(
leading: const CircleAvatar(
backgroundColor: Colors.orange,
child: Icon(Icons.person, color: Colors.white),
),
title: 'Юрий Новиков',
subtitle: 'Разработчик ПО',
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
onTap: (){},
),

Кнопки с изображениями

Изображения с эффектом рябления и обработчиком нажатия.


class ImageButton extends StatelessWidget {
final ImageProvider imageProvider;
final String label;
final VoidCallback onTap;

const ImageButton({
super.key,
required this.imageProvider,
required this.label,
required this.onTap,
});

@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Material(
elevation: 4,
borderRadius: BorderRadius.circular(12),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Ink.image(
image: imageProvider,
width: 120,
height: 120,
fit: BoxFit.cover,
child: Align(
alignment: Alignment.bottomCenter,
child: Container(
width: double.infinity,
color: Colors.black.withValues(alpha: 0.6),
padding: const EdgeInsets.symmetric(vertical: 8),
child: Text(
label,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
),
),
),
],
);
}
}

Используйте их из представления:

                ImageButton(
imageProvider: const NetworkImage(
'https://images.unsplash.com/photo-1472396961693-142e6e269027?w=400&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8NXx8bmF0dXJlfGVufDB8fDB8fHwy'
),
label: 'Природа',
onTap: (){},
),
ImageButton(
imageProvider: const NetworkImage(
'https://images.unsplash.com/photo-1575550959106-5a7defe28b56?q=80&w=1470&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D'
),
label: 'Дикая природа',
onTap: (){},
),

Кнопка с иконкой и обработчиком двойного нажатия

Кнопка для воспроизведения аудио, которая воспроизводит с нормальной скоростью onTap, быстрой onDoubleTapи медленной onLongPress.


class AudioIconButton extends StatelessWidget {
final bool isPlaying;
final VoidCallback onTap;
final VoidCallback onDoubleTap;
final VoidCallback onLongPress;
final double size;
final Color color;

const AudioIconButton({
super.key,
required this.isPlaying,
required this.onTap,
required this.onDoubleTap,
required this.onLongPress,
this.size = 60.0,
this.color = Colors.purple,
});

@override
Widget build(BuildContext context) {
return SizedBox(
width: size,
height: size,
child: Material(
borderRadius: BorderRadius.circular(16),
color: color.withValues(alpha: 0.1),
child: InkWell(
onTap: onTap,
onDoubleTap: onDoubleTap,
onLongPress: onLongPress,
borderRadius: BorderRadius.circular(16),
splashColor: color.withValues(alpha: 0.3),
highlightColor: color.withValues(alpha: 0.1),


child: Container(
padding: const EdgeInsets.all(12),
child: Center(
child: Icon(
isPlaying ? Icons.pause : Icons.play_arrow,
color: color,
size: 28,
),
),
),
),
),
);
}
}

Представление:

           GetBuilder<AudioController>(
id: 'play',
builder: (controller) {
return AudioIconButton(
color: Colors.blue,
isPlaying: controller.isPlaying,
onTap: () {
controller.handleTap();
},
onDoubleTap: () {
controller.handleDoubleTap();
},
onLongPress: () {
controller.handleLongPress();
});
}
),

Контроллер:

 bool isPlaying = false;

void handleTap() {
isPlaying = !isPlaying;
var message = 'Playing is stopped';
if (isPlaying) {
message = 'Playing with normal speed';
}
Get.showSnackbar(GetSnackBar(
message: message,
duration: Duration(seconds: 1),
));

update(['play']);
}

void handleDoubleTap() {
isPlaying = !isPlaying;
var message = 'Playing is stopped';
if (isPlaying) {
message = 'Playing fast';
}
Get.showSnackbar(GetSnackBar(
message: message,
duration: Duration(seconds: 1),
));
update(['play']);
}

void handleLongPress() {
isPlaying = !isPlaying;
var message = 'Playing is stopped';
if (isPlaying) {
message = 'Playing slowly';
}
Get.showSnackbar(GetSnackBar(
message: message,
duration: Duration(seconds: 1),
));
update(['play']);
}

InkWell против InkResponse

InkWell и InkResponse очень похожи. Оба реализуют InteractiveInkFeature и находятся в библиотеке ink_well.dart.

Разница в том, что InkResponse позволяет больше настроить форму выделения, в то время как InkWell всегда создает прямоугольную.

Все приведенные выше примеры можно переписать с использованием InkResponse.

InkWell против Ink

Эти два класса служат совершенно разным целям, но часто используются вместе.

В то время как InkWell позволяет взаимодействовать и предоставляет визуальную обратную связь в виде всплеска и выделения, Ink позволяет рисовать виджеты на подлежащем Material, на котором всплеск и выделение, предоставленные InkWell были бы видны.

Мы уже использовали Ink для наших ImageButtons.

        Material(
elevation: 4,
borderRadius: BorderRadius.circular(12),
child: Ink.image(
image: imageProvider,
width: 120,
height: 120,
fit: BoxFit.cover,
child: InkWell(

Спасибо за чтение!

Report Page