flutter - 如何在全角按钮中居中容器,同时它们中的文本应该从左侧的相同位置开始?
问题描述
换句话说,问题的更长版本:如何在全宽 [Y] 小部件中居中 [X] 小部件,同时 [X] 中具有随机宽度的 [Z] 小部件应从左侧开始与其他 [Y] 小部件中的其他 [Z] 小部件的位置相同?
进行翻译后,我意识到在这种情况下我不能使用硬编码值。需要动态子容器宽度支持。
我试图玩 IntrinsicWidth 但没有运气。我不认为这个问题可以用它来解决。也许它有点复杂,也许不是。希望有人可以提供一个不错的解决方案。
import 'package:flutter/material.dart';
import 'dart:math';
import 'package:flutter/scheduler.dart';
void main() {
runApp(MaterialApp(
home: TestScreen(),
));
}
class TestScreen extends StatefulWidget {
@override
_TestScreenState createState() => _TestScreenState();
}
class _TestScreenState extends State<TestScreen> {
final List<String> _randomStringsForSolutions = Iterable<int>.generate(5).map((e) => _randomStringInRandomLength).toList();
final List<GlobalKey> _globalKeysForSolution2 = Iterable<int>.generate(5).map((e) => GlobalKey()).toList();
double _biggestWidthForSolution1;
double _biggestWidthForSolution2;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback(_setBiggestWidthForSolution2);
}
_setBiggestWidthForSolution2(_) {
double biggestWidth;
for (int i = 0; i < _globalKeysForSolution2.length; i++) {
GlobalKey globalKey = _globalKeysForSolution2[i];
RenderBox renderBox = globalKey.currentContext?.findRenderObject();
if (renderBox == null) {
continue;
}
double width = renderBox.size.width;
if (biggestWidth != null && biggestWidth >= width) {
continue;
}
biggestWidth = width;
}
if (_biggestWidthForSolution2 == biggestWidth) {
return;
}
setState(() {
_biggestWidthForSolution2 = biggestWidth;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.brown,
body: Stack(
children: [
Center(child: Container(color: Colors.black, width: 2.0)), // a vertical line in the center for easier understanding
SingleChildScrollView(
child: SafeArea(
child: Column(
children: [
Text('expected result: the icons are in line and everything is centered as possible - only works with hardcoded content width', style: TextStyle(color: Colors.white)),
...expectedResult,
SizedBox(height: 20.0),
Text('actual result: the icons are in line, but the content is not really centered', style: TextStyle(color: Colors.white)),
...actualResult,
Divider(height: 60.0, thickness: 10.0, color: Colors.green),
Text('solution1: the icons are in line and everything is centered as possible - dynamic content width supported', style: TextStyle(color: Colors.lightGreenAccent)),
Text(
'solution1 disadvantages: performance problems are possible because of re-renders (use-case dependent); a new SizeMeasurer widget class has to be implemented',
style: TextStyle(color: Colors.white),
),
...solution1,
Divider(height: 60.0, thickness: 10.0, color: Colors.green),
Text(
'solution2: the icons are in line and everything is centered as possible - dynamic content width supported with only one re-render',
style: TextStyle(color: Colors.lightGreenAccent),
),
Text(
'solution2 disadvantages: we need to keep records of global keys',
style: TextStyle(color: Colors.white),
),
...solution2,
],
),
),
),
],
),
);
}
/// Expected result with hardcoded content and width settings
List<Widget> get expectedResult {
return [
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {},
child: Container(
width: 200,
child: Center(
child: Row(
children: <Widget>[
Padding(
padding: EdgeInsets.only(right: 10),
child: Icon(Icons.mood),
),
Text('Continue with Facebook'),
],
),
),
),
),
),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {},
child: Container(
width: 200,
child: Center(
child: Row(
children: <Widget>[
Padding(
padding: EdgeInsets.only(right: 10),
child: Icon(Icons.mood),
),
Text('Continue with Twitter'),
],
),
),
),
),
),
];
}
/// Actual result when the lengths of the strings are unknown
List<Widget> get actualResult {
return List.generate(2, (_) {
return SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {},
child: Container(
width: 200, // THIS CAN NO LONGER BE USED!
child: Center(
child: Row(
children: <Widget>[
Padding(
padding: EdgeInsets.only(right: 10),
child: Icon(Icons.mood_bad),
),
Text(_randomStringInRandomLength),
],
),
),
),
),
);
});
}
List<Widget> get solution1 {
return List.generate(_randomStringsForSolutions.length, (index) {
return SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {},
child: Container(
width: _biggestWidthForSolution1 ?? null,
child: SizeMeasurer(
onChange: (sizeOfChild) {
if (_biggestWidthForSolution1 != null && _biggestWidthForSolution1 >= sizeOfChild.width) {
return;
}
setState(() {
_biggestWidthForSolution1 = sizeOfChild.width;
});
},
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: EdgeInsets.only(right: 10),
child: Icon(Icons.mood),
),
Text(_randomStringsForSolutions[index]),
],
),
),
),
),
);
});
}
List<Widget> get solution2 {
return List.generate(_randomStringsForSolutions.length, (index) {
return SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {},
child: Container(
width: _biggestWidthForSolution2 ?? null,
child: Row(
key: _globalKeysForSolution2[index],
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: EdgeInsets.only(right: 10),
child: Icon(Icons.mood),
),
Text(_randomStringsForSolutions[index]),
],
),
),
),
);
});
}
}
String get _randomStringInRandomLength {
Random random = Random();
int randomInt = random.nextInt(20) + 1;
return String.fromCharCodes(List.generate(randomInt, (index) => random.nextInt(33) + 89));
}
typedef void OnWidgetSizeChange(Size size);
class SizeMeasurer extends StatefulWidget {
final Widget child;
final OnWidgetSizeChange onChange;
const SizeMeasurer({
Key key,
@required this.onChange,
@required this.child,
}) : super(key: key);
@override
_SizeMeasurerState createState() => _SizeMeasurerState();
}
class _SizeMeasurerState extends State<SizeMeasurer> {
GlobalKey widgetKey = GlobalKey();
Size oldSize;
@override
Widget build(BuildContext context) {
SchedulerBinding.instance.addPostFrameCallback(postFrameCallback);
return Container(
key: widgetKey,
child: widget.child,
);
}
void postFrameCallback(_) {
BuildContext context = widgetKey.currentContext;
if (context == null) {
return;
}
Size newSize = context.size;
if (oldSize == newSize) {
return;
}
oldSize = newSize;
widget.onChange(newSize);
}
}
更新 - 我创建了两个可能的解决方案并将它们包含在上面的代码中!
解决方案
这是可能的,但您必须在构建小部件后设置值。这是我在Dartpad中为您编写的示例代码( https://dartpad.dev/fea023e2c9191faa21d01af92d808ca7 )。下面的代码仅在构建小部件后运行一次,您可能希望修改它以在每次状态更改时运行,但可以轻松完成:
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: MyHome(),
);
}
}
class MyHome extends StatefulWidget {
final String title;
const MyHome({Key key, this.title}) : super(key: key);
@override
_MyHomeState createState() => _MyHomeState();
}
class _MyHomeState extends State<MyHome> {
GlobalKey _keyRed = GlobalKey();
double padding = 0;
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => _findPosition());
}
void _findPosition() {
final RenderBox renderBoxRed = _keyRed.currentContext.findRenderObject();
final positionRed = renderBoxRed.localToGlobal(Offset.zero);
print(positionRed);
setState(() {
padding = positionRed.dx;
});
}
@override
Widget build(BuildContext context) {
List<String> titles = ["one", "two", "three", "f"];
final String longest = titles.reduce((a, b) {
return a.length > b.length ? a : b;
});
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
for (var title in titles)
MyRow(
title: title,
alignment: longest == title
? MainAxisAlignment.center
: MainAxisAlignment.start,
padding: longest == title ? 0 : padding,
rowKey: longest == title ? _keyRed : null),
],
),
);
}
}
class MyRow extends StatelessWidget {
const MyRow({this.title, this.alignment, this.padding, this.rowKey});
final String title;
final MainAxisAlignment alignment;
final double padding;
final GlobalKey rowKey;
@override
Widget build(BuildContext context) {
return Center(
child: Row(
mainAxisAlignment: alignment,
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: padding),
child: Icon(
Icons.favorite,
color: Colors.pink,
size: 24.0,
key: rowKey,
),
),
SizedBox(width: 10),
Text(title)
],
),
);
}
}
推荐阅读
- python - 如何使用 sed 将 repo 中的所有 print 'example' 替换为 print('example')?
- javascript - 无法在 JQuery 中同时读取单选按钮和选择框
- apache-spark - Spark 的 Partition Pruning 和 Predicate Pushdown 有什么区别?
- c - IP地址作为位置参数的问题
- javascript - 如何在 JavaScript 中以类似 LINQ 的方式进行分组和总结
- apache-kafka - 将 KsqlDB 用作 HIstorian DB
- java - 如何在 MyBatis 中使用带有 @Many 注释的 UUID 类型处理程序?
- python - Python:Coinmarketcap API 关闭?
- vb.net - 按字母顺序对 csv 文件进行排序 Visual Basic
- java - spring boot parent 防止资源:resources