Flutter学习指南:UI布局和控件(1),移动智能终端开发技术设计实验作品

height: 150.0,
);
}
}

按钮

Flutter 提供了两个基本的按钮控件:FlatButton 和 RaisedButton,它们的使用方法是类似的:

class TestWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
var flatBtn = FlatButton(
onPressed: () => print(‘FlatButton pressed’),
child: Text(‘BUTTON’),
);
var raisedButton = RaisedButton(
onPressed: () => print(‘RaisedButton pressed’),
child: Text(‘BUTTON’),
);
return raisedButton;
}
}

通过设置 onPressed 回调,我们可以在按钮被点击的时候得到回调。child 参数用于设置按钮的内容。虽然我们给 child 传递的是 Text,但这不是必需的,它可以接受任意的 Widget,比方说,Image。

注意,由于我们只是在按钮点击的时候打印一个字符串,这里使用 StatelessWidget 是没有问题的。但如果有其他 UI 动作(比如弹出一个 dialog,则必须使用 StatefulWidget)。

它们的区别只是样式不同而已的:

FlatButton:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

flat-button

RaiseButton:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

raised-button

文本输入框

Flutter 的文本输入框叫 TextField。为了获取用户输入的文本,我们需要给他设置一个 controller。通过这个 controller,就可以拿到文本框里的内容:

class MessageForm extends StatefulWidget {
@override
State createState() {
return _MessageFormState();
}
}

class _MessageFormState extends State {
var editController = TextEditingController();

@override
Widget build(BuildContext context) {
// Row、Expand 都是用于布局的控件,这里可以先忽略它们
return Row(
children: [
Expanded(
child: TextField(
controller: editController,
),
),
RaisedButton(
child: Text(“click”),
onPressed: () => print(‘text inputted: ${editController.text}’),
)
],
);
}

@override
void dispose() {
super.dispose();
// 手动调用 controller 的 dispose 方法以释放资源
editController.dispose();
}
}

显示弹框

在前面的 TextField 例子中,我们只是把用户的输入通过 print 打印出来,这未免也太无趣了。在这一小节,我们要把它显示在 dialog 里。为了弹出一个 dialog,我们需要调用 showDialog 方法并传递一个 builder:

class _MessageFormState extends State {
var editController = TextEditingController();

@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: TextField(
controller: editController,
),
),
RaisedButton(
child: Text(“click”),
onPressed: () {
showDialog(
// 第一个 context 是参数名,第二个 context 是 State 的成员变量
context: context,
builder: (_) {
return AlertDialog(
// dialog 的内容
content: Text(editController.text),
// actions 设置 dialog 的按钮
actions: [
FlatButton(
child: Text(‘OK’),
// 用户点击按钮后,关闭弹框
onPressed: () => Navigator.pop(context),
)
],
);
}
);
}
)
],
);
}

@override
void dispose() {
super.dispose();
editController.dispose();
}
}

最简单的布局——Container、Padding 和 Center:

我们经常说,Flutter 里面所有的东西都是 Widget,所以,布局也是 Widget。

控件 Container 可以让我们设置一个控件的尺寸、背景、margin 等:

class TestWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Text(‘text’),
padding: EdgeInsets.all(8.0),
margin: EdgeInsets.all(4.0),
width: 80.0,
decoration: BoxDecoration(
// 背景色
color: Colors.grey,
// 圆角
borderRadius: BorderRadius.circular(5.0),
),
);
}
}

如果我们只需要 padding,可以使用控件 Padding:

class TestWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(8.0),
child: Text(‘text’),
);
}
}

Center 就跟它的名字一样,把一个控件放在中间:

class TestWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(8.0),
margin: EdgeInsets.all(4.0),
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
// 背景色
color: Colors.grey,
// 圆角
borderRadius: BorderRadius.circular(5.0),
),

// 把文本放在 Container 的中间
child: Center(
child: Text(‘text’),
),
);
}
}

水平、竖直布局和 Expand

我们经常说,Flutter 里面所有的东西都是 Widget,所以,布局也是 Widget。水平布局我们可以使用 Row,竖直布局使用 Column。

class TestWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
// 只有一个子元素的 widget,一般使用 child 参数来设置;Row 可以包含多个子控件,
// 对应的则是 children。
children: [
Text(‘text1’),
Text(‘text2’),
Text(‘text3’),
Text(‘text4’),
],
);
}
}

Column 的使用是一样的:

class TestWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(‘text1’),
Text(‘text2’),
Text(‘text3’),
Text(‘text4’),
],
);
}
}

关于 Expand 控件,我们来看看 TextField 的那个例子:

class MessageForm extends StatefulWidget {
@override
State createState() {
return _MessageFormState();
}
}

class _MessageFormState extends State {
var editController = TextEditingController();

@override
Widget build(BuildContext context) {
return Row(
children: [
// 占满一行里除 RaisedButton 外的所有空间
Expanded(
child: TextField(
controller: editController,
),
),
RaisedButton(
child: Text(“click”),
onPressed: () => print(‘text inputted: ${editController.text}’),
)
],
);
}

@override
void dispose() {
super.dispose();
editController.dispose();
}
}

这里通过使用 Expand,TextField 才能够占满一行里除按钮外的所有空间。此外,当一行/列里有多个 Expand 时,我们还可以通过设置它的 flex 参数,在多个 Expand 之间按比例划分可用空间。

class TestWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
// 占一行的 2/3
flex: 2,
child: RaisedButton(child: Text(‘btn1’),),
),
Expanded(
// 占一行的 1/3
flex: 1,
child: RaisedButton(child: Text(‘btn2’),),
),
],
);
}
}

Stack 布局

有些时候,我们可能会希望一个控件叠在另一个控件的上面。于是,Stack 应运而生:

class TestWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Stack(
children: [
Text(‘foobar’),
Text(‘barfoo’),
],
);
}
}

默认情况下,子控件都按 Stack 的左上角对齐,于是,上面的两个文本完全一上一下堆叠在一起。我们还可以通过设置 alignment 参数来改变这个对齐的位置:

class TestWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Stack(
// Aligment 的取值范围为 [-1, 1],Stack 中心为 (0, 0),
// 这里设置为 (-0.5, -0.5) 后,可以让文本对齐到 Container 的 1/4 处
alignment: const Alignment(-0.5, -0.5),
children: [
Container(
width: 200.0,
height: 200.0,
color: Colors.blue,
),
Text(‘foobar’),
],
);
}
}

效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

screenshot-stack

通过组合 Row/Column 和 Stack,已经能够完成绝大部分的布局了,所以 Flutter 里没有相对布局之类的东西。更多的 Flutter 控件,读者可以参考 flutter.io/widgets/

示例一

在这一节里,我们综合前面所学的知识,来实现下面这个界面。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

lakes-diagram

展示图片

  1. 把图片 lake 放到项目根目录的 images 文件夹下(如果没有,你需要自己创建一个)

  2. 修改 pubspec.yaml,找到下面这个地方,然后把图片加进来

flutter:

# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true

# To add assets to your application, add an assets section, like this:
# assets:
#  - images/a_dot_burr.jpeg
#  - images/a_dot_ham.jpeg

修改后如下:

flutter:

# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true

# To add assets to your application, add an assets section, like this:
assets:
- images/lake.jpg

  1. 现在,我们可以把这张图片展示出来了:

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: ‘Flutter UI basic 1’,
home: Scaffold(
appBar: AppBar(
title: Text(‘Top Lakes’),
),
body: Image.asset(
‘images/lake.jpg’,
width: 600.0,
height: 240.0,
// cover 类似于 Android 开发中的 centerCrop,其他一些类型,读者可以查看
// https://docs.flutter.io/flutter/painting/BoxFit-class.html
fit: BoxFit.cover,
)
),
);
}
}

如果读者是初学 Flutter,强烈建议在遇到不熟悉的 API 时翻一翻文档,并在文档中找到 demo 所使用的 API。我们的例子不可能覆盖所有的 API,通过这种方式熟悉文档后,读者就可以根据文档实现出自己想要的效果。不妨就从 Image 开始吧,在 docs.flutter.io/flutter/wid… 找出上面我们使用的 Image.asset 构造函数的几个参数的含义,还有 BoxFit 的其他几个枚举值。

布局

在这一小节,我们来实现图片下方的标题区域。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们直接来看代码:

class _TitleSection extends StatelessWidget {
final String title;
final String subtitle;
final int starCount;

_TitleSection(this.title, this.subtitle, this.starCount);

@override
Widget build(BuildContext context) {
// 为了给 title section 加上 padding,这里我们给内容套一个 Container
return Container(
// 设置上下左右的 padding 都是 32。类似的还有 EdgeInsets.only/symmetric 等
padding: EdgeInsets.all(32.0),
child: Row(
children: [
// 这里为了让标题占满屏幕宽度的剩余空间,用 Expanded 把标题包了起来
Expanded(
// 再次提醒读者,Expanded 只能包含一个子元素,使用的参数名是 child。接下来,
// 为了在竖直方向放两个标题,加入一个 Column。
child: Column(
// Column 是竖直方向的,cross 为交叉的意思,也就是说,这里设置的是水平方向
// 的对齐。在水平方向,我们让文本对齐到 start(读者可以修改为 end 看看效果)
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 聪明的你,这个时候肯定知道为什么突然加入一个 Container 了。
// 跟前面一样,只是为了设置一个 padding
Container(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text(
title,
style: TextStyle(fontWeight: FontWeight.bold),
),
),
Text(
subtitle,
style: TextStyle(color: Colors.grey[500]),
)
],
),

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Android核心知识点

面试成功其实是必然的,因为我做足了充分的准备工作,包括刷题啊,看一些Android核心的知识点,看一些面试的博客吸取大家面试的一些经验。

下面这份PDF是我翻阅了差不多3个月左右一些Android大博主的博客从他们那里取其精华去其糟泊所整理出来的一些Android的核心知识点,全部都是精华中的精华,我能面试到现在2-2资深开发人员跟我整理的这本Android核心知识点有密不可分的关系,在这里本着共赢的心态分享给各位朋友。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

不管是Android基础还是Java基础以及常见的数据结构,这些是无原则地必须要熟练掌握的,尤其是非计算机专业的同学,面试官一上来肯定是问你基础,要是基础表现不好很容易被扣上基础不扎实的帽子,常见的就那些,只要你平时认真思考过基本上面试是没太大问题的。

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上我搜集整理的2019-2021BAT 面试真题解析,我把大厂面试中常被问到的技术点整理成了PDF,包知识脉络 + 诸多细节。

节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
试到现在2-2资深开发人员跟我整理的这本Android核心知识点有密不可分的关系,在这里本着共赢的心态分享给各位朋友。

[外链图片转存中…(img-1Vcdwioe-1712633725895)]

不管是Android基础还是Java基础以及常见的数据结构,这些是无原则地必须要熟练掌握的,尤其是非计算机专业的同学,面试官一上来肯定是问你基础,要是基础表现不好很容易被扣上基础不扎实的帽子,常见的就那些,只要你平时认真思考过基本上面试是没太大问题的。

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上我搜集整理的2019-2021BAT 面试真题解析,我把大厂面试中常被问到的技术点整理成了PDF,包知识脉络 + 诸多细节。

节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-lbOdKTgp-1712633725896)]

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值