← Назад к вопросам
Как получить положение объекта на экране?
2.0 Middle🔥 111 комментариев
#Flutter виджеты#Анимации
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как получить положение объекта на экране?
Получение позиции виджета на экране — важная задача для реализации анимаций, drag-and-drop, overlay и других интерактивных элементов.
Способ 1: GlobalKey и RenderBox
Самый распространённый и надёжный способ:
import 'package:flutter/material.dart';
class PositionExample extends StatefulWidget {
@override
State<PositionExample> createState() => _PositionExampleState();
}
class _PositionExampleState extends State<PositionExample> {
final GlobalKey<State> _widgetKey = GlobalKey();
Offset? _widgetPosition;
Size? _widgetSize;
void _getPosition() {
final RenderBox renderBox =
_widgetKey.currentContext!.findRenderObject() as RenderBox;
final offset = renderBox.localToGlobal(Offset.zero);
final size = renderBox.size;
setState(() {
_widgetPosition = offset;
_widgetSize = size;
});
print('Position: ${offset.dx}, ${offset.dy}');
print('Size: ${size.width}x${size.height}');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Get Position')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
key: _widgetKey,
width: 200,
height: 100,
color: Colors.blue,
child: Center(child: Text('Touch me')),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _getPosition,
child: Text('Get Position'),
),
if (_widgetPosition != null) ...
[
SizedBox(height: 20),
Text('X: ${_widgetPosition!.dx.toStringAsFixed(2)}'),
Text('Y: ${_widgetPosition!.dy.toStringAsFixed(2)}'),
Text('Width: ${_widgetSize!.width.toStringAsFixed(2)}'),
Text('Height: ${_widgetSize!.height.toStringAsFixed(2)}'),
]
],
),
),
);
}
}
Способ 2: GestureDetector для touch позиции
Получение позиции клика/touch:
class TouchPositionExample extends StatefulWidget {
@override
State<TouchPositionExample> createState() => _TouchPositionExampleState();
}
class _TouchPositionExampleState extends State<TouchPositionExample> {
Offset? _touchPosition;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Touch Position')),
body: GestureDetector(
onTapDown: (TapDownDetails details) {
// Позиция относительно экрана
final globalPosition = details.globalPosition;
// Позиция относительно виджета
final localPosition = details.localPosition;
setState(() {
_touchPosition = globalPosition;
});
print('Global: ${globalPosition.dx}, ${globalPosition.dy}');
print('Local: ${localPosition.dx}, ${localPosition.dy}');
},
child: Container(
color: Colors.grey[300],
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Tap anywhere'),
if (_touchPosition != null) ...
[
SizedBox(height: 20),
Text('X: ${_touchPosition!.dx.toStringAsFixed(0)}'),
Text('Y: ${_touchPosition!.dy.toStringAsFixed(0)}'),
]
],
),
),
),
),
);
}
}
Способ 3: Mouse position на web/desktop
class MousePositionExample extends StatefulWidget {
@override
State<MousePositionExample> createState() => _MousePositionExampleState();
}
class _MousePositionExampleState extends State<MousePositionExample> {
Offset? _mousePosition;
@override
Widget build(BuildContext context) {
return Scaffold(
body: MouseRegion(
onHover: (PointerHoverEvent event) {
setState(() {
_mousePosition = event.position; // Глобальная позиция
});
},
child: Container(
color: Colors.grey[300],
child: Center(
child: _mousePosition == null
? Text('Move mouse')
: Text(
'Mouse: ${_mousePosition!.dx.toStringAsFixed(0)}, '
'${_mousePosition!.dy.toStringAsFixed(0)}'
),
),
),
),
);
}
}
Способ 4: Получение позиции элемента в списке
class ListItemPositionExample extends StatefulWidget {
@override
State<ListItemPositionExample> createState() =>
_ListItemPositionExampleState();
}
class _ListItemPositionExampleState extends State<ListItemPositionExample> {
final itemKeys = List.generate(10, (_) => GlobalKey());
void _getListItemPosition(int index) {
final RenderBox renderBox =
itemKeys[index].currentContext!.findRenderObject() as RenderBox;
final offset = renderBox.localToGlobal(Offset.zero);
print('Item $index position: ${offset.dx}, ${offset.dy}');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('List Item Position')),
body: ListView.builder(
itemCount: 10,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () => _getListItemPosition(index),
child: Container(
key: itemKeys[index],
padding: EdgeInsets.all(16),
color: Colors.blue[100 * ((index % 4) + 1)],
child: Text('Item $index - Tap to get position'),
),
);
},
),
);
}
}
Способ 5: Получение относительной позиции (localToGlobal)
class RelativePositionExample extends StatefulWidget {
@override
State<RelativePositionExample> createState() =>
_RelativePositionExampleState();
}
class _RelativePositionExampleState extends State<RelativePositionExample> {
final GlobalKey _containerKey = GlobalKey();
final GlobalKey _childKey = GlobalKey();
void _calculateRelativePosition() {
final RenderBox containerRender =
_containerKey.currentContext!.findRenderObject() as RenderBox;
final RenderBox childRender =
_childKey.currentContext!.findRenderObject() as RenderBox;
// Глобальная позиция контейнера
final containerGlobalPosition =
containerRender.localToGlobal(Offset.zero);
// Глобальная позиция дочернего элемента
final childGlobalPosition = childRender.localToGlobal(Offset.zero);
// Позиция дочернего элемента относительно контейнера
final relativePosition = childGlobalPosition - containerGlobalPosition;
print('Child position relative to container: '
'${relativePosition.dx}, ${relativePosition.dy}');
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
key: _containerKey,
width: 300,
height: 300,
color: Colors.grey[300],
padding: EdgeInsets.all(20),
child: Stack(
children: [
Positioned(
left: 50,
top: 100,
child: Container(
key: _childKey,
width: 100,
height: 100,
color: Colors.red,
),
),
Positioned(
bottom: 10,
left: 10,
child: ElevatedButton(
onPressed: _calculateRelativePosition,
child: Text('Calculate Position'),
),
),
],
),
),
),
);
}
}
Способ 6: Для анимаций (Animate from top-left corner)
class AnimatedPositionExample extends StatefulWidget {
@override
State<AnimatedPositionExample> createState() =>
_AnimatedPositionExampleState();
}
class _AnimatedPositionExampleState extends State<AnimatedPositionExample> {
final GlobalKey _widgetKey = GlobalKey();
Offset? _startPosition;
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
);
WidgetsBinding.instance.addPostFrameCallback((_) {
_capturePosition();
});
}
void _capturePosition() {
final RenderBox renderBox =
_widgetKey.currentContext!.findRenderObject() as RenderBox;
setState(() {
_startPosition = renderBox.localToGlobal(Offset.zero);
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
key: _widgetKey,
width: 100,
height: 100,
color: Colors.blue,
child: Center(child: Text('Animate')),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () => _controller.forward(from: 0.0),
child: Text('Start Animation'),
),
if (_startPosition != null)
Text('Start: ${_startPosition!.dx}, ${_startPosition!.dy}'),
],
),
),
);
}
}
Практическая таблица методов
Сценарий | Метод
--------------------------------|-------------------------------------------
Позиция виджета на экране | RenderBox.localToGlobal(Offset.zero)
Позиция клика/касания | TapDownDetails.globalPosition
Позиция в координатах виджета | TapDownDetails.localPosition
Позиция мыши (web/desktop) | PointerHoverEvent.position
Размер виджета | RenderBox.size
Высота элемента в списке | renderBox.localToGlobal() for each item
Относительная позиция | childPosition - parentPosition
Лучшие практики
// ✅ Используйте Post-frame callback для инициализации
WidgetsBinding.instance.addPostFrameCallback((_) {
_getPosition(); // После первого кадра
});
// ✅ Кэшируйте позиции если часто используются
late Offset _cachedPosition;
// ❌ Не вызывайте во время сборки
@override
Widget build(BuildContext context) {
// _getPosition(); // ❌ Ошибка!
}
// ✅ Для сложных случаев создавайте helper функции
RenderBox? _getRenderBox(GlobalKey key) {
try {
return key.currentContext?.findRenderObject() as RenderBox?;
} catch (e) {
return null;
}
}
Получение позиции виджета — мощный инструмент для создания интерактивных интерфейсов.