Flutter 布局实例 - 静态布局构建实例
以下是 Flutter
官方网站构建布局的例子, 最终创建以下的布局页面:
新建 Flutter
项目
安装和配置好 Flutter
开发环境之后, 完成以下的步骤:
创建一个新的
Flutter
应用项目中新建文件
lib\views\home_view.dart
, 编辑内容如下:
1 |
- 编辑
lib/main.dart
文件的内容如下:
1 |
- 在
Android
模拟器中调试运行项目
对布局进行图形分解
第一步需要将布局分解成它的各个基础元素:
识别出它的行和列。
这个布局是否包含网格布局?
是否有重叠的元素?
界面是否需要选项卡?
留意需要对齐、内间距、或者边界的区域。
首先,识别出稍大的元素。在这个例子中,四个元素排成一列:一个图像,两个行区域,和一个文本区域。
接着,对每一行进行图解。
第一行
: 图片显示区域
第二行
: 也就是标题区域, 有三个子元素:一个文本列, 一个星形图标, 和一个数字。它的第一个子元素,文本列,包含两行文本。第一列占据大量空间,因此它应当被封装在一个 Expanded widget
当中。
第三行
: 也就是按钮区域, 同样有三个子元素:每个子元素是一个包含图标和文本的列。
第四行
: 文本区域, 显示长段文本内容
一旦图解好布局,采取自下而上的方法来实现它就变得尤为轻松了。为了最大程度减少,深层嵌套的布局代码带来的视觉混乱,需要用一些变量和函数来替代某些实现。
实现图片区域
图片组件是Flutter基础组件之一,和文本组件一样必不可少,
使用网络图片
1 | Image.network( |
使用本地图片
更新 pubspec.yaml
文件,添加一个 assets
标签。这使得在你的代码中可以访问到该图片。
1 | flutter: |
请注意,
pubspec.yaml
区分大小写,因此请编写如上所示的assets
: 和图像URL
。
pubspec
文件对空格
也很敏感
,因此请使用适当的缩进。
你可能需要重新启动
正在运行的程序(在模拟器或连接的设备上)才能使 pubspec
更改生效。
1 | body: Column( |
实现标题行
首先,构建标题部分左侧列。添加如下代码到 HomeView
类的 build()
(与 return Scaffold();
并列)方法内顶部。
1 | Widget titleSection = Container( |
将
Column
元素放到Expanded widget
中可以拉伸该列,以利用该行中所有剩余的闲置空间。设置crossAxisAlignment
属性值为CrossAxisAlignment.start
,这会将该列放置在行的起始位置。
将第一行文本放入
Container
容器中使得你可以增加内间距。列中的第二个子元素,同样为文本,显示为灰色。
标题行中的最后两项是一个红色星形图标,和文字
41
。整行都在一个Container
容器布局中,而且每条边都有32
像素的内间距。
添加标题部分到body
中, 修改home_view.dart
中的部分代码如下:
1 | return MaterialApp( |
实现按钮行
按钮区域包含三列使用相同布局-一行文本上面一个图标。此行的各列被等间隙放置,文本和图标被着以初始色。
由于构建每列的代码基本相同,因此可以创建一个名为 _buildButtonColumn()
的私有辅助函数,以颜色、图标和文本为入参,返回一个以指定颜色绘制自身 widgets
的一个 column
列对象。
1 | class HomeView extends StatelessWidget { |
这个函数直接将图标添加到这列里。文本在以一个仅有上间距的 Container
容器中,使得文本与图标分隔开。
通过调用函数并传递针对某列的颜色
,Icon
图标和文本
,来构建包含这些列的行。然后在行的主轴方向通过使用 MainAxisAlignment.spaceEvenly
,将剩余的空间均分到每列各自的前后及中间。只需在 build()
方法中的 titleSection
声明下添加如下代码:
1 | Color color = Theme.of(context).primaryColor; |
添加按钮部分到 body
属性中去:
1 | body: Column( |
实现文本区域
将文本区域定义为一个变量,将文本放置到一个 Container
容器中,然后为每条边添加内边距。只需在 buttonSection
声明的下方
添加如下代码:
1 | Widget textSection = const Padding( |
通过设置 softwrap
为 true
,文本将在填充满列宽后在单词边界处自动换行
。
添加文本部分到 body
属性:
1 | body: Column( |
使用 ListView
在最后的步骤中,需要在一个 ListView
中排列好所有的元素,而不是在一个 Column
中,因为当 app
运行在某个小设备上或当前显示内容超出屏幕高度时,ListView
支持 app body
的滚动。
1 | body: ListView( |
加入交互体验
如何修改您的应用程序以使其对用户输入做出反应?在本教程中,您将为仅包含非交互式 widget
的应用程序添加交互性。具体来说,您将通过创建一个管理两个无状态 widget
的自定义有状态 widget
,修改一个图标实现使其可点击.
当应用第一次启动时,这个星形图标是实心红色,表明这个湖以前已经被收藏过了。星号旁边的数字表示 41
个人已经收藏了此湖。完成本教程后,点击
星形图标将取消
收藏状态,然后用轮廓线
的星形图标代替实心
的,并减少
计数。再次点击会重新
收藏,并增加
计数。
为了实现这个,您将创建一个包含星形图标和计数的自定义 widget
,它们都是 widget
。因为点击星形图标会更改这两个 widget
的状态,所以同一个 widget
应该同时管理这两个 widget
。
有状态和无状态的 widgets
有些 widgets
是有状态的, 有些是无状态的。如果用户与 widget
交互,widget
会发生变化,那么它就是 有状态
的。
无状态
的 widget
自身无法改变。 Icon
、IconButton
和 Text
都是无状态 widget
,它们都是 StatelessWidget
的子类。
而 有状态
的 widget
自身是可动态改变的(基于State
)。例如,可以通过与用户的交互
或是随着数据
的改变
而导致外观形态
的变化。 Checkbox
、Radio
、Slider
、 InkWell
、Form
和 TextField
都是有状态 widget
,它们都是 StatefulWidget
的子类。
一个 widget
的状态保存在一个 State
对象中,它和 widget
的显示分离。 Widget
的状态是一些可以更改的值,如一个滑动条的当前值或一个复选框是否被选中。当 widget
状态改变时,State
对象调用 setState()
,告诉框架去重绘 widget
。