Flutter基础
Posted 怀化纱厂球迷
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter基础相关的知识,希望对你有一定的参考价值。
一.创建新的Flutter工程
- 使用终端进行创建 不支持大写字母,可以加下划线
创建成功后,可以使用VSCode或者android studio打开
flutter create 项目名
- 工程内容
lib文件夹:存放编写的代码,main.dart即为启动入口
- 项目启动
打开ios或者安卓模拟器, 在工具栏找到启动调试,点击进行启动
二、flutter项目编写
- flutter项目的几个特性
热重载 hot reload 、 热重启 restart
运行一个flutter项目有三种方式: 冷启动:(从0启动)、 热重载 (最主要是执行build方法,其他方法不受影响)、 热重启(重新运行整个项目)
使用热重载可以快速预览布局变化,但是只有build方法会重置
- 简单编写启动内容
导入头文件(package:flutter/material.dart), 然后调用runApp函数
import 'package:flutter/material.dart';
main(List<String> args)
runApp(Text("Hello world", textDirection: TextDirection.ltr));
- 具体内容设置
textDirection 传入文字方向
style 设置样式,字体颜色等
Center 也是一个Widget,实现居中操作
runApp(Center(
child: Text("Hello world",
textDirection: TextDirection.ltr,
style: TextStyle(fontSize: 30, color: Colors.red))));
三、Widget介绍
- Widget 即组件
万物皆是Widget, 通过嵌套Widget来实现布局功能
-
- 有状态的Widget:
StatefulWedget 在运行过程中有一些状态(data数据)需要改变 - 无状态的Widget:
StatelessWidget 内容是确定的,没有状态(data数据)的改变
- 有状态的Widget:
2. 几种常用Widget
- MaterialApp 会自动添加某些默认配置,不需要手动进行设置,比如文字方向等
runApp(MaterialApp(
home: Center(
child: Text("Hello World",
style: TextStyle(fontSize: 30, color: Colors.orange)))));
- Scaffold 脚手架 用于快速搭建页面 创建导航栏、Tabbar等
主要参数有 appBar(导航栏或者Tabbar),body(界面内容)
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: Text(
"第一个项目",
style: TextStyle(fontSize: 20, color: Colors.white),
)),
body: Center(
child: Text("Hello world",
textDirection: TextDirection.ltr,
style: TextStyle(fontSize: 30, color: Colors.red))))));
3.自定义Widget子类
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
// TODO: implement build
return Text("hello World");
build方法: 在创建自定义Widget子类时,会执行它的build方法,返回一个希望渲染的Widget元素,例如上例所示的Text Widget
StatelessWidget没办法去主动执行build方法,当我们的数据发生改变的时候,build方法会被重新执行(setState)
- build方法在什么情况下会执行?
1)当对应的StatelessWidget第一次被插入到Widget树中时,即第一次创建时
2) 当对应的父Widget发生改变时,子Widget会被重新构建
3) 如果对应的Widget依赖inheritedWidget的一些数据,inheritedWidget数据发生改变时
将需要构造的Widget放到build方法中返回,然后直接调用编写的Widget子类,即可得到想要的界面```
main(List<String> args)
runApp(MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: Text(
"第一个项目",
style: TextStyle(fontSize: 20, color: Colors.white),
)),
body: Center(
child: Text("Hello world",
textDirection: TextDirection.ltr,
style: TextStyle(fontSize: 30, color: Colors.red)))));
4.代码拆分抽取
将各个Widget包装成一个个自定义Widget子类,再进行组合,完成代码的拆分重组
main(List<String> args)
runApp(MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(debugShowCheckedModeBanner: false, home: HomePageView());
class HomePageView extends StatelessWidget
@override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text(
"第一个项目",
style: TextStyle(fontSize: 20, color: Colors.white),
)),
body: ContentBody());
class ContentBody extends StatelessWidget
@override
Widget build(BuildContext context)
return Center(
child: Text("Hello world",
textDirection: TextDirection.ltr,
style: TextStyle(fontSize: 30, color: Colors.orange)));
5.一行写入多个控件
横向写入多个控件 Row
竖向写入多个控件 Column
class ContentBody extends StatelessWidget
@override
Widget build(BuildContext context)
return Center(
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
Text("111"),
Text("Hello world",
textDirection: TextDirection.ltr,
style: TextStyle(fontSize: 30, color: Colors.orange))
]));
默认从每行的最左端开始布局,使用mainAxisAlignment参数可以改变行内的布局位置
6. @immutable修饰符
- 使用@immutable 注解标明的类及其子类均为不可变类,其中的属性均不可变,使用final修饰,不可定义状态
Widget类使用@immutable标记,所有的Widget都不可以直接定义状态 - StatelessWidget即是不可更改的类,不可定义状态
- StatefulWidget内部有一个抽象方法createState,此方法返回一个State类
- 在自定义的State子类中通过重写build方法实现控件
- StatefulWidget内部不可直接定义状态,但是可以在自定义的State子类中可以定义状态
class ContentBody extends StatefulWidget
@override
State<StatefulWidget> createState()
return ContentBodyState();
class _ContentBodyState extends State<ContentBody>
var flag = true;
@override
Widget build(BuildContext context)
// TODO: implement build
return Center(
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
Checkbox(
value: flag,
onChanged: (value)
setState(()
flag = value ?? false;
);
),
Text("Hello world",
textDirection: TextDirection.ltr,
style: TextStyle(fontSize: 30, color: Colors.orange))
]));
总结:
继承自StatefulWidget的类负责实现createState方法,返回一个State类
State类负责实现Build方法,以及状态的创建,来实现功能
另外需要注意的几点:
- 状态的改变需要在setState回调执行完毕之前进行,否则不生效
- State类一般使用下划线开头,如_ContentBodyState,下划线开头的类在别的文件无法访问,保证安全,而继承自StatefulWidget的类不加下划线
问题1:上述示例中,ContentBody返回的State类一定要定义为ContentBody泛型吗?可以改成别的StatefulWidget类吗?
可以,但没必要。
语法上无问题,但是,定义成此泛型,才可以使用系统提供的一个widget属性来构建出桥梁,连通对应的StatefulWidget子类,所以一般会这么写
问题2:继承自StatefulWidget的类,具体的作用?
1). 生成返回State类 2). 可以接收父Widget传过来的数据
四、使用StatelessWidget完成竖向列表布局
StatelessWidget子类代码块生成快捷键: stle
上篇已经简单介绍了StatelessWidget的基本使用,接下来简单介绍如何使用StatelessWidget创建一个商品列表,效果如下:
- StatelessWidget参数传递
与Dart语法一致,创建属性(final修饰),实现初始化方法,进行赋值 调用:
class YWHomeProduct extends StatelessWidget
final String title;
final String desc;
final String imageUrl;
YWHomeProduct(this.title, this.desc, this.imageUrl);
@override
Widget build(BuildContext context)
return Column(children: [Text(title), Text(desc), Image.network(imageUrl)]);
YWHomeProduct("瓜子", "焦糖瓜子,香甜可口",
"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.yzcdn.cn%2Fupload_files%2F2018%2F08%2F21%2FFiLdNHrxKMohbPJ2QbtOTo9MhIMm.jpg%3FimageView2%2F2%2Fw%2F580%2Fh%2F580%2Fq%2F75%2Fformat%2Fjpg&refer=http%3A%2F%2Fimg.yzcdn.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1632121924&t=b37df3a44a89fc3b768c0f3f69f94f2a")
2.竖向列表
- column: 竖向布局多个控件,使用column布局,当竖向控件高度超出屏幕高度后,会显示条纹遮挡
- ListView: 竖向可滚动控件
3.对象创建:
函数内部可以创建局部变量,减小语句内部代码量,但是每次调用函数都会创建
在类的内部创建变量,则只会在对象实例化时创建一次
在类的外部创建变量,则在整个项目生命周期中都会存在
4.创建控件之间的边距
可以通过SizedBox控件来实现,横向距离width,竖向距离height
SizedBox(height: 8)
5.边框创建
将某个控件放置到Container中,可以用Container来设置边框(decoration)以及内边距(padding)等
问题:BoxDecoration的主要用处?
return Container(
padding: EdgeInsets.all(8),
decoration:
BoxDecoration(border: Border.all(color: Colors.purple, width: 8)),
child: Column(children: [
Text(title, style: titleStyle),
SizedBox(height: 8),
Text(desc, style: descStyle),
SizedBox(height: 8),
Image.network(imageUrl)
]));
用于Widget的装饰
color 颜色背景 Color类型 image 图片背景 DecorationImage类型 border 边界 BoxBorder类型 borderRadius 圆角边界半径 BorderRadiusGeometry类型 boxShadow 阴影 List<BoxShadow>类型 gradient 渐变色 Gradient类型 backgroundBlendMode 背景混合模式 BlendMode类型 shape 形状 BoxShape类型
6.控制column竖向布局控件位置
控件分为主轴(竖向)和交叉轴(横向)
主轴使用MainAxisAlignment控制子控件位置
交叉轴使用CrossAxisAlignment控制子控件位置
通过Flex可以决定主轴和交叉轴
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(title, style: titleStyle),
SizedBox(height: 8),
Text(desc, style: descStyle),
SizedBox(height: 8),
Image.network(imageUrl)
])
下面是完整的代码:
import 'package:flutter/material.dart';
main(List<String> args)
runApp(MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(debugShowCheckedModeBanner: false, home: YWHomePage());
class YWHomePage extends StatelessWidget
@override
Widget build(BuildContext context)
return Scaffold(appBar: AppBar(title: Text("购物车")), body: YWBodyContent());
class YWBodyContent extends StatelessWidget
@override
Widget build(BuildContext context)
return ListView(children: [
YWHomeProduct("瓜子", "焦糖瓜子,香甜可口",
"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.yzcdn.cn%2Fupload_files%2F2018%2F08%2F21%2FFiLdNHrxKMohbPJ2QbtOTo9MhIMm.jpg%3FimageView2%2F2%2Fw%2F580%2Fh%2F580%2Fq%2F75%2Fformat%2Fjpg&refer=http%3A%2F%2Fimg.yzcdn.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1632121924&t=b37df3a44a89fc3b768c0f3f69f94f2a"),
YWHomeProduct("花生", "盐水花生,补气养血",
"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fi2.chuimg.com%2F25d9caf08b6811e6a9a10242ac110002_1171w_801h.jpg%3FimageView2%2F2%2Fw%2F660%2Finterlace%2F1%2Fq%2F90&refer=http%3A%2F%2Fi2.chuimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1632122384&t=f3859622328496babe08d62ffca276ba"),
YWHomeProduct("八宝粥", "居家旅行,出门良品",
"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fcp2.douguo.net%2Fupload%2Fdish%2F7%2F3%2F3%2F600_7355c671e3bf52100c61e043c6110ae3.jpg&refer=http%3A%2F%2Fcp2.douguo.net&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1632122038&t=cedcc17ba9ed999844c9c3c1bedf2ded")
]);
class YWHomeProduct extends StatelessWidget
final String title;
final String desc;
final String imageUrl;
final titleStyle = TextStyle(fontSize: 20, color: Colors.orange);
final descStyle = TextStyle(fontSize: 15, color: Colors.blue);
YWHomeProduct(this.title, this.desc, this.imageUrl);
@override
Widget build(BuildContext context)
return Container(
padding: EdgeInsets.all(8),
decoration:
BoxDecoration(border: Border.all(color: Colors.purple, width: 8)),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(title, style: titleStyle),
SizedBox(height: 8),
Text(desc, style: descStyle),
SizedBox(height: 8),
Image.network(imageUrl)
]));
五、StatefulWidget的使用
- StatefulWidget的使用
有状态需要改变的时候要使用StatefulWidget
- StatefulWidget内部有一个抽象方法createState,此方法返回一个State类,继承自StatefulWidget的类需要实现此方法
- 在自定义的State子类中需要实现build方法生成一个控件
- StatefulWidget内部不可直接定义状态,但是可以在自定义的State子类中可以定义状态
问题:为什么Flutter在设计的时候,StatefulWidget的build方法放在State中?
- build出来的widget是需要依赖State中的变量(状态/数据)
- 在Flutter的运行过程中,widget是不断销毁和创建的,当我们的状态发生改变时,并不希望重新创建一个新的State
- 使用StatefulWidget构建一个计数器
效果图如下:
其中按钮可以使用ElevatedButton(RaisedButton已废弃)
使用Column构建竖向组件,Row构建横向组件
import 'package:flutter/material.dart';
main(List<String> args)
runApp(MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(debugShowCheckedModeBanner: false, home: YWHomePage());
class YWHomePage extends StatelessWidget
@override
Widget build(BuildContext context)
return Scaffold(appBar: AppBar(title: Text("购物车")), body: YWHomeContent());
class YWHomeContent extends StatefulWidget
@override
_YWHomeContentState createState() => _YWHomeContentState();
class _YWHomeContentState extends State<YWHomeContent>
int number = 0;
@override
Widget build(BuildContext context)
return Center(
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
getButtons(),
Text("当前计数:$number", style: TextStyle(fontSize: 20))
]));
Widget getButtons()
return Row(mainAxisAlignment: MainAxisAlignment.center, children: [
ElevatedButton(
child: Text("+", style: TextStyle(fontSize: 20)),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.pink)),
onPressed: ()
setState(()
number++;
);
),
ElevatedButton(
child: Text("-", style: TextStyle(fontSize: 20)),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.purple)),
onPressed: ()
setState(()
if (number > 0)
number--;
);
)
]);
问题:数值改变能放在setState外面吗?
setState会触发widget build方法重新进行渲染
- StatefulWidget传值
父类给子类传值,传值方式与StatelessWdight语法一致,创建属性(final修饰),实现对应初始化方法,进行赋值,将参数传递给Widget子类
然后在State类中通过this.widget来实现取值
class YWHomeContent extends StatefulWidget
final String message;
YWHomeContent(this.message);
@override
_YWHomeContentState createState() =>_YWHomeContentState();
class _YWHomeContentState extends State<YWHomeContent>
int number = 0;
@override
Widget build(BuildContext context)
return Text("$this.widget.message");
这也就是为什么State类定义的泛型需要跟之前创建的StatefulWidget子类保持一致的原因
六、常用Widget
- Text 文本
1.1 普通文本展示
-
- 文本布局参数:如对齐方式TextAlign、排版方向TextDirection、最大行数maxLines、文本超出限制规则overflow、textScaleFactor: 文本缩放因子等,这些都是构造函数参数
- 控制文本样式的参数:如字体名称fontFamily、 字体大小fontSize、颜色color、文本阴影shadow等,这些都被封装到了构造函数的参数style中
return Center(
child: Text("调用YWHomeContent的初始化方法",
textAlign: TextAlign.center,
maxLines: 1,
overflow: TextOverflow.ellipsis,
textScaleFactor: 0.8,
style: TextStyle(
fontSize: 30,
color: Colors.red,
fontWeight: FontWeight.normal)));
1.2 富文本
富文本使用Text.rich进行创建,必传参数textSpan,可以使用TextSpan进行构造,其内部可以传一个widget,也可以传一个Children数组
return Center(
child: Text.rich(TextSpan(children: [
TextSpan(text: "Hello world", style: TextStyle(fontSize: 30)),
WidgetSpan(child: Icon(Icons.favorite, color: Colors.red)),
TextSpan(text: "Hello flutter", style: TextStyle(fontSize: 20))
])));
- Button 按钮
2.1 ElevatedButton (RaisedButton 凸起按钮 ?)
2.2 TextButton (FlatButton 平坦按钮 ?)
2.3 OutlinedButton 边框按钮
2.4 FloatingActionButton 悬浮按钮
常用于脚手架Scaffold的入参
2.5 自定义button 图标文字背景圆角等等
TextButton(
onPressed: ()
print("TextButton clicked");
,
child: Text("TextButton"),
style: ButtonStyle(
foregroundColor: MaterialStateProperty.all(Colors.white),
backgroundColor: MaterialStateProperty.all(Colors.orange)))
OutlinedButton(
onPressed: ()
print("OutlinedButton clicked");
,
child: Text("OutlinedButton"),
style: ButtonStyle(
foregroundColor: MaterialStateProperty.all(Colors.white),
backgroundColor: MaterialStateProperty.all(Colors.blue)))
FloatingActionButton(
onPressed: ()
print("FloatingActionButton clicked");
,
child: Icon(Icons.add))
TextButton(
onPressed: ()
print("自定义button点击");
,
child: Row(
children: [Icon(Icons.add), Text("添加好友")],
mainAxisSize: MainAxisSize.min),
style: ButtonStyle(
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8))),
foregroundColor: MaterialStateProperty.all(Colors.white),
backgroundColor: MaterialStateProperty.all(Colors.orange)))
- Image 图片
3.1 从网络获取图片 NetworkImage
通过url链接加载对应图片,
fit属性控制图片裁切模式,为BoxFit类型,其中fitWidth为宽度固定,自动适配高度;fitHeight为高度固定,自动适配宽度
repeat属性控制空白部分是否进行图片重复填充,跟fit模式相关
alignment控制图片裁切完后在视图内展示的位置,可以直接使用宏定义,也可以自定义,Alignment(0, 0)表示居中,值为-1 —— 1
color和colorBlendMode负责控制颜色混入配置
return Image(
image: NetworkImage(imageUrl),
width: 300,
height: 300,
fit: BoxFit.contain,
repeat: ImageRepeat.repeatY,
alignment: Alignment(1, 1),
color: Colors.pink,
colorBlendMode: BlendMode.color,
);
简写模式如下:
Image.network(imageUrl)
问题:(-1,-1)代表什么位置?
问题: 如何设置图片占位图?
3.2 从本地获取图片
步骤如下:
-
- 在flutter项目中创建一个文件夹,用于存储图片
1x图片放到指定文件夹内,如果有2x、3x图片,在1x图片所在文件夹内建立2.0x文件夹、3.0x文件夹放置 - 在pubspec.yaml进行配置
找到assets:配置处,将注释取消掉
添加对应图片路径配置 配置完成后执行下flutter packages get重新获取依赖
- 在flutter项目中创建一个文件夹,用于存储图片
assets:
- assets/Images/
-
- 使用图片
return Image(image: AssetImage("assets/Images/001.jpg"));
3.3 实现圆角头像
-
- 使用CircleAvatar组件
return CircleAvatar(
backgroundImage: AssetImage("assets/Images/001.jpg"),
radius: 50,
child: Container(
alignment: Alignment(0, 0),
width: 100,
height: 20,
child: Text("兵长利威尔")));
其中backgroundImage传入抽象类ImageProvider图片,radius为半径,child为内部widget
-
- 使用ClipOval组件
ClipOval(
child: Image(
image: NetworkImage(imageUrl),
width: 100,
height: 100,
fit: BoxFit.cover,
))
3.4 实现圆角图片
-
- ClipRRect
ClipRRect(
borderRadius: BorderRadius.circular(80),
child: Image.network(imageUrl,
width: 160, height: 160, fit: BoxFit.cover))
七、StatelessWidget、StatefulWidget生命周期
- 生命周期的作用(意义)
- 初始化一些数据、状态、变量
- 发送网络请求时机
- 进行一些事件的监听
- 管理内存(手动销毁)
- StatelessWidget生命周期
这里比较简单,只有两个生命周期
- 构造函数被调用
- 调用build方法
class TestWidget extends StatelessWidget
TestWidget()
print("构造函数被创建");
@override
Widget build(BuildContext context)
print("build方法被调用");
return Container();
- StatefulWidget生命周期
- 第一步:调用StatefulWidget子类的构造方法
- 第二步:调用StatefulWidget子类的createState方法
- 第三步:调用State子类的构造方法
- 第四步 调用State子类的initState方法
用于状态的初始化,会在State类完成构造之后,build方法执行之前,进行执行
initState方法使用@mustCallSuper标记,必须调用父类initState方法
- 第五步:执行State子类的build方法
- 最后,当前的Widget子类不再使用时,会执行State子类的dispose方法,进行内存的释放
此外:
- didChangeDependencies方法 在initState方法执行后,或者从其他对象中依赖一些数据发生改变时,比如inheritedWidget,会执行此方法
- didUpdateWidget方法 当重新创建Widget子类(rebuild),会触发state子类的didUpdateWidget方法
代码如下所示:
class YWHomeContent extends StatefulWidget
YWHomeContent()
print("1.调用YWHomeContent的初始化方法");
@override
_YWHomeContentState createState()
print("2.调用YWHomeContent的createState方法");
return _YWHomeContentState();
class _YWHomeContentState extends State<YWHomeContent>
_YWHomeContentState()
print("3.调用_YWHomeContentState的初始化方法");
@override
void initState()
super.initState();
print("4.调用_YWHomeContentState的initState方法");
@override
void didChangeDependencies()
print("调用_YWHomeContentState的didChangeDependencies方法");
super.didChangeDependencies();
@override
void didUpdateWidget(covariant YWHomeContent oldWidget)
print("调用_YWHomeContentState的didUpdateWidget方法");
super.didUpdateWidget(oldWidget);
@override
Widget build(BuildContext context)
print("5.调用_YWHomeContentState的build方法");
return Text("Hello World");
@override
void dispose()
super.dispose();
print("6.调用_YWHomeContentState的dispose方法");
- setState方法会在内部触发重新运行build方法,根据最新的状态返回新的控件,所以状态的修改需要在setState方法中进行
- StatefulWidget生命周期的复杂版
在StatefulWidget普通生命周期的基础上,还有一些复杂的过程未提及到
- mounted
- dirty state & clean state
疑问:意义及用法?
以上是关于Flutter基础的主要内容,如果未能解决你的问题,请参考以下文章