Irbisa · Шпаргалка
Жесты и ввод указателя
Арена жестов, hit-test, drag и scale, свои распознаватели, клавиатура, hover, drag-and-drop
Чем отличаются
GestureDetector,InkWellиListener, и когда что использовать?Чем отличаются
IgnorePointerиAbsorbPointer?// A decorative layer on top: taps fall through to the list below.Stack( children: [ const ProductList(), IgnorePointer(child: ConfettiLayer(controller: _confetti)), ],…Как сделать свайп для удаления через
Dismissible, и почему егоkeyдолжен браться из элемента, а не из индекса?ListView.builder( itemCount: tasks.length, itemBuilder: (context, i) { final task = tasks[i]; return Dismissible( key: ValueKey(task.id), // identity, not position…Как арена жестов во Flutter решает, какой жест победил, и почему
onTapсрабатывает только когда палец отрывается?Что делают
HitTestBehavior.deferToChild,opaqueиtranslucent, и почему теряются тапы по пустому месту внутриGestureDetector?// Dead zone: the gap between the icon and the text paints nothing.GestureDetector( onTap: _open, // behavior: deferToChild by default child: const Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [Icon(Icons.folder), Text('Reports')],…Что выведет этот код, когда пользователь один раз тапнет по звезде, и что выведет, когда тапнет по отступу вокруг неё?
GestureDetector( onTap: () => print('card'), onDoubleTap: () => print('card: double'), child: Padding( padding: const EdgeInsets.all(24), child: GestureDetector( onTap: () => print('star'), child: const Icon(Icons.star), ), ),)Поверх карточки лежит оверлей с
HitTestBehavior.translucent, чтобы на тап реагировали оба. Что выведет код при тапе по карточке и почему?Stack( children: [ GestureDetector( onTap: () => print('card'), child: const ColoredBox(color: Colors.white, child: SizedBox.expand()), ), Positioned.fill( child: GestureDetector( behavior: HitTestBehavior.translucent, onTap: () => print('overlay'), ), ), ],)Найдите баг: leak_tracker сообщает о неосвобождённом
TapGestureRecognizerв этой ссылке на условия использования, и их число растёт с каждой перестройкой.class TermsNotice extends StatelessWidget { const TermsNotice({super.key, required this.onOpenTerms}); final VoidCallback onOpenTerms; @override Widget build(BuildContext context) => Text.rich( TextSpan(text: 'By continuing you accept the ', children: [ TextSpan( text: 'Terms of Service', style: const TextStyle(decoration: TextDecoration.underline), recognizer: TapGestureRecognizer()..onTap = onOpenTerms, ), ]), );}Вы добавили
onDoubleTapстроке списка, у которой уже былonTap, и весь экран стал казаться тормозным: чего стоила эта одна строка?Строки со свайпом в архив лежат в вертикальном
ListView, и диагональный свайп то прокручивает список, то двигает строку: что это решает?// Pan needs 36 px in any direction before it claims, so the row starts late and you also get dy.GestureDetector( onPanUpdate: (d) => setState(() => _dx += d.delta.dx), child: const MessageRow(),);…Этот код pinch-to-zoom падает в debug, а после исправления масштаб через пару кадров улетает. Что с ним не так?
GestureDetector( onPanUpdate: (d) => setState(() => _offset += d.delta), onScaleUpdate: (d) => setState(() => _scale *= d.scale), child: Transform.translate( offset: _offset, child: Transform.scale(scale: _scale, child: const MapSurface()), ),)Как сделать drag-and-drop через
DraggableиDragTarget, и почему перетаскиваемая копия часто рисуется без стилей?LongPressDraggable<Task>( // waits, so the ListView keeps its scroll data: task, feedback: Material( // the Overlay has no Material above it elevation: 8, borderRadius: BorderRadius.circular(12), child: SizedBox(width: 260, child: TaskCard(task: task)),…Найдите баг: стикер в начале перетаскивания прыгает вниз и вбок, а потом едет за пальцем со смещением.
class _StickerBoardState extends State<StickerBoard> { Offset _pos = const Offset(40, 40); @override Widget build(BuildContext context) => Stack( children: [ Positioned( left: _pos.dx, top: _pos.dy, child: GestureDetector( onPanUpdate: (d) => setState(() => _pos = d.globalPosition), child: const StickerCard(), ), ), ], );}Свайп для удаления срабатывает на резкий взмах, но не когда пользователь протащил далеко, замер и отпустил: что сообщает
DragEndDetails.velocity?// Inside the State of a swipe row; _c is an AnimationController (0..1 = row travel).static const double _width = 320;static const _spring = SpringDescription(mass: 1, stiffness: 500, damping: 30); void _onUpdate(DragUpdateDetails d) => _c.value -= d.primaryDelta! / _width;…Как добавить зум по двойному тапу в
InteractiveViewer, и почему изображение прыгает, если пользователь делает pinch во время анимации зума?class _ZoomableState extends State<Zoomable> with SingleTickerProviderStateMixin { final _transform = TransformationController(); late final _anim = AnimationController( vsync: this, duration: const Duration(milliseconds: 250), )..addListener(() => _transform.value = _zoom!.value);…Как добавить горячие клавиши через
Shortcuts,ActionsиFocus, и почему Ctrl+S иногда вообще ничего не делает?class SaveIntent extends Intent { const SaveIntent();} Widget buildEditor() { return Shortcuts(…Кнопка в углу карточки не реагирует: ни ряби, ни колбэка, ничего в логах. Как выяснить, куда на самом деле уходит тап?
import 'package:flutter/gestures.dart';import 'package:flutter/rendering.dart'; void main() { debugPaintPointersEnabled = true; // highlights every box the hit test reaches debugPrintHitTestResults = true; // prints the hit path, deepest first…Что даёт
RawGestureDetectorпо сравнению сGestureDetector, и как написать свой распознаватель, например драг только двумя пальцами?class TwoFingerDragRecognizer extends OneSequenceGestureRecognizer { ValueChanged<Offset>? onDrag; final Set<int> _pointers = {}; @override void addAllowedPointer(PointerDownEvent event) {…Как колесо мыши, тачпад и стилус доходят до холста во Flutter, и чего лишается на десктопе холст, построенный только на сенсорных жестах?
Listener( onPointerSignal: (event) { if (event is! PointerScrollEvent) return; // Claim the tick, or a scroll view behind the canvas scrolls on it too. GestureBinding.instance.pointerSignalResolver.register(event, (e) { final dy = (e as PointerScrollEvent).scrollDelta.dy;…С включённым VoiceOver или TalkBack ваш регулятор яркости, который задаётся драгом, вообще не двигается. Что происходит с вводом указателя, когда работает скринридер?
// Pointer-only: with a screen reader on, the user's swipes never arrive here.Listener( onPointerMove: (e) => _setBrightness(e.localPosition.dx / _width), child: const BrightnessBar(),);…