如何 align/render 围绕单个小部件摆动小部件?

How to align/render flutter widgets around a single widget?

基本上这就是我要实现的目标

带有红色 X 的小部件是 小部件,所有小部件都需要围绕它构建

这里的所有小部件都将 'rendered' 动态 这意味着我从某个地方获取它们的信息,所以实现这个我想到了两个选择:

 Widget panelWidget(Panel panel) {
   return Row(
     mainAxisAlignment: MainAxisAlignment.center,
     crossAxisAlignment: CrossAxisAlignment.center,
     children: [
       //WIDGETS ON THE LEFT
       ...panel.attachedPanels
           .where((element) => element.attachedToSide == 3)
           .map(
             (e) => Container(
               height: getH(e.panelHeight),
               width: getW(e.panelWidth),
               decoration:
                   BoxDecoration(border: Border.all(color: Colors.black)),
             ),
           )
           .toList(),
       Column(
         mainAxisAlignment: MainAxisAlignment.center,
         children: [
           //WIDGETS ON TOP
           ...panel.attachedPanels
               .where((element) => element.attachedToSide == 2)
               .map(
                 (e) => Container(
                   height: getH(e.panelHeight),
                   width: getW(e.panelWidth),
                   decoration:
                       BoxDecoration(border: Border.all(color: Colors.black)),
                 ),
               )
               .toList(),
           Center( //THIS IS THE ROOT WIDGET
             child: Container(
               height: getH(panel.panelHeight),
               width: getW(panel.panelWidth),
               decoration:
                   BoxDecoration(border: Border.all(color: Colors.black)),
             ),
           ),
           //WIDGETS ON THE BOTTOM
           ...panel.attachedPanels
               .where((element) => element.attachedToSide == 0)
               .map(
                 (e) => Container(
                   height: getH(e.panelHeight),
                   width: getW(e.panelWidth),
                   decoration:
                       BoxDecoration(border: Border.all(color: Colors.black)),
                 ),
               )
               .toList(),
         ],
       ),
       //WIDGETS ON THE RITGHT
       ...panel.attachedPanels
           .where((element) => element.attachedToSide == 1)
           .map(
             (e) => Container(
               height: getH(e.panelHeight),
               width: getW(e.panelWidth),
               decoration:
                   BoxDecoration(border: Border.all(color: Colors.black)),
             ),
           )
           .toList(),
     ],
   );
 }

请注意逻辑,因为我现在专注于首先解决这个问题,您可以在下面看到根小部件的注释,并且从 4 个侧面围绕它有我需要渲染的小部件列表,在纸上看起来不错,但这是输出,我使用的是 Flutter Web:

如您所见,左右小部件未与根小部件对齐,因为它们遵循行的交叉轴对齐方式。 Flutter 的工作是将这两个小部件放在中心,但是小部件的大小不同,因此会稍微移动整个小部件树的中心点,我尝试了不同的组合,但它们都没有用,任何帮助非常欢迎

使用 Table 小部件,它可以为您完成您想做的事情。

不要考虑在另一个“根”小部件旁边构建一个小部件。从树的角度思考一切,其中一切都具有 parent-child/children 关系。您的每个小部件在树中都处于同一级别,它们只需要正确的父级将它们放在正确的位置。

您在 Table 中使用 Placeholder 所需的布局在以下代码中实现。可以复制粘贴到 DartPad 中。

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: Scaffold(
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Table(
      defaultVerticalAlignment: TableCellVerticalAlignment.middle,
      children: <TableRow>[
        TableRow(
          children: <Widget>[
            const SizedBox(),
            SizedBox(
              height: 100,
              child: Placeholder(color: Colors.white),
            ),
            const SizedBox(),
          ],
        ),
        TableRow(
          children: <Widget>[
            SizedBox(
              height: 100,
              child: Placeholder(color: Colors.white),
            ),
            SizedBox(
              height: 100,
              child: Placeholder(color: Colors.white),
            ),
            SizedBox(
              height: 100,
              child: Placeholder(color: Colors.white),
            ),
          ],
        ),
        TableRow(
          children: <Widget>[
            const SizedBox(),
            SizedBox(
              height: 100,
              child: Placeholder(color: Colors.white),
            ),
            const SizedBox(),
          ],
        ),
      ],
    );
  }
}

结果:


为了适应“动态”布局,对上述代码的更改微不足道。以下调整 MyWidget 以采用可选的顶部、底部、左侧和右侧小部件,并相应地调整所有内容,以便 Table 正常工作。

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: Scaffold(
        body: Center(
          child: MyWidget(
            root: SizedBox(
              height: 100,
              child: Placeholder(color: Colors.white),
            ),
            top: SizedBox(
              height: 100,
              child: Placeholder(color: Colors.white),
            ),
            right: SizedBox(
              height: 100,
              child: Placeholder(color: Colors.white),
            ),
          ),
        ),
      ),
    );
  }
}

class MyWidget extends StatelessWidget {
  final Widget root;
  final Widget? right, left, top, bottom;

  const MyWidget({
    required this.root,
    this.right,
    this.left,
    this.top,
    this.bottom,
  });

  @override
  Widget build(BuildContext context) {
    final List<Widget> middleRow = [
      if(left != null)
        left!,
      root,
      if(right != null)
        right!,
    ];
    
    final List<Widget> topRow = [
      if(left != null)
        const SizedBox(),
      top ?? const SizedBox(),
      if(right != null)
        const SizedBox(),
    ];
    
    final List<Widget> bottomRow = [
      if(left != null)
        const SizedBox(),
      bottom ?? const SizedBox(),
      if(right != null)
        const SizedBox(),
    ];
    
    return Table(
      defaultVerticalAlignment: TableCellVerticalAlignment.middle,
      children: <TableRow>[
        TableRow(
          children: topRow,
        ),
        TableRow(
          children: middleRow,
        ),
        TableRow(
          children: bottomRow,
        ),
      ],
    );
  }
}

或者,如果您想要空 space,其中省略了小部件:

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: Scaffold(
        body: Center(
          child: MyWidget(
            root: SizedBox(
              height: 100,
              child: Placeholder(color: Colors.white),
            ),
            top: SizedBox(
              height: 100,
              child: Placeholder(color: Colors.white),
            ),
            right: SizedBox(
              height: 100,
              child: Placeholder(color: Colors.white),
            ),
          ),
        ),
      ),
    );
  }
}

class MyWidget extends StatelessWidget {
  final Widget root;
  final Widget? right, left, top, bottom;

  const MyWidget({
    required this.root,
    this.right,
    this.left,
    this.top,
    this.bottom,
  });

  @override
  Widget build(BuildContext context) {
    final List<Widget> middleRow = [
      left ?? const SizedBox(),
      root,
      right ?? const SizedBox(),
    ];
    
    final List<Widget> topRow = [
      const SizedBox(),
      top ?? const SizedBox(),
      const SizedBox(),
    ];
    
    final List<Widget> bottomRow = [
      const SizedBox(),
      bottom ?? const SizedBox(),
      const SizedBox(),
    ];
    
    return Table(
      defaultVerticalAlignment: TableCellVerticalAlignment.middle,
      children: <TableRow>[
        TableRow(
          children: topRow,
        ),
        TableRow(
          children: middleRow,
        ),
        TableRow(
          children: bottomRow,
        ),
      ],
    );
  }
}