Предположим, у меня на экране 3 Container
с, которые реагируют на прикосновения, меняя цвет. Когда палец пользователя на них, они должны изменить цвет, а при прикосновении к ним они должны вернуться в нормальное состояние. Я хочу, чтобы эти контейнеры реагировали даже тогда, когда касание начинается в произвольной области на экране, вне контейнеров. Так что в основном то, что я ищу, это нечто вроде css hover.
Если я оберну каждый контейнер отдельно GestureDetector
, они не будут реагировать на события касания, которые начинаются вне их. По другому вопросу (к сожалению, у меня нет ссылки сейчас) предлагается обернуть все контейнеры одним GestureDetector
и назначить GlobalKey
каждому, чтобы отличать их друг от друга.
Вот плата , которая обнаруживает жесты прикосновения:
class MyBoard extends StatefulWidget {
static final keys = List<GlobalKey>.generate(3, (ndx) => GlobalKey());
...
}
class _MyBoardState extends State<MyBoard> {
...
/// I control the number of buttons by the number of keys,
/// since all the buttons should have a key
List<Widget> makeButtons() {
return MyBoard.keys.map((key) {
// preapre some funcitonality here
var someFunctionality;
return MyButton(key, someFunctionality);
}).toList();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTapDown: (tapDownDetails) {
handleTouch(context, tapDownDetails.globalPosition);
},
onTapUp: (tapUpDetails) {
finishTouch(context);
},
onPanUpdate: (dragUpdateDetails) {
handleTouch(context, dragUpdateDetails.globalPosition);
},
onPanEnd: (panEndDetails) {
finishTouch(context);
},
onPanCancel: () {
finishTouch(context);
},
child: Container(
color: Colors.green,
width: 300.0,
height: 600.0,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: makeButtons(),
),
),
);
}
Вот простая MyButton
:
class MyButton extends StatelessWidget {
final GlobalKey key;
final Function someFunctionality;
MyButton(this.key, this.someFunctionality) : super(key: key);
@override
Widget build(BuildContext context) {
return Consumer<KeyState>(
builder: (buildContext, keyState, child) {
return Container(
width: 100.0,
height: 40.0,
color: keyState.touchedKey == this.key ? Colors.red : Colors.white,
);
},
);
}
}
В _MyBoardState
Вот как я работаю, обнаруживая, к какому MyButton
прикоснулись:
MyButton getTouchingButton(Offset globalPosition) {
MyButton currentButton;
// result is outside of [isTouchingMyButtonKey] for performance
final result = BoxHitTestResult();
bool isTouchingButtonKey(GlobalKey key) {
RenderBox renderBox = key.currentContext.findRenderObject();
Offset offset = renderBox.globalToLocal(globalPosition);
return renderBox.hitTest(result, position: offset);
}
var key = MyBoard.keys.firstWhere(isTouchingButtonKey, orElse: () => null);
if (key != null) currentButton = key.currentWidget;
return currentButton;
}
Я использую пакет провайдера и вместо перекомпоновки всего MyBoard
с setState
только соответствующей частью связанной MyButton
будет восстановлен. Тем не менее, каждая кнопка имеет прослушиватель, который перестраивается при каждом обновлении, и я использую GlobalKey
. На флаттерной документации написано:
Глобальные ключи относительно дороги. Если вам не нужны какие-либо из перечисленных выше функций, попробуйте вместо этого использовать Key, ValueKey, ObjectKey или UniqueKey.
Однако если мы рассмотрим метод getTouchingButton
, мне понадобится GlobalKey
дляполучить renderBox
объект и получить currentWidget
. Я также должен сделать hitTest для каждого GlobalKey
. Это вычисление повторяется при срабатывании onPanUpdate
, что означает, что палец пользователя перемещает пиксель.
Пользовательский интерфейс не обновляется достаточно быстро. Одним касанием (tapDown и tapUp на обычной скорости) вы обычно не видите никаких изменений на MyButton
s.
Если мне нужно подвести итог моего вопроса, Как я могу обнаружить один и тот жеприкасаться (не поднимать) от разных виджетов, когда на них более эффективно и элегантно наведен палец?