Flutter布局构建(一)

Flutter中的布局

在Flutter中,布局的核心机制是widgets,一切事物都是由widgets构成。

以下是UI的一个widgets树形图
Widgets树形图
对于widgets中,里面有两个元素最为重要:

  • 一个是child属性,如果只包含一个子项,例如CenterContainer
  • 一个是children属性,如果包含多个子项,例如RowColumnListView

如果我们想要将自己的UI widgets实现,那么就需要继承Flutter所提供的widgets,当然,大都数widgets都会提供一个build()方法

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

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

当然,你可以使用不同的类型,例如非MaterialApp,也是使用同样的方法进行构建。

横向或纵向布局多个widgets

我们最常见的一种布局就是垂直或水平的widgets,Row自然是横向,Column就是纵向。

对于航向和纵向而言,其内部都可以有子widgets列表,因此可以有多种组合。

示例
就比如上述图片中所表现的:

  • 总体上是由一个大的Row横向widget构成
  • 内部由一个子widget:ColumnImage widget构成
  • 左边的Column中又可以分为多个子widgets

left column
那么,我们现在就依照这一种布局模式,来详细解析。

第一步,构建大框架

所谓的大框架,那么就是上述所说的,左边是Row widget,右边是Image widget

Scaffold(
	appBar: AppBar(
		title: Text(title),
	),
	body: Center(
		child: Container(
        	margin: const EdgeInsets.fromLTRB(0, 40, 0, 30),
        	height: 600,
        	child: Card(
            	child: Row(
            		crossAxisAlignment: CrossAxisAlignment.start,
              		children: [
                		SizedBox(
                  			width: 440,
                  			child: leftColumn,
                		),
                		mainImage,
              		],
            	),
          	),
        ),
	),
);
  • Scaffold是Flutter中的一个基本widgets,可以提供AppBar(顶部)、底部的导航栏、抽屉选单等。
  • body属性就是我们需要设置的主要区域,可以看见,在Center中我们主要有两个childSizedBoxmainImage
  • 使用Card widgets的原因是,可以把展示内容和图片以卡片的形式展现出来
第二步,解析子widgets
解析mainImage

mainImage实际上就是右边的Image widgets:

final mainImage = Image.asset(
    './images/pavlova.jpg',
	fit: BoxFit.cover,
);
解析leftColumn

leftColumn实际上是一个自定义的子widgets

final leftColumn = Container(
	padding: const EdgeInsets.fromLTRB(20, 30, 20, 20),
    child: Column(
    	children: [
          	titleText,
          	subTitle,
          	ratings,
          	iconList,
    	],
   	),
);

// 左边的标题
const titleText = Padding(
	padding: EdgeInsets.all(20),
 	child: Text(
    	'Strawberry Pavlova',
    	style: TextStyle(
          	fontWeight: FontWeight.w800,
          	letterSpacing: 0.5,
          	fontSize: 30,
        ),
    ),
);

const subTitle = Text(
	'Pavlova is a meringue-based dessert named after the Russian ballerina '
    'Anna Pavlova. Pavlova features a crisp crust and soft, light inside, '
    'topped with fruit and whipped cream.',
    textAlign: TextAlign.center,
    style: TextStyle(
        fontFamily: 'Georgia',
        fontSize: 25,
    ),
);

// 类似于打评分的星
var stars = Row(
	mainAxisSize: MainAxisSize.min,
    children: [
    	Icon(Icons.star, color: Color.fromRGBO(0, 255, 0, 0.5)),
        Icon(Icons.star, color: Color.fromRGBO(0, 255, 0, 0.5)),
        Icon(Icons.star, color: Color.fromRGBO(0, 255, 0, 0.5)),
        const Icon(Icons.star, color: Colors.black),
        const Icon(Icons.star, color: Colors.black),
    ],
);

final ratings = Container(
	padding: const EdgeInsets.all(20),
    child: Row(
    	mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          	stars, 
          	const Text(
            	'170 Reviews',
            	style: TextStyle(
              		color: Colors.black,
              		fontWeight: FontWeight.w800,
              		fontFamily: 'Roboto',
              		letterSpacing: 0.5,
              		fontSize: 20,
            	),
          	)
        ],
    ),
);

final iconList = DefaultTextStyle.merge(
	style: descTextStyle,
    child: Container(
    	padding: const EdgeInsets.all(20),
        child: Row(
          	mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          	children: [
            	Column(
              		children: [
                		Icon(Icons.kitchen, color: Colors.green[500]),
                		const Text('PREP:'),
                		const Text('25 min'),
              		],
            	),
            	Column(
              		children: [
                		Icon(Icons.timer, color: Colors.green[500]),
                		const Text('COOK:'),
                		const Text('1 hr'),
              		],
            	),
	            Column(
	              	children: [
	                	Icon(Icons.restaurant, color: Colors.green[500]),
	                	const Text('FEEDS:'),
	                	const Text('4-6'),
	              	],
	            ),
	        ],
        ),
    ),
);
  • 可以看见,左边的子widgets由四个小widgets构成,我们只需要分别实现四个部分,然后并入最后的leftColumn即可。

如下是rating行的构成树形图
rating行的widget树形图
如下是iconList行的构成树形图

iconList行的构成树形图

通用布局widgets

Flutter中一般会将widgets分为两类:标准widgetsMaterial widgets,需要注意的是任何App都可以使用标准widgets,Material组件只能在MaterialApp中使用

标准widgets特征Material特征
Container向widgets中增加padding、margins、borders等装饰Card将相关信息整理到一个有圆角和阴影的盒子中
GridView将widgets展示为一个可滚动的网格ListTile将最多三行的文本、可选的导语以及后面的图标组织在一行中
ListView将widgets展示为一个可滚动的列表
Stack将widgets覆盖在另一个的上面
Container

任何布局都可以任意使用Container

Container
一般地,Container可以修改背景色或者样式图片。

Widget _buildImageColumn() {
	return Container(
      	decoration: const BoxDecoration(
        	color: Colors.black26,
      	),
      	child: Column(
        	children: [
          		_buildImageRow(1),
          		_buildImageRow(3),
        	],
      	),
    );
}

Widget _buildDecoratedImage(int imageIndex) => Expanded(
	child: Container(
    	decoration: BoxDecoration(
    		border: Border.all(width: 10, color: Colors.black38),
        	borderRadius: const BorderRadius.all(Radius.circular(8)),
        ),
        margin: const EdgeInsets.all(4),
        child: Image.asset('images/pic$imageIndex.jpg'),
	),	
);

Widget _buildImageRow(int imageIndex) => Row(
	children: [
    	_buildDecoratedImage(imageIndex),
        _buildDecoratedImage(imageIndex + 1),
    ],
);

Container

  • BoxDecoration widget中的borderRadius属性能够设置圆角效果。
GridView

GridView将widgets作为二维列表展示,GridView提供两个预制的列表,如果检测到内容太长而无法适应渲染盒时,则会自动支持滚动

GridView

ListView

ListView是一个和Column很像的widget,当内容长于自己的渲染盒时,会自动支持滚动

  • 相较于Column
    • 可以水平或者垂直布局
    • 检测空间不足时,会提供滚动
    • 配置更少,使用更容易,并且支持滚动

ListView

Stack

可以使用Stack在基础widget上排列widget

  • 其主要用于覆盖另一个Stack
  • 子列表的第一个widget是基础,后面的子项覆盖在基础widget的顶部
  • Stack的内容是无法滚动的

Stack
CircleAvatar的上面使用Stack覆盖Container(在透明的黑色背景上展示它的 Text)。Stack 使用alignment属性和 Alignment让文本偏移。

Card

Material 库 中的 Card 包含相关有价值的信息,几乎可以由任何 widget 组成,但是通常和 ListTile 一起使用。 Card 只有一个子项,这个子项可以是列、行、列表、网格或者其他支持多个子项的 widget。默认情况下,Card 的大小是 0x0 像素。你可以使用 SizedBox 控制 card 的大小。

在 Flutter 中,Card 有轻微的圆角和阴影来使它具有 3D 效果。改变 Cardelevation 属性可以控制阴影效果。例如,把 elevation 设置为 24,可以从视觉上更多的把 Card 抬离表面,使阴影变得更加分散。关于支持的 elevation 的值的列表,可以查看 Material guidelines 中的 Elevation。使用不支持的值则会使阴影无效。

在这里插入图片描述

ListTile

ListTileMaterial 库 中专用的行widget,它可以很轻松的创建一个包含三行文本以及可选的行前和行尾图标的行。 ListTileCard 或者 ListView 中最常用,但是也可以在别处使用。

ListTile

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值