【问题标题】:How to align/render flutter widgets around a single widget?如何围绕单个小部件对齐/渲染颤振小部件?
【发布时间】:2021-10-12 02:24:05
【问题描述】:

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

以红色 X 为 小部件,所有小部件都需要围绕它构建

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

  • 使用 Stack 小部件或其他我还没有找到的神秘布局小部件
  • 使用会导致问题的简单列和行: 这是我正在使用的代码
 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 :

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

【问题讨论】:

    标签: flutter flutter-layout flutter-web


    【解决方案1】:

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

    不要想在另一个“根”小部件旁边构建一个小部件。用一棵树来思考所有事物,其中所有事物都具有父子关系。您的每个小部件都在树中的同一级别,它们只需要正确的父级将它们放在正确的位置。

    Table 中使用Placeholders 所需的布局在以下代码中实现。它可以复制粘贴到 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,
            ),
          ],
        );
      }
    }
    

    或者,如果您想要省略小部件的空白空间:

    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,
            ),
          ],
        );
      }
    }
    

    【讨论】:

    • 感谢您的回答,它回答了一些问题,但是由于我正在动态获取“行”,我无法保证或知道表格必须在一行中有多少小部件每行有相同数量的小部件
    • @ExtremeGeek 为什么这是个问题?只需根据需要删除/添加行/列。没有什么可以阻止您一次只使用此布局的一部分。
    • 在您的示例中,您正在用SizedBox() 填充空单元格我不能这样做,因为一切都是动态的,因此我的问题还有其他解决方案吗?因为使用表格限制了我以后必须做的自定义,网格在这里有用吗?
    • @ExtremeGeek 用没有大小的东西填充空单元格有什么问题?目前尚不清楚您所说的“动态”是什么意思。 Table 一个网格。它与GridView 相同,只是没有滚动。
    • 问题是我不知道我需要使用多少个SizedBox()s 来填充空格,除非我添加更多代码只是为了检查我总共拥有的最大单元格是多少行
    猜你喜欢
    • 2019-02-22
    • 2021-03-13
    • 2021-09-05
    • 2021-06-11
    • 2018-10-18
    • 2019-11-29
    • 2020-11-15
    • 2019-05-23
    • 2019-06-06
    相关资源
    最近更新 更多