Flutter 基础系列篇
文章目录1、输出Hello World2、文本、容器3、远程图片、本地图片、实现圆形图片,实现圆角图片4、基础列表组件、 水平列表组件 、 图标组件5、列表、动态列表6、网格布局7、底部导航8、页面跳转、跳转传值(普通路由、普通路由传值)9、页面跳转、跳转传值(命名路由、命名路由传值)10、替换路由、返回到根路由11、自定义AppBar 定义顶部Tab切换、底部Tab结合顶部Tab实现类似头条页面
文章目录
- 1、输出Hello World
- 2、文本、容器
- 3、远程图片、本地图片、实现圆形图片,实现圆角图片
- 4、基础列表组件、 水平列表组件 、 图标组件
- 5、列表、动态列表
- 6、网格布局
- 7、底部导航
- 8、页面跳转、跳转传值(普通路由、普通路由传值)
- 9、页面跳转、跳转传值(命名路由、命名路由传值)
- 10、替换路由、返回到根路由
- 11、自定义AppBar 定义顶部Tab切换、底部Tab结合顶部Tab实现类似头条页面布局
- 12、TabController定义顶部tab切换,并介绍生命周期函数
- 13、Drawer侧边栏、以及侧边栏内容布局
- 14、按钮组件 RaisedButton、FlatButton、OutlineButton、IconButton、ButtonBar以及自定义按钮组件
- 15、类似闲鱼App底部导航凸起按钮
- 16、单行文本框、多选框
- 17、多行文本框、开关按钮、多选框、单选按钮、RadioListTile、Radio、表单
- 18、调用原生时间选择器、日期选择器、时间戳、Future异步
- 19、调用第三方时间选择器、日期选择器、时间戳
- 20、轮播图 flutter_swiper
- 21、普通对话框、列表对话框、单选对话框、Toast提示
我叫王睿,对您有帮助的话,请给个点赞或关注喔,谢谢!
1、输出Hello World
1.1、 效果图
1.2、输出一句Hello World
import 'package:flutter/material.dart';
void main()=>runApp(MyApp());
//等价于:
/*
void main(){
runApp(MyApp());
}
*/
class MyApp extends StatelessWidget{ //抽离页面全部内容
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp( //顶层 widget
home:Scaffold( //Material Design 布局结构的基本实现
appBar: AppBar( //显示在界面顶部的一个 AppBar。
title: Text('Flutter Demo'), //标题
),
body: HomeContent(), //当前界面所显示的主要内容 Widget。
),
theme: ThemeData( //主题
primarySwatch: Colors.yellow //修改主题颜色
),
);
}
}
class HomeContent extends StatelessWidget{ //抽离主界面
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center( //居中
child: Text("Hello Flutter", //Text组件用来显示字符串内容到页面中
textDirection:TextDirection.ltr, //表示文本的显示方式
style: TextStyle( //样式
fontSize: 40.0, //大小
color: Colors.yellow, //颜色
),
),
);
}
}
1.3、知识点
1 、MaterialApp
MaterialApp 是一个方便的 Widget,它封装了应用程序实现 Material Design 所需要的
一些 Widget。一般作为顶层 widget 使用。
常用的属性:
home(主页)
title(标题)
color(颜色)
theme(主题)
routes(路由)
2、Scaffold
Scaffold 是 Material Design 布局结构的基本实现。此类提供了用于显示 drawer、snackbar 和底部 sheet 的 API。
Scaffold 有下面几个主要属性:
appBar - 显示在界面顶部的一个 AppBar。
body - 当前界面所显示的主要内容 Widget。
drawer - 抽屉菜单控件。
1.4、笔记
1.
所有的组件都是类
2.
在Dart中,允许实例化类的时候,不写关键词new,例如:
var p = new Persion();
可以写成:
var p = Persion();
3.在Flutter里面,我们把Center放在runApp里面是显然不合理的!
因为:当我们的一个组件功能需求很多的时候,我们的代码就会很多,那么我们放到runApp里,代码就会变得很杂乱冗余
所以:我们可以把它单独抽离成一个组件,相当于Java里的封装,减轻程序负担,节俭代码质量
4.问题:为什么实例化runApp(MyApp());的时候, Widget build(BuildContext context) {
// TODO: implement build
return Center( //居中
child: Text('Hello Flutter',
textDirection: TextDirection.ltr, //表示文本的显示方式
)
);
}抽象方法会自动执行?
答:build 是用来创建 Widget 的,build 在每次界面刷新的时候都会调用
5.child: Text是实例化Text的意思吗?
答:不,我们来看下面这个代码自然就能明白了!
class MyApp extends StatelessWidget{
@override
//build 是用来创建 Widget 的,build 在每次界面刷新的时候都会调用
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home:Scaffold(
appBar: AppBar(
title: Text('Flutter Demo'),
),
body: HomeContent(),
),
theme: ThemeData(
primarySwatch: Colors.yellow //修改主题颜色
),
);
}
}
class HomeContent extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center( //居中
child: Text('Hello Flutter',
textDirection: TextDirection.ltr, //表示文本的显示方式
style: TextStyle( //字体样式
fontSize: 40.0,
//设置颜色的两种方式
// 方式一
color: Colors.yellow,
// 方式二:
//参数依次的含义为:红色,绿色,蓝色,透明度 ,颜色取前三个的混合值,透明度取第四个参数
// color: Color.fromRGBO(244, 233, 321, 0.5)
),
)
);
}
}
我们主要分析body就行了其他的不用管,可以看到body是主页内容,他直接 body: HomeContent(),
等价于 body: new HomeContent(),那么意思已经很明显了,就是在要实现哪些功能的时候,就实例化哪些组件即可!
2、文本、容器
2.1丶 效果图
2.2、代码+注释
import 'package:flutter/material.dart';
void main()=>runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home:Scaffold(
appBar: AppBar(
title: Text('Fullter Demo'),
),
body: HomeContent(),
)
);
}
}
class HomeContent extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Container( //是一个容器,相当于前端的div,用于布局
child: Text('我是一个文本我是一个文本我是一个文本我是一个文本我是一个文本我是一个文本',
textAlign: TextAlign.center, //居中显示
overflow: TextOverflow.ellipsis, //表示溢出后用...代替
maxLines: 1, //最多显示一行
textScaleFactor: 1.8, //字体放大两倍
style: TextStyle(
fontSize: 16.0 , //设置字体大小
color: Colors.red , //字体颜色
fontWeight:FontWeight.w800, //字体加粗
fontStyle: FontStyle.italic, //字体倾斜
decoration: TextDecoration.lineThrough , //穿过文本的线
decorationColor: Colors.white, //穿过线的颜色
decorationStyle: TextDecorationStyle.dashed, //虚线
letterSpacing: 5.0 //字体间距
),
),
height: 300.0, //设置容器高度
width: 300.0, //设置容器宽度
decoration: BoxDecoration(
color: Colors.yellow, //背景颜色
border: Border.all(
color: Colors.blue, //边框颜色
width: 2.0 //边框宽度
),
borderRadius: BorderRadius.all(
Radius.circular(20), //设置圆角边框
// Radius.circular(150), //设置圆形边框
)
),
// padding: EdgeInsets.all(20), //表示与上下左右四边都有20的内边距
padding: EdgeInsets.fromLTRB(10, 30, 5, 0), //设置左上右下内边距
margin: EdgeInsets.fromLTRB(10, 30, 5, 0), //设置左上右下外边距
// transform: Matrix4.translationValues(100, 0, 0) //X轴位移100
// transform: Matrix4.rotationZ(0.3), //Z轴旋转,正值是顺时针旋转,负值是逆时针旋转
// transform: Matrix4.diagonal3Values(1.2, 1, 1), //缩放
alignment: Alignment.bottomLeft, //让边框内的元素居左侧底部
)
);
}
}
2.3丶 知识点
Text 组件
[1]
textAlign 文本对齐方式(center 居中,left 左对齐,right 右对齐,justfy 两端对齐)
textDirection 文本方向(ltr 从左至右,rtl 从右至左)
overflow 文字超出屏幕之后的处理方式(clip裁剪,fade 渐隐,ellipsis 省略号)
textScaleFactor 字体显示倍率
maxLines 文字显示最大行数
style 字体的样式设置
[2]下面是 TextStyle 参数 :
名称 功能
decoration 文字装饰线(none 没有线,lineThrough 删除线,overline 上划线,underline 下划线)
decorationColor 文字装饰线颜色
decorationStyle 文字装饰线风格([dashed,dotted]虚线,
double 两根线,solid 一根实线,wavy 波浪线)
wordSpacing 单词间隙(如果是负值,会让单词变得更紧凑
letterSpacing 字母间隙(如果是负值,会让字母变得更紧凑)
fontStyle 文字样式(italic 斜体,normal 正常体)
fontSize 文字大小
color 文字颜色
fontWeight 字体粗细(bold 粗体,normal 正常体)
更多参数:https://docs.flutter.io/flutter/painting/TextStyle-class.html
3、远程图片、本地图片、实现圆形图片,实现圆角图片
3.1、效果图
【1】引用远程图片
【2】引用本地图片
【3】实现圆形图片(方法一)、圆角图片(受弧度影响)
【4】实现圆形图片(方法二)
3.2、代码+注释
【1】引用远程图片
import 'package:flutter/material.dart';
void main()=> runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('FlutterDemo'),
),
body: HomeCentent(),
),
);
}
}
class HomeCentent extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Container(
child: Image.network( //引入一张远程图片
"https://www.itying.com/images/flutter/2.png", //远程图片链接
alignment: Alignment.topLeft, //设置图片的方位在左上角
// color: Colors.yellow, //设置这张图片的颜色
// colorBlendMode: BlendMode.luminosity, //设置颜色的混合模式
fit:BoxFit.cover, //设置图片的显示模式,cover—全屏显示,最常用!
// repeat: ImageRepeat.repeatX, //横向平铺,纵向不变
// repeat: ImageRepeat.repeat, //横向纵向都平铺,
),
width: 300,
height: 300,
decoration: BoxDecoration(
color: Colors.yellow //设置方框背景颜色
),
)
);
}
}
【2】引用本地图片
先做准备工作:(三步)
1.项目根目录下,创建Images文件夹,如下图:
2.分别创建2.0x,3.0x,4.0x并且在这些文件夹里面放图片,然后再在外面放一张图片,如图:
3.在pubspec.yaml 文件里,增加如下配置:
点击右上角 Packages get并且要Ctrl+s保存
然后就可以开始敲代码了:
import 'package:flutter/material.dart';
void main()=> runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('FlutterDemo'),
),
body: HomeCentent(),
),
);
}
}
class HomeCentent extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Container(
child: Image.asset('images/aa.png', //导入本地图片
fit: BoxFit.cover,
),
height: 300,
width: 300,
)
);
}
}
【3】实现圆形图片(方法一)、圆角图片(根据弧度变化)
return Center(
child: Container(
width: 300,
height: 300,
decoration: BoxDecoration(
color: Colors.yellow , //设置方框背景颜色
// borderRadius: BorderRadius.all(
// Radius.circular(150) //变成圆形,不过一般不这么实现圆形图片
// ),
borderRadius: BorderRadius.circular(150), //实现圆形图片的方式一: 有些麻烦
image: DecorationImage(
image: NetworkImage("https://www.itying.com/images/flutter/2.png"),
fit: BoxFit.cover //铺满全屏
),
),
)
);
【4】实现圆形图片(方法二):
return Center(
child: Container(
child: ClipOval( //实现圆形图片方式二,最简单的方式
child: Image.network('https://www.itying.com/images/flutter/2.png',
height: 100,
width: 100,
fit: BoxFit.cover,
),
),
)
);
3.3、知识点
图片组件是显示图像的组件,Image 组件有很多构造函数,这里我们只给大家讲两个
Image.asset, 本地图片
Image.network 远程图片
Image 组件的常用属性:
名称 类型 说明
alignment Alignment 图片的对齐方式
color 和 colorBlendMode 设置图片的背景颜色,通常和 colorBlendMode 配合一起使用,这样可以是图片颜色和背景色混合。上面的图片就是进行了颜色的混合,绿色背景和图片红色的混合
fit BoxFit fit 属性用来控制图片的拉伸和挤压,这都是根据父容器来的。
BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。
BoxFit.contain:全图显示,显示原比例,可能会有空隙。
BoxFit.cover:显示可能拉伸,可能裁切,充满(图片要充满整个容器,还不变形)。
BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,可能裁切。
BoxFit.fitHeight :高度充满(竖向充满),显示可能拉伸,可能裁切。
BoxFit.scaleDown:效果和 contain 差不多,但是此属性不允许显示超过源图片大小,可小不可大。
repeat 平铺
ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整个画布。
ImageRepeat.repeatX: 横向重复,纵向不重复。
ImageRepeat.repeatY:纵向重复,横向不重复。
width 宽度 一般结合 ClipOval 才能看到效果
height 高度 一般结合 ClipOval 才能看到效果
更多属性参考:https://api.flutter.dev/flutter/widgets/Image-class.html
4、基础列表组件、 水平列表组件 、 图标组件
4.1、效果图
【1】基础列表
【2】基础列表+图标组件
【3】基础列表+引用远程图片
【4】垂直列表
【5】水平列表
4.2、代码+注释
【1】基础列表
import 'package:flutter/material.dart';
void main()=>runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home:Scaffold(
appBar: AppBar(
title: Text('Hello Flutter'),
),
body: HomeContent(),
)
);
}
}
class HomeContent extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return ListView( //列表
children: <Widget>[ //表示配置它的子元素
ListTile( //每个item一般都是写在ListTile里,这是规范
title: Text('床前明月光,按数据库里大家都刻录机按时考虑到就开始拉到就',
style: TextStyle(
fontSize: 18
),
), //标题
subtitle: Text('阿斯利康大神解答控件阿斯科利大家酷啦四大皆空拉丝机的酷啦数据库了多久啊上课了多久啊上课了经典款了'), //内容
),
ListTile( //每个item一般都是写在ListTile里,这是规范
title: Text('床前明月光,按数据库里大家都刻录机按时考虑到就开始拉到就'), //标题
subtitle: Text('阿斯利康大神解答控件阿斯科利大家酷啦四大皆空拉丝机的酷啦数据库了多久啊上课了多久啊上课了经典款了'), //内容
),
ListTile( //每个item一般都是写在ListTile里,这是规范
title: Text('床前明月光,按数据库里大家都刻录机按时考虑到就开始拉到就'), //标题
subtitle: Text('阿斯利康大神解答控件阿斯科利大家酷啦四大皆空拉丝机的酷啦数据库了多久啊上课了多久啊上课了经典款了'), //内容
),
ListTile( //每个item一般都是写在ListTile里,这是规范
title: Text('床前明月光,按数据库里大家都刻录机按时考虑到就开始拉到就'), //标题
subtitle: Text('阿斯利康大神解答控件阿斯科利大家酷啦四大皆空拉丝机的酷啦数据库了多久啊上课了多久啊上课了经典款了'), //内容
),
ListTile( //每个item一般都是写在ListTile里,这是规范
title: Text('床前明月光,按数据库里大家都刻录机按时考虑到就开始拉到就'), //标题
subtitle: Text('阿斯利康大神解答控件阿斯科利大家酷啦四大皆空拉丝机的酷啦数据库了多久啊上课了多久啊上课了经典款了'), //内容
),
ListTile( //每个item一般都是写在ListTile里,这是规范
title: Text('床前明月光,按数据库里大家都刻录机按时考虑到就开始拉到就'), //标题
subtitle: Text('阿斯利康大神解答控件阿斯科利大家酷啦四大皆空拉丝机的酷啦数据库了多久啊上课了多久啊上课了经典款了'), //内容
)
],
);
}
}
【2】基础列表+图标组件
...
...
...
return ListView( //列表
children: <Widget>[ //表示配置它的子元素
ListTile( //每个item一般都是写在ListTile里,这是规范
leading: Icon(Icons.settings,color: Colors.yellow), //在左侧放置一个图标,修改图标颜色
title: Text('床前明月光,按数据库里大家都刻录机按时考虑到就开始拉到就'), //标题
subtitle: Text('阿斯利康大神解答控件阿斯科利大家酷啦四大皆空拉丝机的酷啦数据库了多久啊上课了多久啊上课了经典款了'), //内容
),
ListTile( //每个item一般都是写在ListTile里,这是规范
leading: Icon(Icons.home,size: 30), //在左侧放置一个图标
title: Text('床前明月光,按数据库里大家都刻录机按时考虑到就开始拉到就'), //标题
subtitle: Text('阿斯利康大神解答控件阿斯科利大家酷啦四大皆空拉丝机的酷啦数据库了多久啊上课了多久啊上课了经典款了'), //内容
trailing: Icon(Icons.settings), //在右侧放置一个图标
),
ListTile( //每个item一般都是写在ListTile里,这是规范
leading: Icon(Icons.settings), //在左侧放置一个图标
title: Text('床前明月光,按数据库里大家都刻录机按时考虑到就开始拉到就'), //标题
subtitle: Text('阿斯利康大神解答控件阿斯科利大家酷啦四大皆空拉丝机的酷啦数据库了多久啊上课了多久啊上课了经典款了'), //内容
),
ListTile( //每个item一般都是写在ListTile里,这是规范
leading: Icon(Icons.home), //在左侧放置一个图标
title: Text('床前明月光,按数据库里大家都刻录机按时考虑到就开始拉到就'), //标题
subtitle: Text('阿斯利康大神解答控件阿斯科利大家酷啦四大皆空拉丝机的酷啦数据库了多久啊上课了多久啊上课了经典款了'), //内容
)
],
);
【3】基础列表+引用远程图片
...
...
...
return ListView( //列表
children: <Widget>[ //表示配置它的子元素
ListTile( //每个item一般都是写在ListTile里,这是规范
leading: Image.network("https://www.itying.com/images/flutter/1.png"),
title: Text('床前明月光,按数据库里大家都刻录机按时考虑到就开始拉到就'), //标题
subtitle: Text('阿斯利康大神解答控件阿斯科利大家酷啦四大皆空拉丝机的酷啦数据库了多久啊上课了多久啊上课了经典款了'), //内容
),
ListTile( //每个item一般都是写在ListTile里,这是规范
leading: Image.network("https://www.itying.com/images/flutter/2.png"),
title: Text('床前明月光,按数据库里大家都刻录机按时考虑到就开始拉到就'), //标题
subtitle: Text('阿斯利康大神解答控件阿斯科利大家酷啦四大皆空拉丝机的酷啦数据库了多久啊上课了多久啊上课了经典款了'), //内容
trailing: Image.network("https://www.itying.com/images/flutter/5.png")
),
ListTile( //每个item一般都是写在ListTile里,这是规范
leading: Image.network("https://www.itying.com/images/flutter/3.png"),
title: Text('床前明月光,按数据库里大家都刻录机按时考虑到就开始拉到就'), //标题
subtitle: Text('阿斯利康大神解答控件阿斯科利大家酷啦四大皆空拉丝机的酷啦数据库了多久啊上课了多久啊上课了经典款了'), //内容
),
ListTile( //每个item一般都是写在ListTile里,这是规范
leading: Image.network("https://www.itying.com/images/flutter/4.png"),
title: Text('床前明月光,按数据库里大家都刻录机按时考虑到就开始拉到就'), //标题
subtitle: Text('阿斯利康大神解答控件阿斯科利大家酷啦四大皆空拉丝机的酷啦数据库了多久啊上课了多久啊上课了经典款了'), //内容
)
],
);
【4】垂直列表
...
...
return ListView(
scrollDirection: Axis.vertical, //垂直列表
padding: EdgeInsets.all(10),
children: <Widget>[
Image.network('https://www.itying.com/images/flutter/1.png'),
Container(
child: Text('我是一个标题',
textAlign: TextAlign.center, //居中
style: TextStyle(
fontSize: 28,
),
),
height: 60,
padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
),
Image.network('https://www.itying.com/images/flutter/2.png'),
Container(
child: Text('我是一个标题',
textAlign: TextAlign.center, //居中
style: TextStyle(
fontSize: 28,
),
),
height: 60,
padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
),
Image.network('https://www.itying.com/images/flutter/3.png'),
Container(
child: Text('我是一个标题',
textAlign: TextAlign.center, //居中
style: TextStyle(
fontSize: 28,
),
),
height: 60,
padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
),
Image.network('https://www.itying.com/images/flutter/4.png'),
Container(
child: Text('我是一个标题',
textAlign: TextAlign.center, //居中
style: TextStyle(
fontSize: 28,
),
),
height: 60,
padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
),
Image.network('https://www.itying.com/images/flutter/5.png'),
Container(
child: Text('我是一个标题',
textAlign: TextAlign.center, //居中
style: TextStyle(
fontSize: 28,
),
),
height: 60,
padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
),
Image.network('https://www.itying.com/images/flutter/6.png'),
Container(
child: Text('我是一个标题',
textAlign: TextAlign.center, //居中
style: TextStyle(
fontSize: 28,
),
),
height: 60,
padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
),
Image.network('https://www.itying.com/images/flutter/2.png'),
Image.network('https://www.itying.com/images/flutter/3.png'),
Image.network('https://www.itying.com/images/flutter/4.png'),
Image.network('https://www.itying.com/images/flutter/1.png'),
Image.network('https://www.itying.com/images/flutter/2.png'),
Image.network('https://www.itying.com/images/flutter/3.png'),
Image.network('https://www.itying.com/images/flutter/1.png'),
Image.network('https://www.itying.com/images/flutter/2.png'),
Image.network('https://www.itying.com/images/flutter/3.png'),
],
);
【5】水平列表
...
...
...
return Container(
height: 180,
child: ListView(
// scrollDirection: Axis.vertical, //垂直列表
scrollDirection: Axis.horizontal, //水平列表,
children: <Widget>[
Container(
width: 180.0,
height: 180.0,
color: Colors.yellow,
),
Container(
width: 180.0,
height: 180.0,
color: Colors.blue,
child: ListView(
children: <Widget>[
Image.network("https://www.itying.com/images/flutter/2.png"),
Text('我是一个文本')
],
),
),
Container(
width: 180.0,
height: 180.0,
color: Colors.red,
),
Container(
width: 180.0,
height: 180.0,
color: Colors.green,
)
],
),
);
4.3、知识点
-
Flutter 列表组件概述
列表布局是我们项目开发中最常用的一种布局方式。Flutter 中我们可以通过 ListView 来定义
列表项,支持垂直和水平方向展示。通过一个属性就可以控制列表的显示方向。列表有一下
分类:
1、垂直列表
2、垂直图文列表
3、水平列表
4、动态列表
5、矩阵式列表 -
Flutter 列表参数
名称 类型 说明
scrollDirection Axis Axis.horizontal 水平列表
Axis.vertical 垂直列表
padding EdgeInsetsGeometry 内边距
resolve bool 组件反向排序
children List<Widget> 列表元素
4.4、笔记
-
在ListView的children: 里可以放置任何的组件
-
我们在写完每一个ListTile(android里的item)后,它会自动的生成Widget数组,这就相当于是一个adapter管理数据后的一个数组
5、列表、动态列表
5.1、 效果图
5.2、代码+注释
实现动态列表:
【1】方式一:抽离组件
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home:Scaffold(
appBar: AppBar(
title: Text('FlutterDemo'),
),
body: HomeContent(),
)
);
}
}
class HomeContent extends StatelessWidget{
//方式一:通过创建一个一个的ListTile来实现列表
List<Widget> _getData(){
return [
ListTile(
title: Text("我是一个列表"),
),
ListTile(
title: Text("我是一个列表"),
),
ListTile(
title: Text("我是一个列表"),
),
ListTile(
title: Text("我是一个列表"),
)
];
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return ListView(
children: this._getData(),
);
}
}
【2】方式二:通过List集合,来动态生成ListTile数据
...
...
class HomeContent extends StatelessWidget{
//方式二:通过List集合,来动态生成ListTile数据
List<Widget> _getData(){
List<Widget> list = new List();
for(var i=0;i<20;i++){
list.add(ListTile(
title: Text("我是$i列表"),
));
}
return list;
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return ListView(
children: this._getData( ),
);
}
}
【3】方式三:通过Map集合,来动态生成ListTile数据
现在外部创建好数据:就拿我当前是在lib/res/listData.dart
List listData=[
{
"title":'Candy Shop',
"author":'Mohamed Chahin',
"imageUrl":'https://www.itying.com/images/flutter/1.png',
},
{
"title":'Childhood in a picture',
"author":'Google',
"imageUrl":'https://www.itying.com/images/flutter/2.png',
},
{
"title":'Alibaba Shop',
"author":'Alibaba',
"imageUrl":'https://www.itying.com/images/flutter/3.png',
},
{
"title":'Candy Shop',
"author":'Mohamed Chahin',
"imageUrl":'https://www.itying.com/images/flutter/4.png',
},
{
"title":'Tornado',
"author":'Mohamed Chahin',
"imageUrl":'https://www.itying.com/images/flutter/5.png',
}
];
记得引入:import ‘res/listData.dart’;
class HomeContent extends StatelessWidget{
//方式三:通过Map集合,来动态生成ListTile数据
List<Widget> _getData(){
var tempList = listData.map((value){ //首先我要说明一下,因为它返回的是一个map类型的值,所以我们就用var让它自动判断来接收值
return ListTile( //其次再来分析这个return,这里它的作用是,每次返回改变后的值赋给tempList
leading: Image.network(value["imageUrl"]),
title: Text(value["title"]),
subtitle: Text(value["author"]),
);
});
return tempList.toList(); //最后再返回这个值,但是要记住,他不是一个集合数组,所以我们要让它转换下类型,通过toList
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return ListView(
children: this._getData( ),
);
}
}
【4】方式四:通过builder规范,让ListView自动循环遍历 假数据
...
...
class HomeContent extends StatelessWidget{
//注意这里是在实例化构造方法的时候就执行了,所以是先创建好数据,再通过下面的方式四来遍历获取数据
List list = new List();
HomeContent(){
for(var i=0;i<20;i++){
this.list.add('我是第$i条');
}
}
@override
Widget build(BuildContext context) {
// TODO: implement build
//方式四:通过builder规范,让ListView自动循环遍历数据
return ListView.builder(
itemCount: this.list.length, //这里必须要指定List的长度
itemBuilder:(context,index){ //需要传入两个参数,然后Builder会自动从0一直循环到最大长度
return ListTile(
title: Text(this.list[index]), //每次取出index的索引对应的数据返回
);
},
);
}
}
【5】方式五:通过builder规范,让ListView自动循环遍历 动态真实数据
class HomeContent extends StatelessWidget{
//自定义方法
Widget _getListData(context,index){
return ListTile(
title: Text(listData[index]["title"]), //每次取出index的索引对应的数据返回
leading: Image.network(listData[index]["imageUrl"]),
subtitle: Text(listData[index]["author"])
);
}
@override
Widget build(BuildContext context) {
// TODO: implement build
//方式四:通过builder规范,让ListView自动循环遍历数据
return ListView.builder(
itemCount: listData.length, //这里必须要指定List的长度
itemBuilder:this._getListData //☆这里要注意写法,我们只是把方法名后面的东西赋值给itemBuilder,并不是要执行方法,所以一定要搞清楚!
//赋值的目的是在于将代码抽离出去,简洁明了
);
}
5.3、知识点
1、Flutter 列表组件概述
列表布局是我们项目开发中最常用的一种布局方式。Flutter 中我们可以通过 ListView 来定义
列表项,支持垂直和水平方向展示。通过一个属性就可以控制列表的显示方向。列表有一下
分类:
1、垂直列表
2、垂直图文列表
3、水平列表
4、动态列表
5、矩阵式列表
2、 Flutter 列表参数
名称 类型 说明
scrollDirection Axis Axis.horizontal 水平列表
Axis.vertical 垂直列表
padding EdgeInsetsGeometry 内边距
resolve bool 组件反向排序
children List<Widget> 列表元素
6、网格布局
6.1、效果图
【1】简单的网格布局
【2】循环遍历假数据,实现网格布局
【3】GridView.count 动态获取数据并实现网格布局
【4】GridView.builder 动态获取数据并实现网格布局
6.2、 代码+注释
【1】简单的网格布局
import 'package:flutter/material.dart';
void main()=>runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home:Scaffold(
appBar: AppBar(
title: Text('Flutter Demo'),
),
body: HomeContent(),
)
);
}
}
class HomeContent extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return GridView.count( //网格组件
crossAxisCount: 2, //控制网格列数
children: <Widget>[ //控制网格元素
Text('这是一个文本'), //孩子元素
Text('这是一个文本'),
Text('这是一个文本'),
Text('这是一个文本'),
Text('这是一个文本'),
Text('这是一个文本'),
Text('这是一个文本'),
Text('这是一个文本'),
Text('这是一个文本')
],
);
}
}
【2】循环遍历假数据,实现网格布局
...
...
class HomeContent extends StatelessWidget{
List<Widget> _getlistData(){
List<Widget> list = new List();
for(var i=0;i<20;i++){
list.add(Container(
alignment: Alignment.center,
child: Text(
'这是第$i条数据',
style: TextStyle(color: Colors.white,fontSize: 20),
),
color: Colors.blue,
// height: 400, //设置高度没有反应,
));
}
return list;
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return GridView.count( //网格组件
crossAxisSpacing: 20.0, //水平子Widget 之间的间距
mainAxisSpacing: 20.0, //垂直子Widget 之间的间距
padding: EdgeInsets.all(10), //与View上下左右间隔10
crossAxisCount: 2, //控制网格列数
childAspectRatio: 0.7, //宽度和高度的比例
children: this._getlistData() //返回的是Widget
);
}
}
【3】GridView.count 动态获取数据并实现网格布局
在开头记得导入外部数据的dart文件,如下:
List listData=[
{
"title":'Candy Shop',
"author":'Mohamed Chahin',
"imageUrl":'https://www.itying.com/images/flutter/1.png',
},
{
"title":'Childhood in a picture',
"author":'Google',
"imageUrl":'https://www.itying.com/images/flutter/2.png',
},
{
"title":'Alibaba Shop',
"author":'Alibaba',
"imageUrl":'https://www.itying.com/images/flutter/3.png',
},
{
"title":'Candy Shop',
"author":'Mohamed Chahin',
"imageUrl":'https://www.itying.com/images/flutter/4.png',
},
{
"title":'Tornado',
"author":'Mohamed Chahin',
"imageUrl":'https://www.itying.com/images/flutter/5.png',
}
];
main.dart
import 'package:flutter/material.dart';
import 'res/listData.dart';
...
...
class HomeContent extends StatelessWidget{
List<Widget> _getlistData(){
var tempList = listData.map((value){
return Container(
child: Column( //注意,这里也可以用ListView来获取数据,但是ListView的不足在它会自适应宽度,也就是平铺整个宽度
children: <Widget>[ //但是,Column不会,他只会显示元素多大就是多大,而且是自动垂直排列
Image.network(value['imageUrl']),
SizedBox(height: 10,), //让Text与Image有10的间距
Text(value['title'],
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15
),
)
],
),
decoration: BoxDecoration( //边框类
border: Border.all( //设置边框样式
color: Color.fromRGBO(233, 233, 233, 0.9), //颜色
width: 1 //边框宽度
)
),
);
});
return tempList.toList();
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return GridView.count( //网格组件 — 列表布局
crossAxisSpacing: 20.0, //水平子Widget 之间的间距
mainAxisSpacing: 20.0, //垂直子Widget 之间的间距
padding: EdgeInsets.all(10), //与View上下左右间隔10
crossAxisCount: 2, //控制网格列数
// childAspectRatio: 0.7, //宽度和高度的比例
children: this._getlistData() //返回的是Widget
);
}
}
【4】GridView.builder 动态获取数据并实现网格布局
import 'package:flutter/material.dart';
import 'res/listData.dart';
...
...
class HomeContent extends StatelessWidget{
Widget _getlistData(context,index){
return Container(
child: Column( //注意,这里也可以用ListView来获取数据,但是ListView的不足在它会自适应宽度,也就是平铺整个宽度
children: <Widget>[ //但是,Column不会,他只会显示元素多大就是多大,而且是自动垂直排列
Image.network(listData[index]['imageUrl']),
SizedBox(height: 10,), //让Text与Image有10的间距
Text(listData[index]['title'],
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15
),
)
],
),
decoration: BoxDecoration( //边框类
border: Border.all( //设置边框样式
color: Color.fromRGBO(233, 233, 233, 0.9), //颜色
width: 1 //边框宽度
)
),
);
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return GridView.builder( //网格组件 — 列表布局
/**
* 说明一下,这里写这个方法的作用是,因为当我们使用GridView.builder时,我们不能再将元素的样式放在外边,而是
* 应该要借助下面这个类,才能去填写样式,否则会报错
*/
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisSpacing: 20.0, //水平子Widget 之间的间距
mainAxisSpacing: 20.0, //垂直子Widget 之间的间距
crossAxisCount: 2, //控制网格列数
),
itemCount: listData.length, //数据长度
itemBuilder: this._getlistData
);
}
}
6.3、 知识点
GridView 组件的常用参数
当数据量很大的时候用矩阵方式排列比较清晰。此时我们可以用网格列表组件 GridView 实
现布局。
GridView 创建网格列表有多种方式,下面我们主要介绍两种。
1、可以通过 GridView.count 实现网格布局
2、通过 GridView.builder 实现网格布局
常用属性:
名称 类型 说明
scrollDirection Axis 滚动方法
padding EdgeInsetsGeometry 内边距
resolve bool 组件反向排序
crossAxisSpacing double 水平子 Widget 之间间距
mainAxisSpacing double 垂直子 Widget 之间间距
crossAxisCount int 一行的 Widget 数量
childAspectRatio double 子 Widget 宽高比例
children <Widget>[ ]
gridDelegate SliverGridDelegateWithFixedCrossAxisCount(常用)SliverGridDelegateWithMax
CrossAxisExtent 控制布局主要用在GridView.builder 里面
6.4、笔记
1、Container 不是布局,是容器!
2、网格组件,也叫列表布局
7、底部导航
效果图:
Home.dart
Category.dart
Setting.dart
main.dart:
import 'package:flutter/material.dart';
import 'pages/Tabs.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home: Tabs() //将代码全部抽离出去成一个Tabs组件
);
}
}
在lib根目录下,新建pages,再新建Tabs.dart文件
Tabs.dart
import 'package:flutter/material.dart';
import 'tabs/Category.dart';
import 'tabs/Home.dart';
import 'tabs/Setting.dart';
class Tabs extends StatefulWidget {
@override
_TabsState createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex=0;
List _pageList = [ //先将所有页面放到List集合内
HomePage(),
CategoryPage(),
SettingPage()
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('FlutterDemo'),
),
body: this._pageList[this._currentIndex], //再根据下标获取对应页面设置到body 里
bottomNavigationBar: BottomNavigationBar( //自定义底部导航条
currentIndex: this._currentIndex, //配置对应的索引值选中
onTap: (int index){
setState(() { //改变状态
this._currentIndex=index; //更改选中的Tab坐标
});
},
iconSize: 45.0, //Icon的大小,默认在20左右
fixedColor: Colors.red, //选中的颜色,默认是蓝色
type: BottomNavigationBarType.fixed, //配置底部tabs可以有多个按钮,默认是只能至多只能放三个,三个以上时,需要加上这句代码
items:[
BottomNavigationBarItem( //设置导航项
icon:Icon(Icons.home),
title: Text('首页')
),
BottomNavigationBarItem( //设置导航项
icon:Icon(Icons.category),
title: Text('分类')
),
BottomNavigationBarItem( //设置导航项
icon:Icon(Icons.settings),
title: Text('设置')
),
]
),
);
}
}
分别新建三个页面,在pages根目录下新建tabs文件夹,在这个文件夹里,分别新建三个文件:Category.dart、Home.dart、Setting.dart
Category.dart
import 'package:flutter/material.dart';
class CategoryPage extends StatefulWidget {
@override
_CategoryPageState createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
@override
Widget build(BuildContext context) {
return Text('分类');
}
}
Home.dart
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Text('我是首页组件');
}
}
Setting.dart
import 'package:flutter/material.dart';
class SettingPage extends StatefulWidget {
@override
_SettingPageState createState() => _SettingPageState();
}
class _SettingPageState extends State<SettingPage> {
@override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
ListTile(
title: Text('我是一个文本'),
),
ListTile(
title: Text('我是一个文本'),
),
ListTile(
title: Text('我是一个文本'),
),
ListTile(
title: Text('我是一个文本'),
),
ListTile(
title: Text('我是一个文本'),
)
],
);
}
}
8、页面跳转、跳转传值(普通路由、普通路由传值)
效果图:
Home.dart
Search.dart
Category.dart
Form.dart
底部导航基本框架在:flutter实现底部导航
好的,做好准备工作,我们就开始进入正轨,开始实现页面跳转
Home.dart
import 'package:flutter/material.dart';
import '../Search.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('跳转到搜索页面'),
onPressed: (){ //监听器
//路由跳转
Navigator.of(context).push(
MaterialPageRoute(
builder: (context)=>SearchPage()
)
);
},
color: Theme.of(context).accentColor,
textTheme: ButtonTextTheme.primary,
),
SizedBox(height: 20,),
],
);
}
}
Category.dart
import 'package:flutter/material.dart';
import '../Form.dart';
class CategoryPage extends StatefulWidget {
@override
_CategoryPageState createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start, //次轴居左
mainAxisAlignment: MainAxisAlignment.center, //主轴居中
children: <Widget>[
RaisedButton(
child: Text('跳转到表单页面'),
onPressed: () { //监听器
Navigator.of(context).push(
MaterialPageRoute(
builder: (context)=>FormPage(title: '我是跳转传值',)
/**
* 相当于:builder:(context){
* return FormPage();
* }
*/
)
);
},
color: Theme.of(context).accentColor,
textTheme: ButtonTextTheme.primary,
)
],
);
}
}
在lib→pages目录下新建这两个文件:
Search.dart
import 'package:flutter/material.dart';
class SearchPage extends StatefulWidget {
@override
_SearchPageState createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('搜索页面'),
),
body: Text('搜索页面内容区域'),
);
}
}
Form.dart
import 'package:flutter/material.dart';
class FormPage extends StatelessWidget {
String title;
FormPage({this.title='表单'}); //参数是一个可选参数,默认值是表单,当接收到值时,表单值直接被覆盖
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton( //悬浮按钮
child: Text('返回'),
onPressed: (){ //监听器
Navigator.of(context).pop(); //退出当前堆,返回上一级页面
},
),
appBar: AppBar(
title: Text(this.title),
),
body: ListView(
children: <Widget>[
ListTile(
title: Text('我是表单页面'),
),
ListTile(
title: Text('我是表单页面'),
),
ListTile(
title: Text('我是表单页面'),
),
ListTile(
title: Text('我是表单页面'),
),
],
),
);
}
}
9、页面跳转、跳转传值(命名路由、命名路由传值)
效果图:
Home.dart
Product.dart
ProductInfo.dart
底部导航基本框架参考:flutter实现底部导航
其他按钮界面参考上一节:flutter实现页面跳转、跳转传值(普通路由、普通路由传值)
我先把所有相关的文件夹与文件页面位置截图给你们看,方便你们理解,其他重复代码你们参考上面两个章节去学习一下,就会了,加油,各位!
然后,我这里就直接贴命名路由的相关代码了:
main.dart 修改
import 'package:flutter/material.dart';
import 'routes/Routes.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
initialRoute: '/', //初始化的时候加载的路由
// home: Tabs(), //将代码全部抽离出去成一个Tabs组件,再抽离成一个初始化路由组件,在上方 initialRoute: '/',
//配置命名路由
onGenerateRoute: oonGenerateRoute //将抽离出去的路由传值规范赋值给左边,而不是执行,记住喔!
);
}
}
Home.dart
import 'package:flutter/material.dart';
import '../Search.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('跳转到搜索页面'),
onPressed: (){ //监听器
//命名路由跳转
Navigator.pushNamed(context, '/search',arguments: { //我们必须要用arguments 工具来携带id这个键,才能在那边取出值
"id":123
});
},
color: Theme.of(context).accentColor,
textTheme: ButtonTextTheme.primary,
),
SizedBox(height: 20,),
RaisedButton(
child: Text('跳转到商品页面'),
onPressed: (){ //监听器
//命名路由跳转
Navigator.pushNamed(context, '/product');
},
color: Theme.of(context).accentColor,
textTheme: ButtonTextTheme.primary,
),
],
);
}
}
Routes.dart
import '../pages/Form.dart';
import '../pages/Search.dart';
import '../pages/Tabs.dart';
import '../pages/Product.dart';
import '../pages/ProductInfo.dart';
import 'package:flutter/material.dart';
//配置路由
final routes={ //配置命名路由
'/':(context)=>Tabs(), //命名路由传值 arguments工具是必须的
'/form':(context)=>FormPage(),
'/product':(context)=>ProductPage(), //命名路由传值 arguments工具是必须的
'/productInfoPage':(context,{arguments})=>ProductInfoPage(arguments:arguments), //命名路由传值 arguments工具是必须的
'/search':(context,{arguments})=>SearchPage(arguments:arguments) //命名路由传值 arguments工具是必须的
};
//固定写法
var oonGenerateRoute=(RouteSettings settings) {
// 统一处理
final String name = settings.name; //得到命名路由的名字,例如:'/form'
final Function pageContentBuilder = routes[name]; //得到命名路由的键去获取值,例如:(context)=>FormPage(),
if (pageContentBuilder != null) {
if (settings.arguments != null) {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context, arguments: settings.arguments));
return route;
} else {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context));
return route;
}
}
};
Tabs.dart不变
Product.dart
import 'package:flutter/material.dart';
class ProductPage extends StatefulWidget {
@override
_ProductPageState createState() => _ProductPageState();
}
class _ProductPageState extends State<ProductPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('商品详情'),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('跳转到商品详情'),
onPressed: (){ //监听器
Navigator.pushNamed(context, '/productInfoPage',arguments: {
'pid':456
});
},
color: Theme.of(context).accentColor,
textTheme: ButtonTextTheme.primary,
),
],
),
);
}
}
ProductInfo.dart
import 'package:flutter/material.dart';
class ProductInfoPage extends StatefulWidget {
final Map arguments;
ProductInfoPage({Key key,this.arguments}) : super(key: key);
@override
_ProductInfoPageState createState() => _ProductInfoPageState(arguments:this.arguments);
}
class _ProductInfoPageState extends State<ProductInfoPage> {
Map arguments;
_ProductInfoPageState({this.arguments});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('商品详情'),
),
body: Container(
child: Text('pid=${arguments['pid']}'),
),
);
}
}
10、替换路由、返回到根路由
效果图:
Setting.dart
RegisterFirst.dart
RegisterSecond.dart
RegisterThird.dart
无相关的页面代码架构请参考:flutter实现页面跳转、跳转传值(命名路由、命名路由传值)
主要变动:
这里负责贴主要代码:
Login.dart
import 'package:flutter/material.dart';
class LoginPage extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('登录'),
),
body: Center(
child: Column(
children: <Widget>[
SizedBox(height: 40,),
Text('这是一个登录页面,点击登录会执行登录操作'),
RaisedButton(
child: Text('登录'),
onPressed: (){
},
)
],
),
),
);
}
}
RegisterFirst.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class RegisterFirstPage extends StatelessWidget {
const RegisterFirstPage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("第一步-输入手机号")
),
body:Column(
children: <Widget>[
SizedBox(height: 40),
Text("这是注册的第一步,请输入您的手机号 然后点击下一步"),
SizedBox(height: 40),
RaisedButton(
child: Text('下一步'),
onPressed: (){
// Navigator.pushNamed(context, '/registerSecond');
//替换路由
// 表示点击按钮时跳转到第二个页面,并且是一种替换,简单来说可以理解为被销毁的意思
Navigator.of(context).pushReplacementNamed('/registerSecond');
},
)
],
)
);
}
}
RegisterSecond.dart
import 'package:flutter/material.dart';
class RegisterSecondPage extends StatelessWidget {
const RegisterSecondPage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("第二步-验证码")
),body:Column(
children: <Widget>[
SizedBox(height: 40),
Text("输入验证码完成注册"),
SizedBox(height: 40),
RaisedButton(
child: Text('下一步'),
onPressed: (){
Navigator.pushNamed(context, '/registerThird');
//替换路由
// Navigator.of(context).pushReplacementNamed('/registerThird');
},
)
],
)
);
}
}
RegisterThird.dart
import 'package:flutter/material.dart';
import 'package:flutter_app15/pages/Tabs.dart';
class RegisterThirdPage extends StatelessWidget {
const RegisterThirdPage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("第三步-完成注册")
),body:Column(
children: <Widget>[
SizedBox(height: 40),
Text("输入密码完成注册"),
SizedBox(height: 40),
RaisedButton(
child: Text('确定'),
onPressed: (){
/**
* 返回根 pushAndRemoveUntil
* 实现原理:
* 1、首先先将所有的路由全部置为空 (route) => route == null
* 2、然后再跳转回根路由 new MaterialPageRoute(builder: (context) => new Tabs()),
*/
Navigator.of(context).pushAndRemoveUntil(
new MaterialPageRoute(builder: (context) => new Tabs(index:1)),
(route) => route == null
);
},
)
],
)
);
}
}
Setting.dart
import 'package:flutter/material.dart';
class SettingPage extends StatefulWidget {
@override
_SettingPageState createState() => _SettingPageState();
}
class _SettingPageState extends State<SettingPage> {
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Column(
children: <Widget>[
ListTile(
title: Text('我是一个文本'),
),
ListTile(
title: Text('我是一个文本'),
),
ListTile(
title: Text('我是一个文本'),
),
ListTile(
title: Text('我是一个文本'),
),
ListTile(
title: Text('我是一个文本'),
)
],
),
RaisedButton(
child: Text('跳转到登录页面'),
onPressed: (){
Navigator.pushNamed(context, '/login');
},
),
RaisedButton(
child: Text('跳转到注册页面'),
onPressed: (){
Navigator.pushNamed(context, '/registerFirst');
},
)
],
);
}
}
Routes.dart
import 'package:flutter_app15/pages/user/RegisterThird.dart';
import '../pages/Form.dart';
import '../pages/Search.dart';
import '../pages/Tabs.dart';
import '../pages/Product.dart';
import '../pages/ProductInfo.dart';
import '../pages/user/Login.dart';
import '../pages/user/RegisterFirst.dart';
import '../pages/user/RegisterSecond.dart';
import '../pages/user/RegisterThird.dart';
import 'package:flutter/material.dart';
//配置路由
final routes={ //配置命名路由
'/':(context)=>Tabs(), //命名路由传值 arguments工具是必须的
'/form':(context)=>FormPage(),
'/product':(context)=>ProductPage(), //命名路由传值 arguments工具是必须的
'/productInfoPage':(context,{arguments})=>ProductInfoPage(arguments:arguments), //命名路由传值 arguments工具是必须的
'/search':(context,{arguments})=>SearchPage(arguments:arguments), //命名路由传值 arguments工具是必须的
'/login':(context)=>LoginPage(),
'/registerFirst':(context)=>RegisterFirstPage(),
'/registerSecond':(context)=>RegisterSecondPage(),
'/registerThird':(context)=>RegisterThirdPage()
};
//固定写法
var oonGenerateRoute=(RouteSettings settings) {
// 统一处理
final String name = settings.name; //得到命名路由的名字,例如:'/form'
final Function pageContentBuilder = routes[name]; //得到命名路由的键去获取值,例如:(context)=>FormPage(),
if (pageContentBuilder != null) {
if (settings.arguments != null) {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context, arguments: settings.arguments));
return route;
} else {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context));
return route;
}
}
};
11、自定义AppBar 定义顶部Tab切换、底部Tab结合顶部Tab实现类似头条页面布局
效果图:
Home.dart
AppBarDemo.dart
Categroy.dart
底部导航基本架构参考:flutter实现底部导航
main.dart
import 'package:flutter/material.dart';
import 'routes/Routes.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
debugShowCheckedModeBanner: false, //去掉debug图标
initialRoute: '/', //初始化的时候加载的路由
// home: Tabs(), //将代码全部抽离出去成一个Tabs组件,再抽离成一个初始化路由组件,在上方 initialRoute: '/',
//配置命名路由
onGenerateRoute: oonGenerateRoute //将抽离出去的路由传值规范赋值给左边,而不是执行,记住喔!
);
}
}
Routes.dart
import '../pages/Tabs.dart';
import 'package:flutter/material.dart';
import '../pages/AppBarDemo.dart';
//配置路由
final routes={ //配置命名路由
'/':(context)=>Tabs(), //命名路由传值 arguments工具是必须的
'/appBarDemo':(context)=>AppBarDemoPage()
};
//固定写法
var oonGenerateRoute=(RouteSettings settings) {
// 统一处理
final String name = settings.name; //得到命名路由的名字,例如:'/form'
final Function pageContentBuilder = routes[name]; //得到命名路由的键去获取值,例如:(context)=>FormPage(),
if (pageContentBuilder != null) {
if (settings.arguments != null) {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context, arguments: settings.arguments));
return route;
} else {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context));
return route;
}
}
};
Tabs.dart
import 'package:flutter/material.dart';
import 'tabs/Category.dart';
import 'tabs/Home.dart';
import 'tabs/Setting.dart';
class Tabs extends StatefulWidget {
final index; //用来作为返回根路由时,判断要显示第几个底部导航项的坐标
Tabs({Key key,this.index=0}) : super(key: key); //可选参数,默认是0
@override
_TabsState createState() => _TabsState(this.index); //把当前坐标通过_TabsState构造方法传给_TabsState类
}
class _TabsState extends State<Tabs> {
int _currentIndex;
_TabsState(index){ //接收Tabs 类调用时,传过来的参数,赋值给 _currentIndex
this._currentIndex=index;
}
List _pageList = [ //先将所有页面放到List集合内
HomePage(),
CategoryPage(),
SettingPage()
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('FlutterDemo'),
),
body: this._pageList[this._currentIndex], //再根据下标获取对应页面设置到body 里
bottomNavigationBar: BottomNavigationBar( //自定义底部导航条
currentIndex: this._currentIndex, //配置对应的索引值选中
onTap: (int index){
setState(() { //改变状态
this._currentIndex=index; //更改选中的Tab坐标
});
},
// iconSize: 45.0, //Icon的大小,默认在20左右
fixedColor: Colors.red, //选中的颜色,默认是蓝色
items:[
BottomNavigationBarItem( //设置导航项
icon:Icon(Icons.home),
title: Text('首页')
),
BottomNavigationBarItem( //设置导航项
icon:Icon(Icons.category),
title: Text('分类')
),
BottomNavigationBarItem( //设置导航项
icon:Icon(Icons.settings),
title: Text('设置')
)
]
),
);
}
}
AppBarDemo.dart
import 'package:flutter/material.dart';
class AppBarDemoPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DefaultTabController( //顶部导航切换
length: 2, //必须配置:顶部图标一共多少个
child: Scaffold(
appBar: AppBar(
title: Text('AppBarDemoPage'),
backgroundColor: Colors.red, //设置导航上的背景颜色
centerTitle: true, //设置:无论是在Android 还是 ios 上,标题都是居中显示
// leading: Icon(Icons.menu), //给导航左边添加图标,默认是返回图标,无法监听
// leading: IconButton(
// icon: Icon(Icons.menu), //给导航左边添加图标,默认是返回图标,可以监听
// onPressed: (){
// print('menu');
// },
// ),
// actions: <Widget>[ //右侧添加图标按钮
// IconButton(
// icon: Icon(Icons.search),
// onPressed: (){
// print('search');
// },
// ),
// IconButton( //右侧添加第二个图标按钮
// icon: Icon(Icons.settings),
// onPressed: (){
// print('settings');
// },
// )
// ],
bottom: TabBar(
tabs: <Widget>[ //配置Tabs菜单,系统会根据这里配置的是顺序对应下边body:TabBarView 里元素的顺序进行显示
Tab(text: '热门',),
Tab(text: '推荐',)
],
),
),
body: TabBarView(
children: <Widget>[
ListView(
children: <Widget>[
ListTile(
title: Text('第一个tab'),
),
ListTile(
title: Text('第一个tab'),
),
ListTile(
title: Text('第一个tab'),
)
],
),
ListView(
children: <Widget>[
ListTile(
title: Text('第二个tab'),
),
ListTile(
title: Text('第二个tab'),
),
ListTile(
title: Text('第二个tab'),
)
],
)
],
),
),
);
}
}
Home.dart
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('跳转到appBar'),
onPressed: (){
//路由跳转
Navigator.pushNamed(context, '/appBarDemo');
},
)
],
),
);
}
}
Categroy.dart
import 'package:flutter/material.dart';
class CategoryPage extends StatefulWidget {
@override
_CategoryPageState createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 4,
child: Scaffold( //在Scaffold 里再嵌套Scaffold
appBar: AppBar(
backgroundColor: Colors.black26,
/**
* 将顶部导航写在title里是为了防止,当Scaffold嵌套Scaffold时,发生两个顶部的bug,为了修复这个bug,
* 我们就可以把顶部导航条写在title里
*/
title: Row(
children: <Widget>[
Expanded(
child: TabBar(
indicatorColor: Colors.blue, //设置指示器的颜色
labelColor: Colors.blue, //设置:选中颜色
unselectedLabelColor: Colors.white, //设置:未选中颜色
indicatorSize: TabBarIndicatorSize.label, //选中时,底部指示条与文字一样长,默认是tab
tabs: <Widget>[
Tab(text: '热销'),
Tab(text: '推荐',),
Tab(text: '三',),
Tab(text: '四',)
],
),
)
],
),
),
body: TabBarView(
children: <Widget>[
ListView(
children: <Widget>[
ListTile(
title: Text('第一个tab'),
),
ListTile(
title: Text('第一个tab'),
),
ListTile(
title: Text('第一个tab'),
)
],
),
ListView(
children: <Widget>[
ListTile(
title: Text('第二个tab'),
),
ListTile(
title: Text('第二个tab'),
),
ListTile(
title: Text('第二个tab'),
)
],
),
ListView(
children: <Widget>[
ListTile(
title: Text('第三个tab'),
),
ListTile(
title: Text('第三个tab'),
),
ListTile(
title: Text('第三个tab'),
)
],
),
ListView(
children: <Widget>[
ListTile(
title: Text('第四个tab'),
),
ListTile(
title: Text('第四个tab'),
),
ListTile(
title: Text('第四个tab'),
)
],
)
],
),
),
);
}
}
Demo目录结构:
12、TabController定义顶部tab切换,并介绍生命周期函数
效果图:
Home.dart
TabBarController.dart
Home.dart
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('跳转到appBar'),
onPressed: (){
//路由跳转
Navigator.pushNamed(context, '/appBarDemo');
},
),
SizedBox(width: 10,),
RaisedButton(
child: Text('TabController定义顶部tab切换 '),
onPressed: (){
//路由跳转
Navigator.pushNamed(context, '/tabBarController');
},
)
],
),
);
}
}
TabBarController.dart
import 'package:flutter/material.dart';
class TabBarControllerPage extends StatefulWidget {
@override
_TabBarControllerPageState createState() => _TabBarControllerPageState();
}
class _TabBarControllerPageState extends State<TabBarControllerPage> with SingleTickerProviderStateMixin {
TabController _tabController; //第二种配置顶部导航的方式、
@override
void dispose() { //声明周期函数,销毁时调用
// TODO: implement dispose
super.dispose();
_tabController.dispose(); //销毁时,把_tabController也给销毁
}
@override
void initState() { //生命周期函数,初始化时,自动调用
// TODO: implement initState
super.initState();
_tabController=new TabController(length: 2, vsync: this); //调用构造方法初始化时,进行实例化,参数一是长度,参数二是固定写法
_tabController.addListener((){
print(_tabController.index); //监听改变时的下标
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('TabBarControllerpage'),
bottom: TabBar(
controller: this._tabController, //注意,这里是不一样的地方,要让controller=上面的_tabController
tabs: <Widget>[
Tab(text:"热销"),
Tab(text:"推荐"),
],
),
),
body: TabBarView(
controller: this._tabController, //注意这里也需要配置
children: <Widget>[
Center(child: Text('热销'),),
Center(child: Text('推荐'),),
],
),
);
}
}
还有记得配置命名路由哟:
Routes.dart
import '../pages/Tabs.dart';
import 'package:flutter/material.dart';
import '../pages/AppBarDemo.dart';
import '../pages/TabBarController.dart';
//配置路由
final routes={ //配置命名路由
'/':(context)=>Tabs(), //命名路由传值 arguments工具是必须的
'/appBarDemo':(context)=>AppBarDemoPage(),
'/tabBarController':(context)=>TabBarControllerPage()
};
//固定写法
var oonGenerateRoute=(RouteSettings settings) {
// 统一处理
final String name = settings.name; //得到命名路由的名字,例如:'/form'
final Function pageContentBuilder = routes[name]; //得到命名路由的键去获取值,例如:(context)=>FormPage(),
if (pageContentBuilder != null) {
if (settings.arguments != null) {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context, arguments: settings.arguments));
return route;
} else {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context));
return route;
}
}
};
13、Drawer侧边栏、以及侧边栏内容布局
效果图:
Tabs.dart
User.dart
Tabs.dart
项目结构:
main.dart
import 'package:flutter/material.dart';
import 'routes/Routes.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
debugShowCheckedModeBanner: false, //去掉debug图标
initialRoute: '/', //初始化的时候加载的路由
// home: Tabs(), //将代码全部抽离出去成一个Tabs组件,再抽离成一个初始化路由组件,在上方 initialRoute: '/',
//配置命名路由
onGenerateRoute: oonGenerateRoute //将抽离出去的路由传值规范赋值给左边,而不是执行,记住喔!
);
}
}
Routes.dart
import '../pages/Tabs.dart';
import 'package:flutter/material.dart';
import '../pages/User.dart';
//配置路由
final routes={ //配置命名路由
'/':(context)=>Tabs(), //命名路由传值 arguments工具是必须的
'/user':(context)=>UserPage()
};
//固定写法
var oonGenerateRoute=(RouteSettings settings) {
// 统一处理
final String name = settings.name; //得到命名路由的名字,例如:'/form'
final Function pageContentBuilder = routes[name]; //得到命名路由的键去获取值,例如:(context)=>FormPage(),
if (pageContentBuilder != null) {
if (settings.arguments != null) {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context, arguments: settings.arguments));
return route;
} else {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context));
return route;
}
}
};
User.dart 跳转到用户中心代码
import 'package:flutter/material.dart';
class UserPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('用户中心'),
),
);
}
}
Tabs.dart 侧边栏主要代码
import 'package:flutter/material.dart';
import 'tabs/Category.dart';
import 'tabs/Home.dart';
import 'tabs/Setting.dart';
class Tabs extends StatefulWidget {
final index; //用来作为返回根路由时,判断要显示第几个底部导航项的坐标
Tabs({Key key,this.index=0}) : super(key: key); //可选参数,默认是0
@override
_TabsState createState() => _TabsState(this.index); //把当前坐标通过_TabsState构造方法传给_TabsState类
}
class _TabsState extends State<Tabs> {
int _currentIndex;
_TabsState(index){ //接收Tabs 类调用时,传过来的参数,赋值给 _currentIndex
this._currentIndex=index;
}
List _pageList = [ //先将所有页面放到List集合内
HomePage(),
CategoryPage(),
SettingPage()
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('FlutterDemo'),
),
body: this._pageList[this._currentIndex], //再根据下标获取对应页面设置到body 里
bottomNavigationBar: BottomNavigationBar( //自定义底部导航条
currentIndex: this._currentIndex, //配置对应的索引值选中
onTap: (int index){
setState(() { //改变状态
this._currentIndex=index; //更改选中的Tab坐标
});
},
// iconSize: 45.0, //Icon的大小,默认在20左右
fixedColor: Colors.red, //选中的颜色,默认是蓝色
items:[
BottomNavigationBarItem( //设置导航项
icon:Icon(Icons.home),
title: Text('首页')
),
BottomNavigationBarItem( //设置导航项
icon:Icon(Icons.category),
title: Text('分类')
),
BottomNavigationBarItem( //设置导航项
icon:Icon(Icons.settings),
title: Text('设置')
)
]
),
drawer: Drawer( //左侧边栏
child: Column(
children: <Widget>[
Row( //这里使用Row和Expanded进行配合是为了,让底部的线水平铺满屏幕
children: <Widget>[
Expanded(
child: UserAccountsDrawerHeader(
accountName: Text('睿少帅哥'), //用户名
accountEmail: Text('ruishao@qq.com'), //邮箱
currentAccountPicture: CircleAvatar( //设置圆形头像
backgroundImage: NetworkImage('https://www.itying.com/images/flutter/3.png'),
),
decoration: BoxDecoration( //设置背景图片
image: DecorationImage(
image: NetworkImage('https://www.itying.com/images/flutter/2.png'),
fit: BoxFit.cover
)
),
otherAccountsPictures: <Widget>[ //将图片显示在头像右边
Image.network('https://www.itying.com/images/flutter/4.png'),
Image.network('https://www.itying.com/images/flutter/5.png'),
],
)
)
],
),
ListTile(
leading: CircleAvatar(
child: Icon(Icons.home),
),
title: Text('我的空间'),
),
Divider(),
ListTile(
leading: CircleAvatar(
child: Icon(Icons.people),
),
title: Text('用户中心'),
onTap: (){ //侧边栏监听器
Navigator.of(context).pop(); //隐藏侧边栏
Navigator.pushNamed(context, '/user');
},
),
Divider(),
ListTile(
leading: CircleAvatar(
child: Icon(Icons.settings),
),
title: Text('设置中心'),
),
Divider(),
],
),
),
endDrawer: Drawer( //右侧边栏
child: Text('右侧侧边栏'),
),
);
}
}
剩余:Category.dart、Home.dart、Setting.dart 属于与本章无关的代码,但是还是给你们贴出来一起学学,顺便整理成完整Demo,请参考:
Flutter实现TabController定义顶部tab切换,并介绍生命周期函数
14、按钮组件 RaisedButton、FlatButton、OutlineButton、IconButton、ButtonBar以及自定义按钮组件
效果图:
import 'package:flutter/material.dart';
class ButtonDemoPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('按钮演示页面'),
actions: <Widget>[
IconButton( //图标按钮
icon: Icon(Icons.settings),
onPressed: (){
},
)
],
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center, //垂直居中
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center, //水平居中
children: <Widget>[
RaisedButton(
child: Text('普通按钮'),
onPressed: (){
print('普通按钮');
},
),
SizedBox(width: 10,),
RaisedButton(
child: Text('颜色按钮'),
color: Colors.blue, //背景颜色
textColor: Colors.white, //字体白色
onPressed: (){
print('颜色按钮');
},
),
SizedBox(width: 10,),
RaisedButton(
child: Text('阴影按钮'),
color: Colors.blue, //背景颜色
textColor: Colors.white, //字体白色
elevation: 10, //设置阴影效果,值越大阴影效果越好
onPressed: (){
print('阴影按钮');
},
),
],
),
RaisedButton.icon(
// onPressed: null,
icon: Icon(Icons.search),
label: Text('图标按钮'),
color: Colors.blue,
textColor: Colors.white,
onPressed: (){
print('图标按钮');
},
),
SizedBox(height: 5,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container( //利用容器来设置按钮的宽度和高度
height: 50,
width: 300,
child: RaisedButton(
child: Text('宽度高度'),
color: Colors.blue,
textColor: Colors.white,
elevation: 20,
onPressed: (){
print('宽度高度');
},
),
)
],
),
SizedBox(height: 5,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded( //铺满屏幕宽度
child: Container( //利用容器设置高度
height: 80,
margin: EdgeInsets.all(10), //设置:左右间距10
child: RaisedButton(
child: Text('自适应按钮'),
color: Colors.blue,
textColor: Colors.white,
elevation: 20,
onPressed: (){
print('自适应按钮');
},
),
)
)
],
),
SizedBox(height: 10,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('圆角按钮'),
color: Colors.blue,
textColor: Colors.white,
elevation: 20,
shape: RoundedRectangleBorder( //圆角按钮
borderRadius: BorderRadius.circular(10) //圆角弧度
),
onPressed: (){
print('圆角按钮');
}),
SizedBox(width: 10,),
Container(
height: 80, //设置:高度、也可以理解为直径
child: RaisedButton(
child: Text('圆形按钮'),
color: Colors.blue,
textColor: Colors.white,
elevation: 20,
splashColor: Colors.red, //设置长按按钮时,水波纹颜色
shape: CircleBorder( //圆角按钮
side: BorderSide(
color: Colors.white
)
),
onPressed: (){
print('圆形按钮');
}),
),
FlatButton( //扁平按钮,默认是没有阴影,而且默认也没有背景颜色
child: Text('扁平按钮'),
color: Colors.blue,
textColor: Colors.yellow,
onPressed: (){
print('扁平化按钮');
},
)
],
),
SizedBox(height: 10,),
OutlineButton( //带边框按钮
child: Text('边框按钮'),
// color: Colors.red, 没有效果,这就是边框按钮的特性,不仅自带边框,还无法设置它的背景颜色,我猜可能是作者怕设置的背景颜色跟边框颜色一致
// textColor: Colors.yellow, //有效果
onPressed: (){
print('边框按钮');
},
),
SizedBox(height: 5,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded( //自适应水平平铺
child: Container(
margin: EdgeInsets.all(20), //上下左右分别间距 20
height: 50,
child: OutlineButton(
child: Text('注册'),
onPressed: (){
},
),
),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ButtonBar(
children: <Widget>[
RaisedButton(
child: Text('登录'),
color: Colors.blue,
textColor: Colors.white,
elevation: 20,
onPressed: (){
print('登录');
},
),
RaisedButton(
child: Text('注册'),
color: Colors.blue,
textColor: Colors.white,
elevation: 20,
onPressed: (){
print('注册');
},
),
MyButton(text:'自定义按钮',height: 60.0,width: 100,pressed: (){
print('自定义按钮');
})
],
)
],
)
],
),
);
}
}
//自定义按钮组件
class MyButton extends StatelessWidget {
final text;
final pressed;
final double width;
final double height;
const MyButton({this.text="",this.pressed=null,this.width=80.0,this.height=30.0});
@override
Widget build(BuildContext context) {
return Container(
height: this.height,
width: this.width,
child: RaisedButton(
child: Text(this.text),
onPressed: this.pressed,
),
);
}
}
15、类似闲鱼App底部导航凸起按钮
效果图:
底部导航栏基本架构请参考:
Flutter实现底部导航
我这里主要贴如何将中间的图标变成: 类似闲鱼App底部导航凸起按钮
Tabs.dart
import 'package:flutter/material.dart';
import 'tabs/Category.dart';
import 'tabs/Home.dart';
import 'tabs/Setting.dart';
class Tabs extends StatefulWidget {
final index; //用来作为返回根路由时,判断要显示第几个底部导航项的坐标
Tabs({Key key,this.index=0}) : super(key: key); //可选参数,默认是0
@override
_TabsState createState() => _TabsState(this.index); //把当前坐标通过_TabsState构造方法传给_TabsState类
}
class _TabsState extends State<Tabs> {
int _currentIndex;
_TabsState(index){ //接收Tabs 类调用时,传过来的参数,赋值给 _currentIndex
this._currentIndex=index;
}
List _pageList = [ //先将所有页面放到List集合内
HomePage(),
CategoryPage(),
SettingPage()
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('FlutterDemo'),
),
floatingActionButton: Container(
height: 70,
width: 70,
padding: EdgeInsets.all(8), //设置:内边距8
margin: EdgeInsets.only(top: 2), //设置:外边距2, 这样就可以让浮动按钮下来,贴近分类Tab文字
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40),
color: Colors.white
),
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: (){
setState(() { //可以实现重新渲染页面,因为_currentIndex变成了1,所以页面会跳转到分类页面
this._currentIndex=1; //点击浮动按钮时,切换到分类页面
});
},
backgroundColor: this._currentIndex==1?Colors.red:Colors.yellow //利用三目运算符,实现选中时,浮动按钮背景颜色变化
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, //浮动按钮居中并且位于界面底部
body: this._pageList[this._currentIndex], //再根据下标获取对应页面设置到body 里
bottomNavigationBar: BottomNavigationBar( //自定义底部导航条
currentIndex: this._currentIndex, //配置对应的索引值选中
onTap: (int index){
setState(() { //改变状态
this._currentIndex=index; //更改选中的Tab坐标
});
},
// iconSize: 45.0, //Icon的大小,默认在20左右
fixedColor: Colors.red, //选中的颜色,默认是蓝色
items:[
BottomNavigationBarItem( //设置导航项
icon:Icon(Icons.home),
title: Text('首页')
),
BottomNavigationBarItem( //设置导航项
icon:Icon(Icons.category),
title: Text('分类')
),
BottomNavigationBarItem( //设置导航项
icon:Icon(Icons.settings),
title: Text('设置')
)
]
),
);
}
}
16、单行文本框、多选框
效果图:
TextField.dart
CheckBox.dart
TextField.dart
import 'package:flutter/material.dart';
class TextFieldDemoPage extends StatefulWidget {
@override
_TextFieldDemoPageState createState() => _TextFieldDemoPageState();
}
class _TextFieldDemoPageState extends State<TextFieldDemoPage> {
var _username = new TextEditingController(); //初始化时给表单赋值
var _password; //初始化时不赋值
@override
void initState() {
// TODO: implement initState
super.initState();
_username.text='初始值'; //调用text赋值
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('表单演示页面'),
),
body: Padding(
padding: EdgeInsets.all(20),
// child: TextDemo(),
child: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(
hintText: '请输入用户名'
),
controller: _username, //通过唯一标识对象,初始化时赋值
onChanged: (value){ //文本框变化时的触发事件 , 如果发生改变,系统就会自动将输入框的值赋给value
setState(() {
_username.text = value;
});
},
),
SizedBox(height: 20,),
TextField(
obscureText: true, //开启密码模式
decoration: InputDecoration(
hintText: '请输入密码'
),
onChanged: (value){ //文本框变化时的触发事件 , 如果发生改变,系统就会自动将输入框的值赋给value
setState(() {
this._password = value; //注意,这里没有写.text,所以下面获取值的时候,也不需要写.text,直接写变量名即可
});
},
),
SizedBox(height: 40,),
Container(
width: double.infinity, //表示Container的宽度变成自适应宽度
height: 40,
child: RaisedButton(
child: Text('登录'),
onPressed: (){
print(this._username.text); //打印用户名
print(this._password); //打印密码
},
color: Colors.blue,
textColor: Colors.white,
),
)
],
),
),
);
}
}
//特殊效果举例,不过这里没有调用,要想看效果的话直接在body:TextDemo 即可
class TextDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
TextField(), //输入框表单
SizedBox(height: 20,),
TextField(
decoration: InputDecoration(
hintText: '请输入搜索的内容', //提示文字,相当于Android里的hint
border: OutlineInputBorder() //给表单四周添加边框
),
),
SizedBox(height: 20,),
TextField(
maxLines: 4, //设置对最大行数
decoration: InputDecoration(
hintText: '多行文本框', //多行文本框
border: OutlineInputBorder() //给表单四周添加边框
),
),
SizedBox(height: 20,),
TextField(
obscureText: true, //开启密码模式
decoration: InputDecoration(
hintText: '密码框', //多行文本框
border: OutlineInputBorder() //给表单四周添加边框
),
),
SizedBox(height: 20,),
TextField( //特殊效果,输入信息时,用户名动态跑到边框届提示
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: '用户名'
),
),
SizedBox(height: 20,),
TextField( //特殊效果,输入信息时,用户名动态跑到边框届提示
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: '密码'
),
),
SizedBox(height: 20,),
TextField( //在文本框前面加上图标
decoration: InputDecoration(
icon: Icon(Icons.people),
hintText: '请输入用户名'
),
)
],
),
);
}
} //列举文本框的样式与模式,要使用时在body里调用TextDemo组件即可
CheckBox.dart
import 'package:flutter/material.dart';
class CheckBoxPage extends StatefulWidget {
@override
_CheckBoxPageState createState() => _CheckBoxPageState();
}
class _CheckBoxPageState extends State<CheckBoxPage> {
var flag = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('checkbox'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(children: <Widget>[
Checkbox( //多选框组件
value: this.flag,
onChanged: (v){ //多选框值变化时,触发
setState(() {
this.flag=v;
});
},
activeColor: Colors.red, //设置:选中时的颜色
),
],),
Row(
children: <Widget>[
Text(this.flag?'选中':'未选中') //显示:checkBox当前是否勾选的值
],
),
SizedBox(height: 40,),
CheckboxListTile( //多选框组件
value: this.flag,
onChanged: (v){ //多选框值变化时,触发
setState(() {
this.flag=v;
});
},
title: Text('标题'),
subtitle: Text('这是二级标题'),
),
Divider(),
CheckboxListTile( //多选框组件
value: this.flag,
onChanged: (v){ //多选框值变化时,触发
setState(() {
this.flag=v;
});
},
title: Text('标题'),
subtitle: Text('这是二级标题'),
secondary: Icon(Icons.help),
)
],
),
);
}
}
17、多行文本框、开关按钮、多选框、单选按钮、RadioListTile、Radio、表单
效果图:
Radio.dart
FormDemo.dart
Radio.dart
import 'package:flutter/material.dart';
class RadioPage extends StatefulWidget {
@override
_RadioPageState createState() => _RadioPageState();
}
class _RadioPageState extends State<RadioPage> {
int sex=1;
bool flag = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Radio'),
),
body: Padding(
padding: EdgeInsets.all(20),
child: Column(
children: <Widget>[
// Row(
// children: <Widget>[
// Text('男: '),
// Radio( //类似单选按钮组
// value: 1,
// onChanged: (v){ //Radio改变时,触发事件
// setState(() { //重新渲染界面
// this.sex=v;
// });
// },
// groupValue: this.sex, //如果这里的值是一样的,说明都是属于同一个人单选按钮组
// ),
// SizedBox(width: 20,),
// Text('女: '),
// Radio( //类似单选按钮组
// value: 2,
// onChanged: (v){ //Radio改变时,触发事件
// setState(() { //重新渲染界面
// this.sex=v;
// });
// },
// groupValue: this.sex, //如果这里的值是一样的,说明都是属于同一个人单选按钮组
// )
// ],
// ),
// Row(
// children: <Widget>[
// Text('${this.sex}'), //打印下标
// Text(this.sex==1?'男':'女') //利用三目运算符,打印值
// ],
// ),
SizedBox(height: 40,),
RadioListTile(
value: 1,
onChanged: (v){ //Radio改变时,触发事件
setState(() { //重新渲染界面
this.sex=v;
});
},
groupValue: this.sex, //如果这里的值是一样的,说明都是属于同一个人单选按钮组
title: Text('标题'),
subtitle: Text('这是二级标题'),
secondary: Icon(Icons.help), //设置: 图标
selected: this.sex==1, //选中时,文字发亮
),
RadioListTile(
value: 2,
onChanged: (v){ //Radio改变时,触发事件
setState(() { //重新渲染界面
this.sex=v;
});
},
groupValue: this.sex, //如果这里的值是一样的,说明都是属于同一个人单选按钮组
title: Text('标题'),
subtitle: Text('这是二级标题'),
secondary: Image.network('https://www.itying.com/images/flutter/1.png'), //加载远程图片
selected: this.sex==2, //选中时,文字发亮
),
SizedBox(height: 40,),
Switch( //Android里的开关按钮
value: this.flag,
onChanged: (v){
setState(() {
print((v));
this.flag=v;
});
},
)
],
),
),
);
}
}
FormDemo.dart
import 'package:flutter/material.dart';
class FormDemoPage extends StatefulWidget {
@override
_FormDemoPageState createState() => _FormDemoPageState();
}
class _FormDemoPageState extends State<FormDemoPage> {
String username;
int sex=1; //默认1是男
String info = '';
List hobby=[
{
'checked':true,
'title':'吃饭'
},
{
'checked':false,
'title':'睡觉'
},
{
'checked':true,
'title':'写代码'
},
];
List<Widget> _getHobby(){ //返回多个checkBox集合
List<Widget> tempList=[];
for(var i=0;i<this.hobby.length;i++){
//写法一:
tempList.add(Text(this.hobby[i]['title']+":"));
tempList.add(
Checkbox(
value: this.hobby[i]['checked'],
onChanged: (value){
setState(() {
this.hobby[i]['checked']=value;
});
},
)
);
//写法二:
// tempList.add(
// Row(
// children: <Widget>[
// Text(this.hobby[i]['title']+":"),
// Checkbox(
// value: this.hobby[i]['checked'],
// onChanged: (value){
// setState(() {
// this.hobby[i]['checked']=value;
// });
// },
// )
// ],
// )
// );
}
return tempList;
}
void _sexChanged(value){ //将单选组按钮的监听器抽离出来
setState(() {
this.sex=value;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('学员信息登记系统'),
),
body: Padding(
padding: EdgeInsets.all(20),
child: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(
hintText: '输入用户信息'
),
onChanged: (value){
setState(() {
this.username=value;
});
},
),
SizedBox(height: 10,),
Row(
children: <Widget>[
Text('男'),
Radio(
value: 1,
onChanged: this._sexChanged,
groupValue: this.sex,
),
SizedBox(width: 20,),
Text('女'),
Radio(
value: 2,
onChanged: this._sexChanged,
groupValue: this.sex,
)
],
),
//爱好
SizedBox(height: 40,),
Wrap(
children: this._getHobby(),
),
SizedBox(height: 20,),
TextField(
maxLines: 4,
decoration: InputDecoration(
hintText: '描述信息',
border: OutlineInputBorder()
),
onChanged: (value){
setState(() {
this.info=value;
});
},
),
SizedBox(height: 40,),
Container(
width: double.infinity, //表示Container的宽度变成自适应宽度
height: 40,
child: RaisedButton(
child: Text('提交信息'),
onPressed: (){
print(this.sex); //获取单选按钮的值
print(this.username); //获取文本框的值
print(this.hobby); //获取多选框的值
print(this.info); //获取描述信息
},
color: Colors.blue,
textColor: Colors.white,
),
)
],
),
),
);
}
}
18、调用原生时间选择器、日期选择器、时间戳、Future异步
效果图:
参考第三方库:https://pub.dev/packages/date_format
在pubspec.yaml,引入最新的依赖,我这里是
在相关类引入包名:
import ‘package:date_format/date_format.dart’;
时间戳转化演示:
时间戳就是时间在服务器存储的值,他跟正常我们看得的值是不一样的
//日期转化成时间戳:
var now = new DateTime.now();
print(now.millisecondsSinceEpoch);//单位毫秒,13 位时间戳
//时间戳转化成日期:
var now = new DateTime.now();
var a=now.millisecondsSinceEpoch; //时间戳
print(DateTime.fromMillisecondsSinceEpoch(a));
DatePicker.dart
import 'package:flutter/material.dart';
import 'package:date_format/date_format.dart';
import 'package:flutter/material.dart' as prefix0;
class DatePickerPage extends StatefulWidget {
@override
_DatePickerPageState createState() => _DatePickerPageState();
}
class _DatePickerPageState extends State<DatePickerPage> {
DateTime _nowDate = DateTime.now();
var _nowTime = TimeOfDay(hour: 12,minute: 20); //初始化的时候时间值
_showDatePicker() async{ //Flutter自带的日期组件 //获取异步数据方式二:通过async异步方法获取
//获取异步数据方式一:通过then获取
// showDatePicker(
// context:context,
// initialDate:_nowDate, //当前日期
// firstDate:DateTime(1980), //起始日期
// lastDate:DateTime(2100) //结束日期
// ).then((result){
// print(result);
// });
/**
* await 配合 async完成异步流程
* await表示等待异步请求,请求完后就把值赋给result
* 我们再打印这个result即可拿到数据
*/
var result = await showDatePicker(
context:context,
initialDate:_nowDate, //当前日期
firstDate:DateTime(1980), //起始日期
lastDate:DateTime(2100), //结束日期
locale: prefix0.Locale('zh') // 非必须,如果操作系统是中文的话可以不加,如果不是的话,就要加上才能变成中文
);
// print(result);
setState(() {
this._nowDate = result; //将选择日期后的值赋给_nowDate
});
}
_showTimePicker() async{ //Flutter自带的时间组件
var result = await showTimePicker(
context:context,
initialTime: _nowTime
);
setState(() {
this._nowTime = result;
});
}
@override
void initState() {
// TODO: implement initState
super.initState();
// var now = DateTime.now();
// print(now); //2019-11-17 12:23:06.811117
// print(now.millisecondsSinceEpoch); //1573964623294
/* print(DateTime.fromMicrosecondsSinceEpoch(1573964623294)); //1970-01-19 13:12:44.623294*/
print(formatDate(DateTime.now(), [yyyy, '年', mm, '月', dd]));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('DatePickerDemo'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
InkWell( //让组件可以点击
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// Text('${_nowDate}'),
Text('${formatDate(_nowDate, [yyyy, '年', mm, '月', dd])}'), //使用第三方库按照指定形式显示数据
Icon(Icons.arrow_drop_down)
],
),
onTap: _showDatePicker,
),
InkWell( //让组件可以点击
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// Text('${_nowDate}'),
// Text('${_nowTime}'), //这样显示的值是:TimeOfDay(07:26) 并不是我们想要的,所以要改成下面
Text('${_nowTime.format(context)}'),
Icon(Icons.arrow_drop_down)
],
),
onTap: _showTimePicker,
)
],
)
],
),
);
}
}
这里可能会遇到关于调用后,时间还为英文的情况,针对这种情况,解决办法为:
flutter showDatePicker显示中文日期_Flutter时间控件显示中文
如果看不懂教程,我还提供了一些代码变动截图,你们照着改就行:
pubspec.yaml
main.dart
导入国际化的包 flutter_localizations
import ‘package:flutter_localizations/flutter_localizations.dart’;
DatePicker.dart
_showDatePicker() async{ //Flutter自带的日期组件 //获取异步数据方式二:通过async异步方法获取
//获取异步数据方式一:通过then获取
// showDatePicker(
// context:context,
// initialDate:_nowDate, //当前日期
// firstDate:DateTime(1980), //起始日期
// lastDate:DateTime(2100) //结束日期
// ).then((result){
// print(result);
// });
/**
* await 配合 async完成异步流程
* await表示等待异步请求,请求完后就把值赋给result
* 我们再打印这个result即可拿到数据
*/
var result = await showDatePicker(
context:context,
initialDate:_nowDate, //当前日期
firstDate:DateTime(1980), //起始日期
lastDate:DateTime(2100), //结束日期
locale: prefix0.Locale('zh') // 非必须,如果操作系统是中文的话可以不加,如果不是的话,就要加上才能变成中文
);
// print(result);
setState(() {
this._nowDate = result; //将选择日期后的值赋给_nowDate
});
}
19、调用第三方时间选择器、日期选择器、时间戳
效果图:
如果会遇到英文问题,请参考:
Flutter实现调用原生时间选择器、日期选择器、时间戳、Future异步
我在此篇博文已对解决方法进行了详细讲解
那么下面我们就开始这一章相关的知识
参考第三方库:
https://pub.dev/packages/flutter_cupertino_date_picker
配置:pubspec.yaml
在相关类引入包名:
import ‘package:flutter_cupertino_date_picker/flutter_cupertino_date_picker.dart’;
DatePickerPub.dart
import 'package:flutter/material.dart';
import 'package:date_format/date_format.dart';
import 'package:flutter_cupertino_date_picker/flutter_cupertino_date_picker.dart'; //第三方时间组件库
class DatePickerPubPage extends StatefulWidget {
@override
_DatePickerPubPageState createState() => _DatePickerPubPageState();
}
class _DatePickerPubPageState extends State<DatePickerPubPage> {
DateTime _dateTime = DateTime.now(); //获取当前日期
_showDatePicker(){
DatePicker.showDatePicker(
context,
pickerTheme: DateTimePickerTheme(
showTitle: true,
confirm: Text('确定', style: TextStyle(color: Colors.red)),
cancel: Text('取消', style: TextStyle(color: Colors.cyan)),
),
minDateTime: DateTime.parse("1980-05-12"), //起始日期
maxDateTime: DateTime.parse('2100-05-12'), //终止日期
initialDateTime: DateTime.now(), //获取当前时间
dateFormat: 'yyyy年M月d日 EEE,H时:m分', //设置:年-月-日的格式
pickerMode: DateTimePickerMode.datetime, // show TimePicker
locale: DateTimePickerLocale.zh_cn, //设置:语言-中文
onClose: () => print("----- onClose -----"), //取消监听器
// onCancel: () => print('onCancel'),
// onChange: (dateTime, List<int> index) { //滑动时间组件时,触发
// setState(() {
// _dateTime = dateTime;
// });
// },
onConfirm: (dateTime, List<int> index) { //确定监听器
setState(() {
_dateTime = dateTime;
});
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('DatePickerPubDemo'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
InkWell(
child: Row(
children: <Widget>[
Text('${formatDate(_dateTime, [yyyy, '年', mm, '月', dd," ",HH,":",nn])}'),
Icon(Icons.arrow_drop_down)
],
),
onTap: _showDatePicker
)
],
)
],
),
);
}
}
20、轮播图 flutter_swiper
效果图:
图1
图2
图3
图4
图5
第三方库参考:https://pub.dev/packages/flutter_swiper
配置 pubspec.yaml
在需要轮播的类引入,包名:
import ‘package:flutter_swiper/flutter_swiper.dart’;
图1、图2代码:
import 'package:flutter/material.dart';
import 'package:flutter_swiper/flutter_swiper.dart';
class SwiperPage extends StatefulWidget {
@override
_SwiperPageState createState() => _SwiperPageState();
}
class _SwiperPageState extends State<SwiperPage> {
List<Map> imgList = [ //设置: 轮播图的图片素材
{
"url":"https://www.itying.com/images/flutter/1.png"
},
{
"url":"https://www.itying.com/images/flutter/2.png"
},
{
"url":"https://www.itying.com/images/flutter/3.png"
},
{
"url":"https://www.itying.com/images/flutter/4.png"
},
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('轮播图组件演示'),
),
body: Column(
children: <Widget>[
/**
* 必须要套Container,才能使用轮播图,否则会报错
*/
Container(
// height: 150,
/**
* 用AspectRatio套住轮播图组件是为了让图片不变形,根据手机屏幕宽高比自适应,效果最佳
*/
// width: double.infinity, 还是会变形的话就加上这句
child: AspectRatio(
aspectRatio: 16/9,
child: Swiper(
itemBuilder: (BuildContext context,int index){ //每次循环遍历时,将i赋值给index
return new Image.network(
imgList[index]['url'],
fit: BoxFit.fill,);
},
itemCount: imgList.length,
pagination: new SwiperPagination(), //设置:分页器.
loop: true, //无限循环
autoplay: true, //图片自动轮播
// control: new SwiperControl(), //设置:左右的箭头
),
),
)
],
)
);
}
}
图3代码
import 'package:flutter/material.dart';
import 'package:flutter_swiper/flutter_swiper.dart';
class SwiperPage extends StatefulWidget {
@override
_SwiperPageState createState() => _SwiperPageState();
}
class _SwiperPageState extends State<SwiperPage> {
List<Map> imgList = [ //设置: 轮播图的图片素材
{
"url":"https://www.itying.com/images/flutter/1.png"
},
{
"url":"https://www.itying.com/images/flutter/2.png"
},
{
"url":"https://www.itying.com/images/flutter/3.png"
},
{
"url":"https://www.itying.com/images/flutter/4.png"
},
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('轮播图组件演示'),
),
body: Column(
children: <Widget>[
/**
* 必须要套Container,才能使用轮播图,否则会报错
*/
Container(
// height: 150,
/**
* 用AspectRatio套住轮播图组件是为了让图片不变形,根据手机屏幕宽高比自适应,效果最佳
*/
// width: double.infinity, 还是会变形的话就加上这句
child: AspectRatio(
aspectRatio: 16/9,
child: new Swiper(
layout: SwiperLayout.CUSTOM,
customLayoutOption: new CustomLayoutOption(
startIndex: -1,
stateCount: 3
).addRotate([
-45.0/180,
0.0,
45.0/180
]).addTranslate([
new Offset(-370.0, -40.0),
new Offset(0.0, 0.0),
new Offset(370.0, -40.0)
]),
itemWidth: 300.0,
itemHeight: 200.0,
itemBuilder: (context, index) {
return new Container(
// color: Colors.grey, 设置:轮播图背景
child: new Center(
child: Image.network(imgList[index]['url'],fit: BoxFit.contain,),
),
);
},
itemCount: imgList.length)
),
)
],
)
);
}
}
图4、图5代码
import 'package:flutter/material.dart';
import 'package:flutter_swiper/flutter_swiper.dart';
class SwiperPage extends StatefulWidget {
@override
_SwiperPageState createState() => _SwiperPageState();
}
class _SwiperPageState extends State<SwiperPage> {
List<Map> imgList = [ //设置: 轮播图的图片素材
{
"url":"https://www.itying.com/images/flutter/1.png"
},
{
"url":"https://www.itying.com/images/flutter/2.png"
},
{
"url":"https://www.itying.com/images/flutter/3.png"
},
{
"url":"https://www.itying.com/images/flutter/4.png"
},
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('轮播图组件演示'),
),
body: Swiper(
itemBuilder: (BuildContext context,int index){ //每次循环遍历时,将i赋值给index
return new Image.network(
imgList[index]['url'],
fit: BoxFit.fill,);
},
itemCount: imgList.length,
// pagination: new SwiperPagination(), 设置:分页器
// control: new SwiperControl(), 设置:左右的箭头
),
);
}
}
21、普通对话框、列表对话框、单选对话框、Toast提示
效果图:
Dialog.dart
这里我把所有的代码都写在了一个类里,不过要想实现Toast的功能,我们必须要引入第三方库,可参考:https://pub.dev/packages/fluttertoast
配置:pubspec.yaml
引入包名:import ‘package:fluttertoast/fluttertoast.dart’;
Dialog.dart
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
class DialogPage extends StatefulWidget {
@override
_DialogPageState createState() => _DialogPageState();
}
class _DialogPageState extends State<DialogPage> {
_alertDialog() async{
var result = await showDialog( //通过异步在外面获取值
context:context,
builder: (context){
return AlertDialog( //系统自带: 普通对话框
title: Text('提示信息!'),
content: Text('您确定要删除吗?'),
actions: <Widget>[ //监听器
FlatButton( //确定监听
child: Text('取消'),
onPressed: (){
print('取消');
Navigator.pop(context,'Cancle');
},
),
FlatButton( //取消监听
child: Text('确定'),
onPressed: (){
print('确定');
Navigator.pop(context,'OK');
},
)
],
);
}
);
print(result); //在外部获取数据并打印
}
_simpleDialog() async{
var result = await showDialog(
context: context,
builder: (context){
return SimpleDialog(
title: Text('选择内容'),
children: <Widget>[
SimpleDialogOption(
child: Text('Option A'),
onPressed: (){
print('Option A');
Navigator.pop(context,'A');
},
),
Divider(),
SimpleDialogOption(
child: Text('Option B'),
onPressed: (){
print('Option B');
Navigator.pop(context,'B');
},
),
Divider(),
SimpleDialogOption(
child: Text('Option C'),
onPressed: (){
print('Option C');
Navigator.pop(context,'C');
},
),
Divider(),
],
);
}
);
print(result);
}
_modelBottomSheet() async{
var result = await showModalBottomSheet(
context: context,
builder: (context){
return Container(
height: 250, //配置底部弹出框高度
child: Column(
children: <Widget>[
ListTile(
title: Text('分享 A'),
onTap: (){
Navigator.pop(context,'分享A');
},
),
Divider(),
ListTile(
title: Text('分享 B'),
onTap: (){
Navigator.pop(context,'分享B');
},
),
Divider(),
ListTile(
title: Text('分享 C'),
onTap: (){
Navigator.pop(context,'分享C');
},
),
],
),
);
}
);
print(result);
}
_toast(){
Fluttertoast.showToast(
msg: "提示信息",
toastLength: Toast.LENGTH_SHORT, //跟Android一样,短时间提示
gravity: ToastGravity.CENTER, //居中
timeInSecForIos: 1, //Android无效果,ios有效果文档上已经说明:timeInSecForIos int (only for ios)
backgroundColor: Colors.red, //背景颜色
textColor: Colors.white, //字体颜色
fontSize: 16.0 //字体大小
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('DialogDemo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('alert弹出框-AlerDialog'),
onPressed: _alertDialog,
),
SizedBox(height: 20,),
RaisedButton(
child: Text('select弹出框-SimpleDialog'),
onPressed: _simpleDialog,
),
SizedBox(height: 20,),
RaisedButton(
child: Text('ActionSheet底部弹出框-showModalBottomSheet'),
onPressed: _modelBottomSheet,
),
SizedBox(height: 20,),
RaisedButton(
child: Text('toast-fluttertoast第三方库'),
onPressed: _toast,
)
],
),
),
);
}
}
我叫王睿,对您有帮助的话,请给个点赞或关注喔,谢谢!
更多推荐
所有评论(0)