Flutter 自定义布局,根据其子级决定小部件的大小

Gaz*_*kus 7 layout dart flutter flutter-layout

理解制约文章是理解的布局是如何工作非常有用的。但是,我无法创建按照那里解释的方式工作的自定义布局。

文章说size是向上流动的,首先我们得到child的size,然后我们可以根据children的size来决定我们自己的size。

这似乎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());
}

Run Code Online (Sandbox Code Playgroud)

有人建议我可以用Align. 我在这里尝试过,但Align长大了。Align'swidthFactorheightFactor利用孩子的大小,但不能以这种方式使其成为正方形。去除蓝色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());
}

Run Code Online (Sandbox Code Playgroud)

在这里,我试图用它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());
}

Run Code Online (Sandbox Code Playgroud)

在这里,我正在尝试进行自定义布局,但我无法使用孩子的尺寸来决定我的尺寸。在收到我孩子的尺寸之前,我必须决定我的尺寸。一旦我知道我孩子的大小,试图强制重新布局会引发异常,这是在代码的末尾。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

 */
Run Code Online (Sandbox Code Playgroud)

Sim*_*mon 8

这应该可以解决您的问题。我们通过使用UnconstrainedBox. 我们只需要删除垂直,以便AspectRatio使用宽度约束。然后我们要求子树根据其内在大小来调整大小。当然,这现在很容易溢出,因为它永远不会换行并始终使用其固有宽度。但是,您可以将UnconstrainedBoxa包装起来FittedBox,它永远不会溢出,而是缩放以适应可能小于其内在尺寸的父级。您可能还想尝试将轴参数设置UnconstrainedBox为水平。然后 IntrinsicWidth 将被限制为父宽度。

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: UnconstrainedBox(
            child: IntrinsicWidth(
              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());
}

Run Code Online (Sandbox Code Playgroud)