赞
踩
通过 LayoutBuilder,我们可以在布局过程中拿到父组件传递的约束信息,然后我们可以根据约束信息动态的构建不同的布局。
比如我们实现一个响应式的 Column 组件 ResponsiveColumn,它的功能是当当前可用的宽度小于 200 时,将子组件显示为一列,否则显示为两列。简单来实现一下:
- class ResponsiveColumn extends StatelessWidget {
- const ResponsiveColumn({Key? key, required this.children}) : super(key: key);
-
- final List<Widget> children;
-
- @override
- Widget build(BuildContext context) {
- // 通过 LayoutBuilder 拿到父组件传递的约束,然后判断 maxWidth 是否小于200
- return LayoutBuilder(
- builder: (BuildContext context, BoxConstraints constraints) {
- if (constraints.maxWidth < 200) {
- // 最大宽度小于200,显示单列
- return Column(children: children, mainAxisSize: MainAxisSize.min);
- } else {
- // 大于200,显示双列
- var _children = <Widget>[];
- for (var i = 0; i < children.length; i += 2) {
- if (i + 1 < children.length) {
- _children.add(Row(
- children: [children[i], children[i + 1]],
- mainAxisSize: MainAxisSize.min,
- ));
- } else {
- _children.add(children[i]);
- }
- }
- return Column(children: _children, mainAxisSize: MainAxisSize.min);
- }
- },
- );
- }
- }
-
-
- class LayoutBuilderRoute extends StatelessWidget {
- const LayoutBuilderRoute({Key? key}) : super(key: key);
-
- @override
- Widget build(BuildContext context) {
- var _children = List.filled(6, Text("A"));
- // Column在本示例中在水平方向的最大宽度为屏幕的宽度
- return Column(
- children: [
- // 限制宽度为190,小于 200
- SizedBox(width: 190, child: ResponsiveColumn(children: _children)),
- ResponsiveColumn(children: _children),
- LayoutLogPrint(child:Text("xx")) // 下面介绍
- ],
- );
- }
- }

可以发现 LayoutBuilder 的使用很简单,但是不要小看它,因为它非常实用且重要,它主要有两个使用场景:
为了便于排错,我们封装一个能打印父组件传递给子组件约束的组件:
- class LayoutLogPrint <T>extends StatelessWidget {
- final Widget child;
- final T? tag;
- const LayoutLogPrint({Key? key, required this.child, this.tag}) : super(key: key);
-
- @override
- Widget build(BuildContext context) {
- return LayoutBuilder(builder: (_,constraints){
- // assert在编译release版本时会被去除
- assert(() {
- print('${tag ?? key ?? child}: $constraints');
- return true;
- }());
- return child;
- });
- }
- }

这样,我们就可以使用 LayoutLogPrint 组件树中任意位置的约束信息,比如:
LayoutLogPrint(child:Text("xx"))
控制台输出:
flutter: Text("xx"): BoxConstraints(0.0<=w<=428.0, 0.0<=h<=823.0)
可以看到 Text("xx") 的显示空间最大宽度为 428,最大高度为 823 。
- LayoutBuilder(
- builder: (context,constraints){
- context为父级上下文
- constraints.biggest.height; 获取组件在父组件所能设置的最大高度
- contraints.maxWidth; 获取父组件宽度,高度同理
-
- return 组件
- }
- )
代码示例:
- LayoutBuilder(
- builder: (context,constraints){
- final endHeight=constraints.biggest.height;
-
- return GestureDetector(
- onVerticalDragDown: (text){ //当点击时会获取点击坐标
- print(endHeight);
- print(constraints.maxHeight);
- print(constraints.maxWidth);
- },
- onVerticalDragEnd: (text){
- print(text);
- },
- onVerticalDragCancel: (){
- print("取消");
- },
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.center,
- mainAxisAlignment: MainAxisAlignment.center,
- children: <Widget>[
- Icon(Icons.search,size: 15,),
- GestureDetector(
- child:Text('A') ,
- onTap: (){
- scroll.animateTo(312, duration: Duration(milliseconds: 200), curve: Curves.easeIn);
- },
- ),
- GestureDetector(
- child:Text('B') ,
- onTap: (){
- scroll.animateTo(478, duration: Duration(milliseconds: 200), curve: Curves.easeIn);
- },
- ),
- GestureDetector(
- child:Text('C') ,
- onTap: (){
- scroll.animateTo(575, duration: Duration(milliseconds: 200), curve: Curves.easeIn);
- },
- ),
- GestureDetector(
- child:Text('D') ,
- onTap: (){
- scroll.animateTo(741, duration: Duration(milliseconds: 200), curve: Curves.easeIn);
- },
- ),
- Text('E'),
- Text('F'),
- Text('G'),
- Text('H'),
- Text('I'),
- Text('J'),
- Text('K'),
- Text('L'),
- Text('M'),
- Text('N'),
- Text('O'),
- Text('P'),
- Text('Q'),
- Text('R'),
- Text('S'),
- Text('T'),
- Text('U'),
- Text('V'),
- Text('W'),
- Text('X'),
- Text("Y"),
- Text("#")
- ],
- ),
- );
- },
- )

本文章的效果如下:
1 核心代码是通过 LayoutBuilder来获取父窗口尺寸
- LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
-
- double maxWidth = constraints.maxWidth;
- double minWidth = constraints.minWidth;
-
- double maxHeight = constraints.maxHeight;
- double minHeight = constraints.minHeight;
- if(maxWidth>400) {
- return buildRow();
- }
- return buildColumn();
-
- },)
2 全部代码实现
- class LayoutBuilderPage extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- body: Center(
- child: LayoutBuilder(
- builder: (BuildContext context, BoxConstraints constraints) {
- double maxWidth = constraints.maxWidth;
- double minWidth = constraints.minWidth;
-
- double maxHeight = constraints.maxHeight;
- double minHeight = constraints.minHeight;
- if (maxWidth > 400) {
- return buildRow();
- }
- return buildColumn();
- },
- ),
- ),
- );
- }
-
- Widget buildRow() {
- return Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- ElevatedButton(onPressed: () {}, child: Text("右侧按钮")),
- SizedBox(
- width: 60,
- ),
- ElevatedButton(onPressed: () {}, child: Text("左侧按钮")),
- ],
- );
- }
-
- Widget buildColumn() {
- return Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- ElevatedButton(onPressed: () {}, child: Text("右侧按钮")),
- SizedBox(
- height: 60,
- ),
- ElevatedButton(onPressed: () {}, child: Text("左侧按钮")),
- ],
- );
- }
- }

- return LayoutBuilder(builder: (ctx, constraints) {
- /// 这里返回出去的Widget,如果超出显示大小,一旦刷新时,这里就会频繁build。
- /// 解决方案:
- /// 第一次Build的时候,把constraints,保存为成员变量。
- /// 第二次不再通过LayoutBuilder获取constraints。
- });
解决方案:
- return LayoutBuilder(builder: (ctx, constraints) {
- this.constraints = constraints;
- return Container(color:Colors.black);
- });
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。