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(),
      ),
    );
  }
}
相关推荐
Code out the future10 分钟前
【C++——临时对象,const T&】
开发语言·c++
taoyong00114 分钟前
Java线程核心01-中断线程的理论原理
java·开发语言
一雨方知深秋14 分钟前
智慧商城:封装getters实现动态统计 + 全选反选功能
开发语言·javascript·vue2·foreach·find·every
海威的技术博客17 分钟前
关于JS中的this指向问题
开发语言·javascript·ecmascript
froginwe1143 分钟前
PostgreSQL表达式的类型
开发语言
委婉待续1 小时前
java抽奖系统(八)
java·开发语言·状态模式
deja vu水中芭蕾1 小时前
嵌入式C面试
c语言·开发语言
爱码小白1 小时前
PyQt5 学习方法之悟道
开发语言·qt·学习方法
西猫雷婶1 小时前
python学opencv|读取图像(十六)修改HSV图像HSV值
开发语言·python·opencv
weixin_537590451 小时前
《Java编程入门官方教程》第八章练习答案
java·开发语言·servlet