了解约束一文对了解布局的工作原理非常有用。但是,我在创建按照这里所述方式工作的自定义布局时遇到了问题。
这篇文章说尺寸是向上流动的,首先我们获取子元素的大小,然后我们可以根据子元素的大小决定自己的大小。
看起来
因此,问题是,按照了解约束中解释的约束向下流动和尺寸向上流动的布局行为,实现自定义布局的最佳方式是什么?
编辑:以下是一些代码,以解释我真正想要的内容。
这就是我要实现的外观。在这里,我手动提供了蓝色正方形的宽度和高度。我不想这么做。我希望子元素的大小确定正方形的大小。我希望大小从文本流动,蓝色正方形应该决定边长为
建议使用
参考链接:https://codepen.io/gazialankus/pen/MWKXvpO
这篇文章说尺寸是向上流动的,首先我们获取子元素的大小,然后我们可以根据子元素的大小决定自己的大小。
看起来
CustomSingleChildLayout
是创建具有一个子元素的自定义布局的方法。在其中,基于我提到的文章,我想要获得我的子元素的大小,然后根据它来决定自己的大小。然而,这似乎是不可能的。getSize()
方法以约束为参数调用,因此我应该根据从上面接收到的约束决定我的小部件的大小。我能够在getPositionForChild()
中获取子元素的大小,但这是在我在getSize()
中给出我的大小之后调用的。这时触发重新布局是有道理的,但似乎我不允许从布局系统调用的函数中这样做。因此,问题是,按照了解约束中解释的约束向下流动和尺寸向上流动的布局行为,实现自定义布局的最佳方式是什么?
编辑:以下是一些代码,以解释我真正想要的内容。
这就是我要实现的外观。在这里,我手动提供了蓝色正方形的宽度和高度。我不想这么做。我希望子元素的大小确定正方形的大小。我希望大小从文本流动,蓝色正方形应该决定边长为
max(width, height)
。
https://codepen.io/gazialankus/pen/abdKyVR
import 'package:flutter/material.dart';
class AppRoot extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo', // Appears in app switcher
theme: ThemeData(
primaryColor: Colors.grey.shade300,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyPage(),
);
}
}
class MyPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
color: Colors.yellow,
child: SizedBox(
width: 180,
height: 180,
child: Container(
color: Colors.blue,
child: AspectRatio(
aspectRatio: 1,
child: Text(
"I want to be in a tight square.",
style: TextStyle(
backgroundColor: Colors.red
),
),
),
),
),
),
),
);
}
}
void main() {
runApp(AppRoot());
}
建议使用
Align
进行包装。我尝试过,但是 Align
会变大。 Align
的 widthFactor
和 heightFactor
利用了子元素的大小,但不能使其成为正方形。移除蓝色的 Container
没有影响。参考链接:https://codepen.io/gazialankus/pen/MWKXvpO
import 'package:flutter/material.dart';
class AppRoot extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo', // Appears in app switcher
theme: ThemeData(
primaryColor: Colors.grey.shade300,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyPage(),
);
}
}
class MyPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
color: Colors.yellow,
child: Align(
alignment: Alignment.center,
child: Container(
color: Colors.blue,
child: Text(
"I want to be in a square.",
style: TextStyle(
backgroundColor: Colors.red
),
),
),
),
),
),
);
}
}
void main() {
runApp(AppRoot());
}
在这里,我尝试使用 AspectRatio
来制作正方形,但它并不关心子元素的大小,只是单纯的扩大:
https://codepen.io/gazialankus/pen/KKVevXv
import 'package:flutter/material.dart';
class AppRoot extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo', // Appears in app switcher
theme: ThemeData(
primaryColor: Colors.grey.shade300,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyPage(),
);
}
}
class MyPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
color: Colors.yellow,
child: Align(
alignment: Alignment.center,
child: Container(
color: Colors.blue,
child: AspectRatio(
aspectRatio: 1,
child: Text(
"I want to be in a tight square.",
style: TextStyle(
backgroundColor: Colors.red
),
),
),
),
),
),
),
);
}
}
void main() {
runApp(AppRoot());
}
我想要做一个自定义布局,但是我无法使用子元素的尺寸来确定我的尺寸。在接收到子元素之前,我必须决定我的尺寸。尝试在了解子元素的尺寸后强制重新布局会引发异常,代码末尾有详细信息。Codepen会默默失败。
https://codepen.io/gazialankus/pen/LYGrjxM
import 'package:flutter/material.dart';
class AppRoot extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo', // Appears in app switcher
theme: ThemeData(
primaryColor: Colors.grey.shade300,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyPage(),
);
}
}
class MyPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
color: Colors.yellow,
child: CustomSingleChildLayout(
delegate: MyDelegate(relayout: ValueNotifier<int>(0)),
child: Text(
"I want to be in a square.",
style: TextStyle(
backgroundColor: Colors.red
),
),
),
),
),
);
}
}
class MyDelegate extends SingleChildLayoutDelegate {
ValueNotifier<int> relayout;
MyDelegate({ this.relayout }) : super(relayout: relayout);
@override
bool shouldRelayout(SingleChildLayoutDelegate oldDelegate) {
return desiredWidth == 100;
}
@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
print('getConstraintsForChild');
return super.getConstraintsForChild(constraints);
}
double desiredWidth = 300;
@override
Offset getPositionForChild(Size size, Size childSize) {
print('getPositionForChild');
print(size);
print(childSize);
if (size.width > childSize.width) {
desiredWidth = childSize.width;
relayout.value++;
// ^trying to force a relayout
// throws exception, output is given at the bottom in comments
print("RELAYOUT");
}
return super.getPositionForChild(size, childSize);
}
@override
Size getSize(BoxConstraints constraints) {
print('getSize');
return Size(desiredWidth, 100);
}
}
void main() {
runApp(AppRoot());
}
/*
Performing hot reload...
Syncing files to device LG H990...
I/flutter (19850): getSize
I/flutter (19850): getConstraintsForChild
I/flutter (19850): getPositionForChild
I/flutter (19850): Size(300.0, 100.0)
I/flutter (19850): Size(148.0, 16.0)
════════ Exception caught by foundation library ════════════════════════════════════════════════════
The following assertion was thrown while dispatching notifications for ValueNotifier<int>:
'package:flutter/src/rendering/object.dart': Failed assertion: line 1527 pos 12: '_debugCanPerformMutations': is not true.
Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
https://github.com/flutter/flutter/issues/new?template=BUG.md
When the exception was thrown, this was the stack:
#2 RenderObject.markNeedsLayout (package:flutter/src/rendering/object.dart:1527:12)
#3 RenderBox.markNeedsLayout (package:flutter/src/rendering/box.dart:2053:11)
#4 ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:207:21)
#5 ValueNotifier.value= (package:flutter/src/foundation/change_notifier.dart:274:5)
#6 MyDelegate.getPositionForChild (package:m_health/ui/app_root.dart:64:16)
...
The ValueNotifier<int> sending notification was: ValueNotifier<int>#ac2fb(1)
════════════════════════════════════════════════════════════════════════════════════════════════════
Reloaded 2 of 522 libraries in 1,390ms.
I/flutter (19850): RELAYOUT
*/
UnconstrainedBox
和IntrinsicWidth
结合,肯定解决了我的问题。等我有时间,我会尝试跟进我的原始任务,并想出一种类似于“理解约束”文章的创建自定义小部件的方法。 - Gazihan Alankus