Как с помощью виджета Positioned выровнять дочерний виджет по центру
Как с помощью виджета Positioned выровнять дочерний виджет по центру, отняв высоту кнопок и паддинга сверху и снизу кнопок?
Синий квадрат выровнян ВООБЩЕ по центру.
MediaQuery.of(context).size.height / 2 - высота квадрата
Positioned(
// (MediaQuery.of(context).size.height / 2 - 100.0) -> по центру вообще
top: (MediaQuery.of(context).size.height / 2 - 100.0),
left: _animation.value,
//left: 0.0,
child: Container(
width: 100.0,
height: 100.0,
color: Colors.indigoAccent,
),
),
Красный квадрат выровнян ВООБЩЕ по центру с помощью виджета Align. Обратите внимание, он чуть ниже синего квадрата. Я его сделал чисто для сравнения.
alignment: Alignment.center,
Align(
alignment: Alignment.center,
child: Container(
width: 100.0,
height: 100.0,
color: Colors.pink,
),
),
Зеленый квадрат я пытаюсь выровнять НЕ ВООБЩЕ по центру, а поцентру белого фона, отняв контейнер с желтым фоном. Выровняв квадрат по центру, я прибавил к нему отступы(8.0+8.0) и высоту кнопок (36.0), ное все равно, точно не получается. Расстояние белого фона сверху зеленого квадрата, чуть больше расстояния белого фона снизу зеленого квадрата, я даже линейкой померял.
top: (MediaQuery.of(context).size.height / 2 - 100.0) + 8.0 + 36.0 + 8.0,
Positioned(
// (MediaQuery.of(context).size.height / 2 - 100.0) -> по центру вообще
// 8.0 -> padding сверху кнопки
// 36.0 -> высота кнопки
// 8.0 -> padding снизу кнопки
top: (MediaQuery.of(context).size.height / 2 - 100.0) + 8.0 + 36.0 + 8.0,
right: 0.0,
child: Container(
width: 100.0,
height: 100.0,
color: Colors.green,
),
),
Может, я как-то в расчетах ошибся, выравнивая зеленый квадрат? Далее фото и полный код. Спасибо.
main.dart
import 'dart:ui';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Name App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('Name Page'),
),
body: MyHomePage(),
),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Tween _tween;
Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 3),
)..addListener(() {
setState(() {
});
});
_tween = Tween<double>(begin: 0.0, end: window.physicalSize.width / 2 - 100.0);
_animation = _tween.animate(_controller);
}
@override
void dispose() {
super.dispose();
_controller.dispose();
}
void _funStart() {
setState(() {
_controller.forward();
});
}
void _funStop() {
setState(() {
_controller.stop();
});
}
void _funBack() {
setState(() {
_controller.reverse();
});
}
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Align(
alignment: Alignment.topLeft,
child: Container(
padding: const EdgeInsets.all(8.0),
color: Colors.orangeAccent,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
flex: 1,
child: RaisedButton(
onPressed: () { _funStart(); },
splashColor: Colors.blue.withOpacity(0.5),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)
),
child: Text(
'start'.toUpperCase(),
style: TextStyle(
color: Colors.deepPurple,
fontSize: 16.0,
),
),
),
),
SizedBox(width: 8.0,),
Expanded(
flex: 1,
child: RaisedButton(
onPressed: () { _funStop(); },
splashColor: Colors.blue.withOpacity(0.5),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)
),
child: Text(
'stop'.toUpperCase(),
style: TextStyle(
color: Colors.deepPurple,
fontSize: 16.0,
),
),
),
),
SizedBox(width: 8.0,),
Expanded(
flex: 1,
child: RaisedButton(
onPressed: () { _funBack(); },
splashColor: Colors.blue.withOpacity(0.5),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)
),
child: Text(
'back'.toUpperCase(),
style: TextStyle(
color: Colors.deepPurple,
fontSize: 16.0,
),
),
),
),
],
),
),
),
Positioned(
// (MediaQuery.of(context).size.height / 2 - 100.0) -> по центру вообще
top: (MediaQuery.of(context).size.height / 2 - 100.0),
left: _animation.value,
//left: 0.0,
child: Container(
width: 100.0,
height: 100.0,
color: Colors.indigoAccent,
),
),
Align(
alignment: Alignment.center,
child: Container(
width: 100.0,
height: 100.0,
color: Colors.pink,
),
),
Positioned(
// (MediaQuery.of(context).size.height / 2 - 100.0) -> по центру вообще
// 8.0 -> padding сверху кнопки
// 36.0 -> высота кнопки
// 8.0 -> padding снизу кнопки
top: (MediaQuery.of(context).size.height / 2 - 100.0) + 8.0 + 36.0 + 8.0,
right: 0.0,
child: Container(
width: 100.0,
height: 100.0,
color: Colors.green,
),
),
],
);
}
}
Ответы (1 шт):
Да, расчёты у Вас неправильные и с синим, и с зеленым прямоугольником.
MediaQuery.of(context).size - это не про размер родительского виджета, здесь он не подойдёт.
Вам нужен LayoutBuilder, пример по коду из Вашего вопроса:
import 'package:flutter/material.dart';
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
// MARK: - Для наглядных расчётов
final containerHeight = 100.0;
// MARK: - Вот здесь главный момент:
return LayoutBuilder(
builder: (context, BoxConstraints constraints) {
return Stack(
children: <Widget>[
Positioned(
// MARK: - По центру родительского виджета - это:
// 1/2 высоты родительского - 1/2 высоты виджета
top: (constraints.maxHeight - containerHeight) / 2,
left: 0.0,
child: Container(
width: 100.0,
height: containerHeight,
color: Colors.indigoAccent,
),
),
Align(
alignment: Alignment.center,
child: Container(
width: 100.0,
height: 100.0,
color: Colors.pink,
),
),
Positioned(
top: (MediaQuery.of(context).size.height / 2 - 50.0) +
8.0 +
36.0 +
8.0,
right: 0.0,
child: Container(
width: 100.0,
height: 100.0,
color: Colors.green,
),
),
],
);
},
);
}
}
// MARK: - Второстепенное для ответа
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Name App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('Name Page'),
),
body: MyHomePage(),
),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
Результат:
P.S. Никогда не используйте какие-то захардкоженые цифры, как в 3 варианте. Добавится какой-нибудь новый отступ и все рассыпется. К примеру, Safe Area.
UPD. Если центрировать именно по белой части, а не Stack, то вариант первый - добавить в вычисления высоту строки с кнопками. Вариант второй - разделить их в Column:
return Column(
children: [
Container(
padding: const EdgeInsets.all(8.0),
... // MARK: - Контейнер с кнопками
),
// MARK: - Занимаем все оставшееся место, т.е. белый фон из примера
// и квадрат отцентрируется как надо без дополнительных расчётов.
Expanded(
child: LayoutBuilder(
builder: (context, BoxConstraints constraints) {
return Stack(
children: <Widget>[
Positioned(
// MARK: - По центру родительского виджета - это:
// 1/2 высоты родительского - 1/2 высоты виджета
top: (constraints.maxHeight - containerHeight) / 2,
Результат:


