如何获取Widget的高度?
How to get height of a Widget?
我不明白 LayoutBuilder
是如何用来获取 Widget 的高度的。
我需要显示小部件列表并获取它们的高度,以便计算一些特殊的滚动效果。我正在开发一个包,其他开发人员提供小部件(我不控制它们)。我读到 LayoutBuilder 可用于获取高度。
在非常简单的情况下,我尝试将Widget包装在LayoutBuilder.builder中并将其放入Stack中,但我总是得到minHeight
0.0
和maxHeight
INFINITY
。我是否滥用了 LayoutBuilder?
编辑:LayoutBuilder 似乎行不通了。我找到了 CustomSingleChildLayout 这几乎是一个解决方案。
我扩展了那个委托,我能够在 getPositionForChild(Size size, Size childSize)
方法中获得小部件的高度。但是,第一个被调用的方法是 Size getSize(BoxConstraints constraints)
作为约束,我得到 0 到 INFINITY 因为我将这些 CustomSingleChildLayouts 放在 ListView 中。
我的问题是 SingleChildLayoutDelegate getSize
的运行就像它需要 return 视图的高度一样。我不知道那一刻child的高度。我只能 return constraints.smallest (即 0,高度为 0),或者 constraints.biggest 无穷大并使应用程序崩溃。
在文档中它甚至说:
...but the size of the parent cannot depend on the size of the child.
这是一个奇怪的限制。
要获取屏幕上某个小部件的 size/position,您可以使用 GlobalKey
获取其 BuildContext
,然后找到该特定小部件的 RenderBox
,这将包含其全局位置和渲染大小。
只有一件事需要注意:如果未呈现小部件,则该上下文可能不存在。这可能会导致 ListView
出现问题,因为只有当小部件可能可见时才会呈现它们。
另一个问题是您无法在 build
调用期间获取小部件的 RenderBox
,因为小部件尚未呈现。
但是如果我需要在构建过程中获取大小怎么办!我能做什么?
有一个很酷的小部件可以提供帮助:Overlay
及其 OverlayEntry
。
它们用于在其他所有内容之上显示小部件(类似于堆栈)。
但最酷的是他们在不同的 build
流程上;它们是在 常规小部件之后构建的。
这有一个非常酷的含义:OverlayEntry
可以有一个大小取决于实际小部件树的小部件。
好的。但是OverlayEntry不是需要手动重建吗?
是的,他们有。但还有一件事需要注意:ScrollController
,传递给 Scrollable
,是一个类似于 AnimationController
.
的可听对象
这意味着您可以将 AnimatedBuilder
与 ScrollController
结合使用,这样可以在滚动时自动重建您的小部件。非常适合这种情况,对吧?
将所有内容组合成一个示例:
在下面的示例中,您会看到 ListView
内的小部件后面有一个叠加层,并且具有相同的高度。
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final controller = ScrollController();
OverlayEntry sticky;
GlobalKey stickyKey = GlobalKey();
@override
void initState() {
if (sticky != null) {
sticky.remove();
}
sticky = OverlayEntry(
builder: (context) => stickyBuilder(context),
);
SchedulerBinding.instance.addPostFrameCallback((_) {
Overlay.of(context).insert(sticky);
});
super.initState();
}
@override
void dispose() {
sticky.remove();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
controller: controller,
itemBuilder: (context, index) {
if (index == 6) {
return Container(
key: stickyKey,
height: 100.0,
color: Colors.green,
child: const Text("I'm fat"),
);
}
return ListTile(
title: Text(
'Hello $index',
style: const TextStyle(color: Colors.white),
),
);
},
),
);
}
Widget stickyBuilder(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: (_,Widget child) {
final keyContext = stickyKey.currentContext;
if (keyContext != null) {
// widget is visible
final box = keyContext.findRenderObject() as RenderBox;
final pos = box.localToGlobal(Offset.zero);
return Positioned(
top: pos.dy + box.size.height,
left: 50.0,
right: 50.0,
height: box.size.height,
child: Material(
child: Container(
alignment: Alignment.center,
color: Colors.purple,
child: const Text("^ Nah I think you're okay"),
),
),
);
}
return Container();
},
);
}
}
注:
当导航到不同的屏幕时,呼叫跟随否则粘性将保持可见。
sticky.remove();
如果我没理解错的话,你想测量一些任意小部件的尺寸,你可以用另一个小部件包装这些小部件。在这种情况下,this answer 中的方法应该适合您。
基本上解决方案是在小部件生命周期中绑定一个回调,将在第一帧渲染后调用,从那里您可以访问context.size
。问题是您必须将要测量的小部件包装在有状态小部件中。而且,如果您绝对需要 build()
内的尺寸,那么您只能在第二次渲染中访问它(它仅在第一次渲染后可用)。
这是(我认为)最直接的方法。
Copy-paste将以下内容放入您的项目中。
更新:使用 RenderProxyBox
会导致更正确的实现,因为每次重建 child 及其后代时都会调用它,而 [=36= 并非总是如此] build() 方法。
注意:正如 Hixie here 指出的那样,这并不是一种有效的方法。但这是最简单的。
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
typedef void OnWidgetSizeChange(Size size);
class MeasureSizeRenderObject extends RenderProxyBox {
Size? oldSize;
final OnWidgetSizeChange onChange;
MeasureSizeRenderObject(this.onChange);
@override
void performLayout() {
super.performLayout();
Size newSize = child!.size;
if (oldSize == newSize) return;
oldSize = newSize;
WidgetsBinding.instance!.addPostFrameCallback((_) {
onChange(newSize);
});
}
}
class MeasureSize extends SingleChildRenderObjectWidget {
final OnWidgetSizeChange onChange;
const MeasureSize({
Key? key,
required this.onChange,
required Widget child,
}) : super(key: key, child: child);
@override
RenderObject createRenderObject(BuildContext context) {
return MeasureSizeRenderObject(onChange);
}
}
然后,只需用 MeasureSize
.
包裹您想要测量尺寸的小部件
var myChildSize = Size.zero;
Widget build(BuildContext context) {
return ...(
child: MeasureSize(
onChange: (size) {
setState(() {
myChildSize = size;
});
},
child: ...
),
);
}
所以是的,parent cannot can 的大小取决于 child如果你够努力的话。
个人轶事 - 这对于限制像 Align
这样的小部件的大小很方便,它喜欢占用荒谬的 space.
findRenderObject()
returns RenderBox
用于给定绘制的widget的大小,应该在widget树构建完成后调用,所以必须配合使用一些回调机制或 addPostFrameCallback()
回调。
class SizeWidget extends StatefulWidget {
@override
_SizeWidgetState createState() => _SizeWidgetState();
}
class _SizeWidgetState extends State<SizeWidget> {
final GlobalKey _textKey = GlobalKey();
Size textSize;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => getSizeAndPosition());
}
getSizeAndPosition() {
RenderBox _cardBox = _textKey.currentContext.findRenderObject();
textSize = _cardBox.size;
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Size Position"),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
"Currern Size of Text",
key: _textKey,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
),
SizedBox(
height: 20,
),
Text(
"Size - $textSize",
textAlign: TextAlign.center,
),
],
),
);
}
}
输出:
使用包:z_tools。
步骤:
1。更改主文件
void main() async {
runZoned(
() => runApp(
CalculateWidgetAppContainer(
child: Center(
child: LocalizedApp(delegate, MyApp()),
),
),
),
onError: (Object obj, StackTrace stack) {
print('global exception: obj = $obj;\nstack = $stack');
},
);
}
2。在函数中使用
_Cell(
title: 'cal: Column-min',
callback: () async {
Widget widget1 = Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 100,
height: 30,
color: Colors.blue,
),
Container(
height: 20.0,
width: 30,
),
Text('111'),
],
);
// size = Size(100.0, 66.0)
print('size = ${await getWidgetSize(widget1)}');
},
),
这里有一个关于如何使用 LayoutBuilder
确定小部件大小的示例。
由于 LayoutBuilder
小部件能够确定其父小部件的约束,其用例之一是能够使其子小部件适应其父小部件的尺寸。
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var dimension = 40.0;
increaseWidgetSize() {
setState(() {
dimension += 20;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(children: <Widget>[
Text('Dimension: $dimension'),
Container(
color: Colors.teal,
alignment: Alignment.center,
height: dimension,
width: dimension,
// LayoutBuilder inherits its parent widget's dimension. In this case, the Container in teal
child: LayoutBuilder(builder: (context, constraints) {
debugPrint('Max height: ${constraints.maxHeight}, max width: ${constraints.maxWidth}');
return Container(); // create function here to adapt to the parent widget's constraints
}),
),
]),
),
floatingActionButton: FloatingActionButton(
onPressed: increaseWidgetSize,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
演示
日志
I/flutter (26712): Max height: 40.0, max width: 40.0
I/flutter (26712): Max height: 60.0, max width: 60.0
I/flutter (26712): Max height: 80.0, max width: 80.0
I/flutter (26712): Max height: 100.0, max width: 100.0
更新:您也可以使用MediaQuery来实现类似的功能。
@override
Widget build(BuildContext context) {
var screenSize = MediaQuery.of(context).size ;
if (screenSize.width > layoutSize){
return Widget();
} else {
return Widget(); /// Widget if doesn't match the size
}
}
最简单的方法是使用MeasuredSize它是一个在运行时计算其子项大小的小部件。
你可以这样使用它:
MeasuredSize(
onChange: (Size size) {
setState(() {
print(size);
});
},
child: Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
);
您可以在这里找到它:https://pub.dev/packages/measured_size
让我给你一个小部件
class SizeProviderWidget extends StatefulWidget {
final Widget child;
final Function(Size) onChildSize;
const SizeProviderWidget(
{Key? key, required this.onChildSize, required this.child})
: super(key: key);
@override
_SizeProviderWidgetState createState() => _SizeProviderWidgetState();
}
class _SizeProviderWidgetState extends State<SizeProviderWidget> {
@override
void initState() {
///add size listener for first build
_onResize();
super.initState();
}
void _onResize() {
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
if (context.size is Size) {
widget.onChildSize(context.size!);
}
});
}
@override
Widget build(BuildContext context) {
///add size listener for every build uncomment the fallowing
///_onResize();
return widget.child;
}
}
编辑
只需用 OrientationBuilder
包裹 SizeProviderWidget
使其符合设备的方向
这很容易,而且仍然可以在 StatelessWidget 中完成。
class ColumnHeightWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final scrollController = ScrollController();
final columnKey = GlobalKey();
_scrollToCurrentProgress(columnKey, scrollController);
return Scaffold(
body: SingleChildScrollView(
controller: scrollController,
child: Column(
children: [],
),
),
);
}
void _scrollToCurrentProgress(GlobalKey<State<StatefulWidget>> columnKey,
ScrollController scrollController) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
final RenderBox renderBoxRed =
columnKey.currentContext.findRenderObject();
final height = renderBoxRed.size.height;
scrollController.animateTo(percentOfHeightYouWantToScroll * height,
duration: Duration(seconds: 1), curve: Curves.decelerate);
});
}
}
您可以用同样的方式计算任何小部件的子高度并滚动到该位置。
我将此小部件制作为简单的无状态解决方案:
class ChildSizeNotifier extends StatelessWidget {
final ValueNotifier<Size> notifier = ValueNotifier(const Size(0, 0));
final Widget Function(BuildContext context, Size size, Widget child) builder;
final Widget child;
UjChildSizeNotifier({
Key key,
@required this.builder,
this.child,
}) : super(key: key) {}
@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback(
(_) {
notifier.value = (context.findRenderObject() as RenderBox).size;
},
);
return ValueListenableBuilder(
valueListenable: notifier,
builder: builder,
child: child,
);
}
}
这样用
ChildSizeNotifier(
builder: (context, size, child) {
// size is the size of the text
return Text(size.height > 50 ? 'big' : 'small');
},
)
没有直接的方法来计算小部件的大小,所以要找到我们必须借助小部件的上下文。
调用 context.size returns 我们 Size 对象,它包含小部件的高度和宽度。 context.size 计算小部件的渲染框和 returns 大小。
结帐https://medium.com/flutterworld/flutter-how-to-get-the-height-of-the-widget-be4892abb1a2
Might be this could help
Tested on Flutter: 2.2.3
在您的项目中复制下面的代码。
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class WidgetSize extends StatefulWidget {
final Widget child;
final Function onChange;
const WidgetSize({
Key? key,
required this.onChange,
required this.child,
}) : super(key: key);
@override
_WidgetSizeState createState() => _WidgetSizeState();
}
class _WidgetSizeState extends State<WidgetSize> {
@override
Widget build(BuildContext context) {
SchedulerBinding.instance!.addPostFrameCallback(postFrameCallback);
return Container(
key: widgetKey,
child: widget.child,
);
}
var widgetKey = GlobalKey();
var oldSize;
void postFrameCallback(_) {
var context = widgetKey.currentContext;
if (context == null) return;
var newSize = context.size;
if (oldSize == newSize) return;
oldSize = newSize;
widget.onChange(newSize);
}
}
声明一个变量来存储Size
Size mySize = Size.zero;
添加以下代码以获取尺寸:
child: WidgetSize(
onChange: (Size mapSize) {
setState(() {
mySize = mapSize;
print("mySize:" + mySize.toString());
});
},
child: ()
**Credit to @Manuputty**
class OrigChildWH extends StatelessWidget {
final Widget Function(BuildContext context, Size size, Widget? child) builder;
final Widget? child;
const XRChildWH({
Key? key,
required this.builder,
this.child,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return OrientationBuilder(builder: (context, orientation) {
return ChildSizeNotifier(builder: builder);
});
}
}
class ChildSizeNotifier extends StatelessWidget {
final ValueNotifier<Size> notifier = ValueNotifier(const Size(0, 0));
final Widget Function(BuildContext context, Size size, Widget? child) builder;
final Widget? child;
ChildSizeNotifier({
Key? key,
required this.builder,
this.child,
}) : super(key: key);
@override
Widget build(BuildContext context) {
WidgetsBinding.instance!.addPostFrameCallback(
(_) {
notifier.value = (context.findRenderObject() as RenderBox).size;
},
);
return ValueListenableBuilder(
valueListenable: notifier,
builder: builder,
child: child,
);
}
}
**Simple to use:**
OrigChildWH(
builder: (context, size, child) {
//Your child here: mine:: Container()
return Container()
})
我不明白 LayoutBuilder
是如何用来获取 Widget 的高度的。
我需要显示小部件列表并获取它们的高度,以便计算一些特殊的滚动效果。我正在开发一个包,其他开发人员提供小部件(我不控制它们)。我读到 LayoutBuilder 可用于获取高度。
在非常简单的情况下,我尝试将Widget包装在LayoutBuilder.builder中并将其放入Stack中,但我总是得到minHeight
0.0
和maxHeight
INFINITY
。我是否滥用了 LayoutBuilder?
编辑:LayoutBuilder 似乎行不通了。我找到了 CustomSingleChildLayout 这几乎是一个解决方案。
我扩展了那个委托,我能够在 getPositionForChild(Size size, Size childSize)
方法中获得小部件的高度。但是,第一个被调用的方法是 Size getSize(BoxConstraints constraints)
作为约束,我得到 0 到 INFINITY 因为我将这些 CustomSingleChildLayouts 放在 ListView 中。
我的问题是 SingleChildLayoutDelegate getSize
的运行就像它需要 return 视图的高度一样。我不知道那一刻child的高度。我只能 return constraints.smallest (即 0,高度为 0),或者 constraints.biggest 无穷大并使应用程序崩溃。
在文档中它甚至说:
...but the size of the parent cannot depend on the size of the child.
这是一个奇怪的限制。
要获取屏幕上某个小部件的 size/position,您可以使用 GlobalKey
获取其 BuildContext
,然后找到该特定小部件的 RenderBox
,这将包含其全局位置和渲染大小。
只有一件事需要注意:如果未呈现小部件,则该上下文可能不存在。这可能会导致 ListView
出现问题,因为只有当小部件可能可见时才会呈现它们。
另一个问题是您无法在 build
调用期间获取小部件的 RenderBox
,因为小部件尚未呈现。
但是如果我需要在构建过程中获取大小怎么办!我能做什么?
有一个很酷的小部件可以提供帮助:Overlay
及其 OverlayEntry
。
它们用于在其他所有内容之上显示小部件(类似于堆栈)。
但最酷的是他们在不同的 build
流程上;它们是在 常规小部件之后构建的。
这有一个非常酷的含义:OverlayEntry
可以有一个大小取决于实际小部件树的小部件。
好的。但是OverlayEntry不是需要手动重建吗?
是的,他们有。但还有一件事需要注意:ScrollController
,传递给 Scrollable
,是一个类似于 AnimationController
.
这意味着您可以将 AnimatedBuilder
与 ScrollController
结合使用,这样可以在滚动时自动重建您的小部件。非常适合这种情况,对吧?
将所有内容组合成一个示例:
在下面的示例中,您会看到 ListView
内的小部件后面有一个叠加层,并且具有相同的高度。
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final controller = ScrollController();
OverlayEntry sticky;
GlobalKey stickyKey = GlobalKey();
@override
void initState() {
if (sticky != null) {
sticky.remove();
}
sticky = OverlayEntry(
builder: (context) => stickyBuilder(context),
);
SchedulerBinding.instance.addPostFrameCallback((_) {
Overlay.of(context).insert(sticky);
});
super.initState();
}
@override
void dispose() {
sticky.remove();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
controller: controller,
itemBuilder: (context, index) {
if (index == 6) {
return Container(
key: stickyKey,
height: 100.0,
color: Colors.green,
child: const Text("I'm fat"),
);
}
return ListTile(
title: Text(
'Hello $index',
style: const TextStyle(color: Colors.white),
),
);
},
),
);
}
Widget stickyBuilder(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: (_,Widget child) {
final keyContext = stickyKey.currentContext;
if (keyContext != null) {
// widget is visible
final box = keyContext.findRenderObject() as RenderBox;
final pos = box.localToGlobal(Offset.zero);
return Positioned(
top: pos.dy + box.size.height,
left: 50.0,
right: 50.0,
height: box.size.height,
child: Material(
child: Container(
alignment: Alignment.center,
color: Colors.purple,
child: const Text("^ Nah I think you're okay"),
),
),
);
}
return Container();
},
);
}
}
注:
当导航到不同的屏幕时,呼叫跟随否则粘性将保持可见。
sticky.remove();
如果我没理解错的话,你想测量一些任意小部件的尺寸,你可以用另一个小部件包装这些小部件。在这种情况下,this answer 中的方法应该适合您。
基本上解决方案是在小部件生命周期中绑定一个回调,将在第一帧渲染后调用,从那里您可以访问context.size
。问题是您必须将要测量的小部件包装在有状态小部件中。而且,如果您绝对需要 build()
内的尺寸,那么您只能在第二次渲染中访问它(它仅在第一次渲染后可用)。
这是(我认为)最直接的方法。
Copy-paste将以下内容放入您的项目中。
更新:使用 RenderProxyBox
会导致更正确的实现,因为每次重建 child 及其后代时都会调用它,而 [=36= 并非总是如此] build() 方法。
注意:正如 Hixie here 指出的那样,这并不是一种有效的方法。但这是最简单的。
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
typedef void OnWidgetSizeChange(Size size);
class MeasureSizeRenderObject extends RenderProxyBox {
Size? oldSize;
final OnWidgetSizeChange onChange;
MeasureSizeRenderObject(this.onChange);
@override
void performLayout() {
super.performLayout();
Size newSize = child!.size;
if (oldSize == newSize) return;
oldSize = newSize;
WidgetsBinding.instance!.addPostFrameCallback((_) {
onChange(newSize);
});
}
}
class MeasureSize extends SingleChildRenderObjectWidget {
final OnWidgetSizeChange onChange;
const MeasureSize({
Key? key,
required this.onChange,
required Widget child,
}) : super(key: key, child: child);
@override
RenderObject createRenderObject(BuildContext context) {
return MeasureSizeRenderObject(onChange);
}
}
然后,只需用 MeasureSize
.
var myChildSize = Size.zero;
Widget build(BuildContext context) {
return ...(
child: MeasureSize(
onChange: (size) {
setState(() {
myChildSize = size;
});
},
child: ...
),
);
}
所以是的,parent cannot can 的大小取决于 child如果你够努力的话。
个人轶事 - 这对于限制像 Align
这样的小部件的大小很方便,它喜欢占用荒谬的 space.
findRenderObject()
returns RenderBox
用于给定绘制的widget的大小,应该在widget树构建完成后调用,所以必须配合使用一些回调机制或 addPostFrameCallback()
回调。
class SizeWidget extends StatefulWidget {
@override
_SizeWidgetState createState() => _SizeWidgetState();
}
class _SizeWidgetState extends State<SizeWidget> {
final GlobalKey _textKey = GlobalKey();
Size textSize;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => getSizeAndPosition());
}
getSizeAndPosition() {
RenderBox _cardBox = _textKey.currentContext.findRenderObject();
textSize = _cardBox.size;
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Size Position"),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
"Currern Size of Text",
key: _textKey,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
),
SizedBox(
height: 20,
),
Text(
"Size - $textSize",
textAlign: TextAlign.center,
),
],
),
);
}
}
输出:
使用包:z_tools。 步骤:
1。更改主文件
void main() async {
runZoned(
() => runApp(
CalculateWidgetAppContainer(
child: Center(
child: LocalizedApp(delegate, MyApp()),
),
),
),
onError: (Object obj, StackTrace stack) {
print('global exception: obj = $obj;\nstack = $stack');
},
);
}
2。在函数中使用
_Cell(
title: 'cal: Column-min',
callback: () async {
Widget widget1 = Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 100,
height: 30,
color: Colors.blue,
),
Container(
height: 20.0,
width: 30,
),
Text('111'),
],
);
// size = Size(100.0, 66.0)
print('size = ${await getWidgetSize(widget1)}');
},
),
这里有一个关于如何使用 LayoutBuilder
确定小部件大小的示例。
由于 LayoutBuilder
小部件能够确定其父小部件的约束,其用例之一是能够使其子小部件适应其父小部件的尺寸。
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var dimension = 40.0;
increaseWidgetSize() {
setState(() {
dimension += 20;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(children: <Widget>[
Text('Dimension: $dimension'),
Container(
color: Colors.teal,
alignment: Alignment.center,
height: dimension,
width: dimension,
// LayoutBuilder inherits its parent widget's dimension. In this case, the Container in teal
child: LayoutBuilder(builder: (context, constraints) {
debugPrint('Max height: ${constraints.maxHeight}, max width: ${constraints.maxWidth}');
return Container(); // create function here to adapt to the parent widget's constraints
}),
),
]),
),
floatingActionButton: FloatingActionButton(
onPressed: increaseWidgetSize,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
演示
日志
I/flutter (26712): Max height: 40.0, max width: 40.0
I/flutter (26712): Max height: 60.0, max width: 60.0
I/flutter (26712): Max height: 80.0, max width: 80.0
I/flutter (26712): Max height: 100.0, max width: 100.0
更新:您也可以使用MediaQuery来实现类似的功能。
@override
Widget build(BuildContext context) {
var screenSize = MediaQuery.of(context).size ;
if (screenSize.width > layoutSize){
return Widget();
} else {
return Widget(); /// Widget if doesn't match the size
}
}
最简单的方法是使用MeasuredSize它是一个在运行时计算其子项大小的小部件。
你可以这样使用它:
MeasuredSize(
onChange: (Size size) {
setState(() {
print(size);
});
},
child: Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
);
您可以在这里找到它:https://pub.dev/packages/measured_size
让我给你一个小部件
class SizeProviderWidget extends StatefulWidget {
final Widget child;
final Function(Size) onChildSize;
const SizeProviderWidget(
{Key? key, required this.onChildSize, required this.child})
: super(key: key);
@override
_SizeProviderWidgetState createState() => _SizeProviderWidgetState();
}
class _SizeProviderWidgetState extends State<SizeProviderWidget> {
@override
void initState() {
///add size listener for first build
_onResize();
super.initState();
}
void _onResize() {
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
if (context.size is Size) {
widget.onChildSize(context.size!);
}
});
}
@override
Widget build(BuildContext context) {
///add size listener for every build uncomment the fallowing
///_onResize();
return widget.child;
}
}
编辑
只需用 OrientationBuilder
包裹 SizeProviderWidget
使其符合设备的方向
这很容易,而且仍然可以在 StatelessWidget 中完成。
class ColumnHeightWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final scrollController = ScrollController();
final columnKey = GlobalKey();
_scrollToCurrentProgress(columnKey, scrollController);
return Scaffold(
body: SingleChildScrollView(
controller: scrollController,
child: Column(
children: [],
),
),
);
}
void _scrollToCurrentProgress(GlobalKey<State<StatefulWidget>> columnKey,
ScrollController scrollController) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
final RenderBox renderBoxRed =
columnKey.currentContext.findRenderObject();
final height = renderBoxRed.size.height;
scrollController.animateTo(percentOfHeightYouWantToScroll * height,
duration: Duration(seconds: 1), curve: Curves.decelerate);
});
}
}
您可以用同样的方式计算任何小部件的子高度并滚动到该位置。
我将此小部件制作为简单的无状态解决方案:
class ChildSizeNotifier extends StatelessWidget {
final ValueNotifier<Size> notifier = ValueNotifier(const Size(0, 0));
final Widget Function(BuildContext context, Size size, Widget child) builder;
final Widget child;
UjChildSizeNotifier({
Key key,
@required this.builder,
this.child,
}) : super(key: key) {}
@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback(
(_) {
notifier.value = (context.findRenderObject() as RenderBox).size;
},
);
return ValueListenableBuilder(
valueListenable: notifier,
builder: builder,
child: child,
);
}
}
这样用
ChildSizeNotifier(
builder: (context, size, child) {
// size is the size of the text
return Text(size.height > 50 ? 'big' : 'small');
},
)
没有直接的方法来计算小部件的大小,所以要找到我们必须借助小部件的上下文。
调用 context.size returns 我们 Size 对象,它包含小部件的高度和宽度。 context.size 计算小部件的渲染框和 returns 大小。
结帐https://medium.com/flutterworld/flutter-how-to-get-the-height-of-the-widget-be4892abb1a2
Might be this could help
Tested on Flutter: 2.2.3
在您的项目中复制下面的代码。
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class WidgetSize extends StatefulWidget {
final Widget child;
final Function onChange;
const WidgetSize({
Key? key,
required this.onChange,
required this.child,
}) : super(key: key);
@override
_WidgetSizeState createState() => _WidgetSizeState();
}
class _WidgetSizeState extends State<WidgetSize> {
@override
Widget build(BuildContext context) {
SchedulerBinding.instance!.addPostFrameCallback(postFrameCallback);
return Container(
key: widgetKey,
child: widget.child,
);
}
var widgetKey = GlobalKey();
var oldSize;
void postFrameCallback(_) {
var context = widgetKey.currentContext;
if (context == null) return;
var newSize = context.size;
if (oldSize == newSize) return;
oldSize = newSize;
widget.onChange(newSize);
}
}
声明一个变量来存储Size
Size mySize = Size.zero;
添加以下代码以获取尺寸:
child: WidgetSize(
onChange: (Size mapSize) {
setState(() {
mySize = mapSize;
print("mySize:" + mySize.toString());
});
},
child: ()
**Credit to @Manuputty**
class OrigChildWH extends StatelessWidget {
final Widget Function(BuildContext context, Size size, Widget? child) builder;
final Widget? child;
const XRChildWH({
Key? key,
required this.builder,
this.child,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return OrientationBuilder(builder: (context, orientation) {
return ChildSizeNotifier(builder: builder);
});
}
}
class ChildSizeNotifier extends StatelessWidget {
final ValueNotifier<Size> notifier = ValueNotifier(const Size(0, 0));
final Widget Function(BuildContext context, Size size, Widget? child) builder;
final Widget? child;
ChildSizeNotifier({
Key? key,
required this.builder,
this.child,
}) : super(key: key);
@override
Widget build(BuildContext context) {
WidgetsBinding.instance!.addPostFrameCallback(
(_) {
notifier.value = (context.findRenderObject() as RenderBox).size;
},
);
return ValueListenableBuilder(
valueListenable: notifier,
builder: builder,
child: child,
);
}
}
**Simple to use:**
OrigChildWH(
builder: (context, size, child) {
//Your child here: mine:: Container()
return Container()
})