- 使用终端进行创建 不支持大写字母,可以加下划线
创建成功后,可以使用VSCode或者Android studio打开
flutter create 项目名
- 工程内容
lib文件夹:存放编写的代码,main.dart即为启动入口
- 项目启动
打开iOS或者安卓模拟器, 在工具栏找到启动调试,点击进行启动
- flutter项目的几个特性
热重载 hot reload 、 热重启 restart
运行一个flutter项目有三种方式: 冷启动:(从0启动)、 热重载 (最主要是执行build方法,其他方法不受影响)、 热重启(重新运行整个项目)
使用热重载可以快速预览布局变化,但是只有build方法会重置
- 简单编写启动内容
导入头文件(package:flutter/material.dart), 然后调用runApp函数
import 'package:flutter/material.dart'; main(Listargs) { 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(Listargs) { 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(Listargs) { 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 StatecreateState() { return ContentBodyState(); } } class _ContentBodyState extends State { 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://www.kaotop.com/skin/sinaskin/image/nopic.gif")
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类型 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五、StatefulWidget的使用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://www.kaotop.com/skin/sinaskin/image/nopic.gif"), YWHomeProduct("花生", "盐水花生,补气养血", "https://gimg2.baidu.com/image_search/src=http://www.kaotop.com/skin/sinaskin/image/nopic.gif"), YWHomeProduct("八宝粥", "居家旅行,出门良品", "https://gimg2.baidu.com/image_search/src=http://www.kaotop.com/skin/sinaskin/image/nopic.gif") ]); } } 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内部有一个抽象方法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(Listargs) { 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 { 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{ int number = 0; @override Widget build(BuildContext context) { return Text("${this.widget.message}"); } }
这也就是为什么State类定义的泛型需要跟之前创建的StatefulWidget子类保持一致的原因
六、常用Widget查看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{ _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
疑问:意义及用法?
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)