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(),
      ),
    );
  }
}
相关推荐
玩电脑的辣条哥2 小时前
Python如何播放本地音乐并在web页面播放
开发语言·前端·python
ll7788114 小时前
LeetCode每日精进:20.有效的括号
c语言·开发语言·算法·leetcode·职场和发展
Jackson@ML6 小时前
Python数据可视化简介
开发语言·python·数据可视化
赵琳琅6 小时前
Java语言的云计算
开发语言·后端·golang
lly2024066 小时前
jQuery 杂项方法
开发语言
赵琳琅6 小时前
MDX语言的安全开发
开发语言·后端·golang
开开又心心的学嵌入式7 小时前
C语言——指针进阶应用
c语言·开发语言
开开又心心的学嵌入式7 小时前
C语言——指针基础知识
c语言·开发语言
lonelyhiker7 小时前
javascript的原型链
开发语言·javascript·原型模式
夏梓蕙8 小时前
Elixir语言的软件开发工具
开发语言·后端·golang