Flutter(二):Row、Column 布局

MaterialApp

对于 MaterialApp,组件提供了一些默认的属性,如AppBar标题背景颜色等,你可以默认使用它们

dart 复制代码
import 'package:flutter/material.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    /**
     * MaterialApp
     */
    return MaterialApp(
      title: 'Flutter layout demo',
      home: Scaffold(
        appBar: AppBar(
          title: const Text('标题'),
        ),
        body: const Center(
          child: Text('Hello World'),
        ),
      ),
    );
  }
}

非 MaterialApp

默认情况下,非 MaterialApp 不包含 AppBar标题背景颜色,如希望实现这些功能,则必须手动构建它们

dart 复制代码
import 'package:flutter/material.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    /**
     * 非MaterialApp
     */
    return Container(
        decoration: const BoxDecoration(color: Colors.grey),
        child: const Center(
          child: Text(
            'Hello Lee!',
            textDirection: TextDirection.ltr,
            style: TextStyle(
              fontSize: 32,
              color: Colors.black87,
            ),
          ),
        ));
  }
}

Row、Column 布局

实现 Select 组件

Select组件(lib/components/select_widget.dart)

dart 复制代码
import 'package:flutter/material.dart';

// 定义Option
class SelectOption {
  String label;
  Object value;

  SelectOption({required this.label, required this.value});
}

// Select组件
class SelectWidget extends StatefulWidget {
  final String title;
  late final List<SelectOption> options;
  final ValueChanged<SelectOption> onChange; // 监听选择

  SelectWidget({super.key, required this.title, required List<SelectOption> options, required this.onChange}) {
    this.options = options.isNotEmpty ? options : [SelectOption(label: '', value: '')];
  }

  @override
  State<SelectWidget> createState() => _SelectWidgetState();
}

class _SelectWidgetState extends State<SelectWidget> {
  int current = 0;

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 50,
      padding: const EdgeInsets.only(top: 5, bottom: 5),
      decoration: BoxDecoration(color: Colors.cyan, border: Border.all(width: double.minPositive, color: Colors.black)),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(widget.title, style: const TextStyle(color: Colors.white, fontSize: 12)),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              SizedBox(
                width: 24,
                height: 24,
                child: IconButton(
                  padding: const EdgeInsets.all(0),
                  color: Colors.white,
                  highlightColor: Colors.transparent,
                  splashColor: Colors.transparent,
                  icon: const Icon(Icons.arrow_left),
                  onPressed: _prev,
                ),
              ),
              Center(
                child: Text(
                  widget.options[current].label,
                  style: const TextStyle(color: Colors.yellow, fontSize: 12),
                ),
              ),
              SizedBox(
                width: 24,
                height: 24,
                child: IconButton(
                  padding: const EdgeInsets.all(0),
                  color: Colors.white,
                  highlightColor: Colors.transparent,
                  splashColor: Colors.transparent,
                  icon: const Icon(Icons.arrow_right),
                  onPressed: _next,
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }

  // 上一个option
  _prev() {
    setState(() {
      if (current > 0) {
        current--;
      } else {
        current = widget.options.length - 1;
      }
      widget.onChange(widget.options[current]);
    });
  }

  // 下一个option
  _next() {
    setState(() {
      if (current < widget.options.length - 1) {
        current++;
      } else {
        current = 0;
      }
      widget.onChange(widget.options[current]);
    });
  }
}

引用页面(lib/views/SelectPage.dart)

dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter_app/components/select_widget.dart';

class SelectPage extends StatefulWidget {
  const SelectPage({super.key});

  @override
  State<SelectPage> createState() => _SelectPage();
}

class _SelectPage extends State<SelectPage> {
  String text = '天津 - TianJin';
  List<SelectOption> addressList = [
    SelectOption(label: '天津', value: 'TianJin'),
    SelectOption(label: '北京', value: 'BeiJing'),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Select')),
      body: Column(
        children: [
          Container(
            margin: const EdgeInsets.all(10),
            height: 51,
            child: SelectWidget(
              title: '区域',
              options: addressList,
              onChange: (SelectOption option) {
                setState(() {
                  text = "${option.label} - ${option.value}";
                  print(text);
                });
              },
            ),
          ),
          Text(text)
        ],
      ),
    );
  }
}

使用Select实现切换RowColumn属性

布局页面(lib/views/layout.dart)

dart 复制代码
import 'package:flutter/material.dart';

import '../components/select_widget.dart';

enum LayoutType { row, column }

enum ContentType { container, text }

class Layout extends StatefulWidget {
  const Layout({super.key});

  @override
  State<Layout> createState() => _Layout();
}

class _Layout extends State<Layout> {
  ContentType contentValue = ContentType.container;
  List<SelectOption> contentList = <SelectOption>[
    SelectOption(label: 'Container', value: ContentType.container),
    SelectOption(label: 'Text', value: ContentType.text),
  ];

  LayoutType layoutValue = LayoutType.row;
  List<SelectOption> layoutList = <SelectOption>[
    SelectOption(label: 'Row', value: LayoutType.row),
    SelectOption(label: 'Column', value: LayoutType.column),
  ];

  MainAxisAlignment mainAxisAlignmentValue = MainAxisAlignment.start;
  List<SelectOption> mainAxisAlignmentList = <SelectOption>[
    SelectOption(label: 'start', value: MainAxisAlignment.start),
    SelectOption(label: 'end', value: MainAxisAlignment.end),
    SelectOption(label: 'center', value: MainAxisAlignment.center),
    SelectOption(label: 'spaceBetween', value: MainAxisAlignment.spaceBetween),
    SelectOption(label: 'spaceEvenly', value: MainAxisAlignment.spaceEvenly),
    SelectOption(label: 'spaceAround', value: MainAxisAlignment.spaceAround),
  ];

  MainAxisSize mainAxisSizeValue = MainAxisSize.min;
  List<SelectOption> mainAxisSizeList = <SelectOption>[
    SelectOption(label: 'min', value: MainAxisSize.min),
    SelectOption(label: 'max', value: MainAxisSize.max),
  ];

  CrossAxisAlignment crossAxisAlignmentValue = CrossAxisAlignment.start;
  List<SelectOption> crossAxisAlignmentList = <SelectOption>[
    SelectOption(label: 'start', value: CrossAxisAlignment.start),
    SelectOption(label: 'end', value: CrossAxisAlignment.end),
    SelectOption(label: 'center', value: CrossAxisAlignment.center),
    SelectOption(label: 'stretch', value: CrossAxisAlignment.stretch),
    SelectOption(label: 'baseline', value: CrossAxisAlignment.baseline),
  ];

  TextDirection textDirectionValue = TextDirection.ltr;
  List<SelectOption> textDirectionList = <SelectOption>[
    SelectOption(label: 'ltr', value: TextDirection.ltr),
    SelectOption(label: 'rtl', value: TextDirection.rtl),
  ];

  VerticalDirection verticalDirectionValue = VerticalDirection.up;
  List<SelectOption> verticalDirectionList = <SelectOption>[
    SelectOption(label: 'up', value: VerticalDirection.up),
    SelectOption(label: 'down', value: VerticalDirection.down),
  ];

  TextBaseline textBaselineValue = TextBaseline.alphabetic;
  List<SelectOption> textBaselineList = <SelectOption>[
    SelectOption(label: 'alphabetic', value: TextBaseline.alphabetic),
    SelectOption(label: 'ideographic', value: TextBaseline.ideographic),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: PreferredSize(
        preferredSize: const Size.fromHeight(150),
        child: Column(
          children: [
            // 属性
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Expanded(
                  flex: 1,
                  child: SelectWidget(
                    title: 'ContentType',
                    options: contentList,
                    onChange: (SelectOption selectOption) {
                      setState(() {
                        contentValue = selectOption.value as ContentType;
                      });
                    },
                  ),
                ),
                Expanded(
                  flex: 1,
                  child: SelectWidget(
                    title: 'Layout',
                    options: layoutList,
                    onChange: (SelectOption selectOption) {
                      setState(() {
                        layoutValue = selectOption.value as LayoutType;
                      });
                    },
                  ),
                ),
              ],
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Expanded(
                  flex: 1,
                  child: SelectWidget(
                    title: 'MainAxisAlignment',
                    options: mainAxisAlignmentList,
                    onChange: (SelectOption selectOption) {
                      setState(() {
                        mainAxisAlignmentValue = selectOption.value as MainAxisAlignment;
                      });
                    },
                  ),
                ),
                Expanded(
                  flex: 1,
                  child: SelectWidget(
                    title: 'MainAxisSize',
                    options: mainAxisSizeList,
                    onChange: (SelectOption selectOption) {
                      setState(() {
                        mainAxisSizeValue = selectOption.value as MainAxisSize;
                      });
                    },
                  ),
                ),
                Expanded(
                  flex: 1,
                  child: SelectWidget(
                    title: 'CrossAxisAlignment',
                    options: crossAxisAlignmentList,
                    onChange: (SelectOption selectOption) {
                      setState(() {
                        crossAxisAlignmentValue = selectOption.value as CrossAxisAlignment;
                      });
                    },
                  ),
                ),
              ],
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Expanded(
                  flex: 1,
                  child: SelectWidget(
                    title: 'TextDirection',
                    options: textDirectionList,
                    onChange: (SelectOption selectOption) {
                      setState(() {
                        textDirectionValue = selectOption.value as TextDirection;
                      });
                    },
                  ),
                ),
                Expanded(
                  flex: 1,
                  child: SelectWidget(
                    title: 'VerticalDirection',
                    options: verticalDirectionList,
                    onChange: (SelectOption selectOption) {
                      setState(() {
                        verticalDirectionValue = selectOption.value as VerticalDirection;
                      });
                    },
                  ),
                ),
                Expanded(
                  flex: 1,
                  child: SelectWidget(
                    title: 'TextBaseline',
                    options: textBaselineList,
                    onChange: (SelectOption selectOption) {
                      setState(() {
                        textBaselineValue = selectOption.value as TextBaseline;
                      });
                    },
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
      body: Container(
        margin: const EdgeInsets.all(20),
        decoration: BoxDecoration(color: Colors.black12, border: Border.all(width: 1, color: Colors.grey)),
        child: layoutValue == LayoutType.row
            ? Row(
                mainAxisAlignment: mainAxisAlignmentValue,
                mainAxisSize: mainAxisSizeValue,
                crossAxisAlignment: crossAxisAlignmentValue,
                textDirection: textDirectionValue,
                verticalDirection: verticalDirectionValue,
                textBaseline: textBaselineValue,
                children: contentValue == ContentType.container
                    ? [
                        Container(color: Colors.red, width: 100, height: 70),
                        Container(color: Colors.green, width: 60, height: 90),
                        Container(color: Colors.blue, width: 80, height: 50),
                      ]
                    : [
                        const Text('Red', style: TextStyle(fontSize: 20, backgroundColor: Colors.red)),
                        const Text('Green', style: TextStyle(fontSize: 50, backgroundColor: Colors.green)),
                        const Text('Blue', style: TextStyle(fontSize: 30, backgroundColor: Colors.blue)),
                      ],
              )
            : Column(
                mainAxisAlignment: mainAxisAlignmentValue,
                mainAxisSize: mainAxisSizeValue,
                crossAxisAlignment: crossAxisAlignmentValue,
                textDirection: textDirectionValue,
                verticalDirection: verticalDirectionValue,
                textBaseline: textBaselineValue,
                children: contentValue == ContentType.container
                    ? [
                        Container(color: Colors.red, width: 100, height: 70),
                        Container(color: Colors.green, width: 60, height: 90),
                        Container(color: Colors.blue, width: 80, height: 50),
                      ]
                    : [
                        const Text('Red', style: TextStyle(fontSize: 20, backgroundColor: Colors.red)),
                        const Text('Green', style: TextStyle(fontSize: 50, backgroundColor: Colors.green)),
                        const Text('Blue', style: TextStyle(fontSize: 30, backgroundColor: Colors.blue)),
                      ],
              ),
      ),
    );
  }
}

页面入口(lib/main.dart)

dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter_app/views/layout.dart';

void main() {
  runApp(const App());
}

class App extends StatefulWidget {
  const App({super.key});

  @override
  State<App> createState() => _AppState();
}

class _AppState extends State<App> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Row、Column 布局')),
        body: const Layout(),
      ),
    );
  }
}
相关推荐
Ajiang28247353041 小时前
对于C++中stack和queue的认识以及priority_queue的模拟实现
开发语言·c++
幽兰的天空1 小时前
Python 中的模式匹配:深入了解 match 语句
开发语言·python
Theodore_10224 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
----云烟----6 小时前
QT中QString类的各种使用
开发语言·qt
lsx2024066 小时前
SQL SELECT 语句:基础与进阶应用
开发语言
开心工作室_kaic6 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
向宇it6 小时前
【unity小技巧】unity 什么是反射?反射的作用?反射的使用场景?反射的缺点?常用的反射操作?反射常见示例
开发语言·游戏·unity·c#·游戏引擎
武子康7 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
转世成为计算机大神7 小时前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
宅小海7 小时前
scala String
大数据·开发语言·scala