Flutter:如何在左侧添加一个小部件,在一行的中心添加另一个小部件
Flutter: How to add a widget on the left, and another one in the centre of a row
我想将一个文本小部件对齐到一行的中心,我还想在同一行的左侧放置一个按钮小部件。文本小部件被展开的小部件包围,因此它占据了按钮留下的剩余 space。
以下是我目前拥有的:
红线代表屏幕中间,文本小部件应该在它的正上方。
下面是我的代码:
final double _buttonTextSize = 20.0;
final double _buttonWidth = 90.0;
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Container(
padding: const EdgeInsets.all(8.0),
child: _backButton(),),
Expanded(
child: Text(
'Encryption',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 30.0,
color: MyThemeColors.textTitle(),
fontWeight: FontWeight.w400,
),
),
),
],
),
Center(
child: Container(color: Colors.red, height: 100, width: 5),
)
],
),
);
}
Button _backButton() {
return Button(
style: MyButtonStyles.defaultStyle(),
child: Container(
width: _buttonWidth,
padding: const EdgeInsets.symmetric(vertical: 6.0, horizontal: 4.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Padding(
padding: EdgeInsets.only(right: 4.0),
child: Icon(FluentIcons.back, size: 22.0),
),
Text(
'Back',
style: TextStyle(
color: MyThemeColors.textTitle(),
fontSize: _buttonTextSize,
),
),
],
),
),
onPressed: () {
Navigator.pop(context);
},
);
}
对我来说,我有 2 个选项供您选择:
使用 Stack 将按钮放在文本上方。无需计算 :D
使用 LayoutBuilder 正确计算按钮的大小并填充它。
Contrait.maxWith 给你 space 的大小,你可以使用屏幕大小(或外面的另一个 LayoutBuilder 小部件来获得父大小)。
对我来说最好的选择是选项(1)。
这是一些测试代码(抱歉,您提供的代码有一些自定义小部件(Button、MyButtonStyles...)并且不能 copy/paste for 运行,所以我需要创建一个新的)。
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Column(
children: const [
OnlyText(),
Divider(),
OptionOne(),
Divider(),
OptionTwo(),
],
),
);
}
}
class OnlyText extends StatelessWidget {
const OnlyText({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Material(
child: SizedBox(
height: 100,
child: Center(child: Text("this one is in middle")),
),
);
}
}
class OptionTwo extends StatelessWidget {
const OptionTwo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Material(
child: SizedBox(
width: double.infinity,
height: 100,
child: Row(children: [
Container(
color: Colors.green,
height: 100,
child: const Center(child: Text('back button')),
),
Expanded(
child: LayoutBuilder(builder: (context, constrait) {
final padding =
(MediaQuery.of(context).size.width - constrait.maxWidth);
return Padding(
padding: EdgeInsets.only(right: padding),
child: const Center(child: Text('center screen')),
);
}),
)
]),
),
);
}
}
class OptionOne extends StatelessWidget {
const OptionOne({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Material(
child: Align(
alignment: Alignment.topCenter,
child: Container(
width: double.infinity,
height: 100,
padding: const EdgeInsets.all(0),
child: Stack(
// <---- Here you go, a stack will help you
children: [
Row(
children: [
Container(
color: Colors.green,
child: const Center(child: Text('back button')),
),
],
),
const Center(child: Text('center screen')),
],
),
),
),
);
}
}
这是结果:
堆栈建议很好,除非您在中心显示的文本足够长可以重叠。
以下是我如何使用 LayoutBuilder 完成的:
import 'dart:ui' as ui;
Row(
children: [
const BackButton(),
Expanded(
child: LayoutBuilder(
builder: (context, constraints) {
const String string = 'Short title';
final TextSpan span = TextSpan(
text: string,
// Note: use the TextStyle that's relevant for your case. I'm replacing the title of a SliverAppBar, so I used the default TextStyle for AppBar, headline6
style: Theme.of(context).textTheme.headline6,
);
final textPainter = TextPainter(
text: span,
// Maybe test with rtl in the future
textDirection: ui.TextDirection.ltr,
)..layout(maxWidth: constraints.maxWidth);
final double screenWidth = MediaQuery.of(context).size.width;
final double textWidth = textPainter.size.width;
final double leadingWidth = screenWidth - constraints.maxWidth;
final double remainingSpace = screenWidth / 2 - leadingWidth - textWidth / 2;
const Text text = Text(string);
if (remainingSpace <= 0) {
// If the Text overflows, we can just return the text with no positioning
return text;
} else {
// Otherwise, we add blank space to the left to center the text
return Row(
children: [
SizedBox(width: remainingSpace),
text,
],
);
}
},
),
),
],
),
放置一个与左侧小部件大小相同的 Sizedbox,删除展开部分,以便文本仅采用 space 所需的内容并更改对齐方式。
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: const EdgeInsets.all(8.0),
child: _backButton(),),
Text(
'Encryption',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 30.0,
color: MyThemeColors.textTitle(),
fontWeight: FontWeight.w400,
),
SizedBox(
width: _buttonWidth,
),
]}
我想将一个文本小部件对齐到一行的中心,我还想在同一行的左侧放置一个按钮小部件。文本小部件被展开的小部件包围,因此它占据了按钮留下的剩余 space。
以下是我目前拥有的:
红线代表屏幕中间,文本小部件应该在它的正上方。 下面是我的代码:
final double _buttonTextSize = 20.0;
final double _buttonWidth = 90.0;
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Container(
padding: const EdgeInsets.all(8.0),
child: _backButton(),),
Expanded(
child: Text(
'Encryption',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 30.0,
color: MyThemeColors.textTitle(),
fontWeight: FontWeight.w400,
),
),
),
],
),
Center(
child: Container(color: Colors.red, height: 100, width: 5),
)
],
),
);
}
Button _backButton() {
return Button(
style: MyButtonStyles.defaultStyle(),
child: Container(
width: _buttonWidth,
padding: const EdgeInsets.symmetric(vertical: 6.0, horizontal: 4.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Padding(
padding: EdgeInsets.only(right: 4.0),
child: Icon(FluentIcons.back, size: 22.0),
),
Text(
'Back',
style: TextStyle(
color: MyThemeColors.textTitle(),
fontSize: _buttonTextSize,
),
),
],
),
),
onPressed: () {
Navigator.pop(context);
},
);
}
对我来说,我有 2 个选项供您选择:
使用 Stack 将按钮放在文本上方。无需计算 :D
使用 LayoutBuilder 正确计算按钮的大小并填充它。
Contrait.maxWith 给你 space 的大小,你可以使用屏幕大小(或外面的另一个 LayoutBuilder 小部件来获得父大小)。
对我来说最好的选择是选项(1)。 这是一些测试代码(抱歉,您提供的代码有一些自定义小部件(Button、MyButtonStyles...)并且不能 copy/paste for 运行,所以我需要创建一个新的)。
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Column(
children: const [
OnlyText(),
Divider(),
OptionOne(),
Divider(),
OptionTwo(),
],
),
);
}
}
class OnlyText extends StatelessWidget {
const OnlyText({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Material(
child: SizedBox(
height: 100,
child: Center(child: Text("this one is in middle")),
),
);
}
}
class OptionTwo extends StatelessWidget {
const OptionTwo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Material(
child: SizedBox(
width: double.infinity,
height: 100,
child: Row(children: [
Container(
color: Colors.green,
height: 100,
child: const Center(child: Text('back button')),
),
Expanded(
child: LayoutBuilder(builder: (context, constrait) {
final padding =
(MediaQuery.of(context).size.width - constrait.maxWidth);
return Padding(
padding: EdgeInsets.only(right: padding),
child: const Center(child: Text('center screen')),
);
}),
)
]),
),
);
}
}
class OptionOne extends StatelessWidget {
const OptionOne({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Material(
child: Align(
alignment: Alignment.topCenter,
child: Container(
width: double.infinity,
height: 100,
padding: const EdgeInsets.all(0),
child: Stack(
// <---- Here you go, a stack will help you
children: [
Row(
children: [
Container(
color: Colors.green,
child: const Center(child: Text('back button')),
),
],
),
const Center(child: Text('center screen')),
],
),
),
),
);
}
}
这是结果:
堆栈建议很好,除非您在中心显示的文本足够长可以重叠。 以下是我如何使用 LayoutBuilder 完成的:
import 'dart:ui' as ui;
Row(
children: [
const BackButton(),
Expanded(
child: LayoutBuilder(
builder: (context, constraints) {
const String string = 'Short title';
final TextSpan span = TextSpan(
text: string,
// Note: use the TextStyle that's relevant for your case. I'm replacing the title of a SliverAppBar, so I used the default TextStyle for AppBar, headline6
style: Theme.of(context).textTheme.headline6,
);
final textPainter = TextPainter(
text: span,
// Maybe test with rtl in the future
textDirection: ui.TextDirection.ltr,
)..layout(maxWidth: constraints.maxWidth);
final double screenWidth = MediaQuery.of(context).size.width;
final double textWidth = textPainter.size.width;
final double leadingWidth = screenWidth - constraints.maxWidth;
final double remainingSpace = screenWidth / 2 - leadingWidth - textWidth / 2;
const Text text = Text(string);
if (remainingSpace <= 0) {
// If the Text overflows, we can just return the text with no positioning
return text;
} else {
// Otherwise, we add blank space to the left to center the text
return Row(
children: [
SizedBox(width: remainingSpace),
text,
],
);
}
},
),
),
],
),
放置一个与左侧小部件大小相同的 Sizedbox,删除展开部分,以便文本仅采用 space 所需的内容并更改对齐方式。
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: const EdgeInsets.all(8.0),
child: _backButton(),),
Text(
'Encryption',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 30.0,
color: MyThemeColors.textTitle(),
fontWeight: FontWeight.w400,
),
SizedBox(
width: _buttonWidth,
),
]}