提示:此文章为前端学习flutter过程,如有歧义期望各位大大的宝贵意见
学习Dart 基础知识 移步这里
安装flutter环境相关 移步这里
flutter基础组件使用相关 移步这里
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('hello flutter demo'),
),
body: const MyContent()),
);
}
}
class MyContent extends StatelessWidget {
const MyContent({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Text('data...');
}
}
就是将组件抽离出来且可以接收参数
自定义普通组件
class MyContent extends StatelessWidget {
const MyContent({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const IconContent(); //调用自定义组件
}
}
// 自定义子组件
class IconContent extends StatelessWidget {
const IconContent({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
padding: const EdgeInsets.all(8.0),
color: Colors.red.shade900,
child: const Icon(
Icons.search,
color: Colors.white70,
size: 34,
),
);
}
}
自定义动态组件
class MyContent extends StatelessWidget {
const MyContent({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const IconContent(Icons.home,color:Colors.red,size:22); //调用自定义组件
}
}
// 自己定义的自定义组件
class IconContent extends StatelessWidget {
final double size; // 接收传过来的参数当做常量且不能更改,这样减少损耗
final Color color; // 接收传过来的参数当做常量且不能更改,这样减少损耗
final IconData icon; // 接收传过来的参数当做常量且不能更改,这样减少损耗
const IconContent(
this.icon, {
Key? key, // 继承自 statelesswidget 就这样写就可以
this.color = Colors.pink, // 默认赋值
this.size = 50, //默认赋值
}) : super(key: key);// 继承自 statelesswidget 就这样写就可以
@override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
padding: const EdgeInsets.all(8.0),
color: color,// 新版本不需要使用 this.color 调用
child: Icon(
icon, // 新版本不需要使用 this.icon 调用
color: Colors.white,
size: size,
),
);
}
}
**
**
样式库地址:https://www.fluttericon.com/
const Icon(
/// list 引导图标是个icon
Icons.set_meal_sharp,
/// icon名称
size: 60,
/// icon 大小
color: Colors.white,
/// icon 颜色
textDirection: TextDirection.ltr,
),
类似前端span
Text(
"Text组件的使用",
style: TextStyle(
// 文字颜色
color: Color(0xfff0000),
// none 不显示装饰线条,underline 字体下方,overline 字体上方,lineThrough穿过文字
decoration: TextDecoration.none,
// solid 直线,double 双下划线,dotted 虚线,dashed 点下划线,wavy 波浪线
decorationStyle: TextDecorationStyle.solid,
// 装饰线的颜色
decorationColor: Colors.red,
// 文字大小
fontSize: 15.0,
// normal 正常,italic 斜体
fontStyle: FontStyle.normal,
// 字体的粗细
fontWeight: FontWeight.bold,
// 文字间的宽度
letterSpacing: 1.0,
// 文本行与行的高度,作为字体大小的倍数(取值1~2,如1.2)
height: 1,
//对齐文本的水平线:
//TextBaseline.alphabetic:文本基线是标准的字母基线
//TextBaseline.ideographic:文字基线是表意字基线;
//如果字符本身超出了alphabetic 基线,那么ideograhpic基线位置在字符本身的底部。
textBaseline: TextBaseline.alphabetic),
// 段落的间距样式
strutStyle: StrutStyle(
fontFamily: 'serif',
fontFamilyFallback: ['monospace', 'serif'],
fontSize: 20,
height: 2,
leading: 2.0,
fontWeight: FontWeight.w300,
fontStyle: FontStyle.normal,
forceStrutHeight: true,
debugLabel: 'text demo',
),
// 文字对齐方式
textAlign: TextAlign.center,
// 文字排列方向 ltr 左到右,rtl右到左
textDirection: TextDirection.ltr,
// 用于选择区域特定字形的语言环境
locale: Locale('zh_CN'),
// 软包裹 ,文字是否应该在软断行出断行
softWrap: false,
// 如何处理视觉溢出:clip 剪切溢出的文本以修复其容器。ellipsis 使用省略号表示文本已溢出。fade 将溢出的文本淡化为透明。
overflow: TextOverflow.clip,
// 文字的缩放比例
textScaleFactor: 1.0,
// 文本要跨越的可选最大行数,
maxLines: 2,
// 图像的语义描述,用于向Andoid上的TalkBack和iOS上的VoiceOver提供图像描述
semanticsLabel: 'text demo',
textWidthBasis: TextWidthBasis.longestLine,
)
类似前端div
margin:内边距,和子Widget有关系
padding:外边距,和父Widget有关系
alignment:子Widget对齐方式
默认是Alignment(0,0),子Widget居中。取值范围是(-1,1)。
child:子部件
clipBehavior:裁剪方法
color:背景颜色,如果foregroundDecoration设置的话,可能会遮盖color效果。
constraints:边界约束
decoration:用来设置特殊背景、边框。绘制在child后面的装饰。设置了decoration的话,就不能设置color属性
foregroundDecoration:用来设置特殊背景、边框。绘制在child前面的装饰
key:一个唯一标记,仅仅用来更新widget->key相同的小部件的状态
transform:矩阵变换 过度 旋转等
transformAlignment:设置矩阵变换的对齐方式
Container(
child: Image.network(
'http://pic.616pic.com/ys_bnew_img/00/17/41/RGxKWmyJIK.jpg'),
height: 300.0,
width: 300.0,
margin: const EdgeInsets.fromLTRB(20, 20, 20, 20),
padding: const EdgeInsets.fromLTRB(20, 20, 20, 20),
alignment: Alignment.bottomCenter,
transform: Matrix4.skewX(0.1),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(
Radius.circular(10),
),
border: Border.all(
color: tTextColor999.withOpacity(.2),
width: 1,
),
boxShadow: const [
BoxShadow(
color: Colors.black12,
offset: Offset(0.0, 15.0), //阴影xy轴偏移量
blurRadius: 15.0, //阴影模糊程度
spreadRadius: 1.0 //阴影扩散程度
)
],
// 渐变色
gradient: LinearGradient(
colors: [tMainRedColor, Colors.red, tMainRedColor],
),
),
),
Image.network(
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201609%2F07%2F20160907125722_LF84P.thumb.700_0.jpeg&refer=http%3A%2F%2Fb-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1653146319&t=a23832fe5477d1841073d48a644ad6e6',
alignment: Alignment.topLeft, // 图片位置
color: Colors.blue, // 图片颜色
colorBlendMode: BlendMode.color, // 颜色混合模式
fit: BoxFit.cover, //剪切图片
repeat: ImageRepeat.noRepeat, // 图片重复
),
方式1 container 的背景图片处理图片圆角
Container(
width: 300.0,
height: 300.0,
decoration: BoxDecoration(
color: Colors.red,
border: Border.all(
color: Colors.purple,
width: 4.0,
),
borderRadius: BorderRadius.circular(150.0),
image: const DecorationImage(
image: NetworkImage(
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201609%2F07%2F20160907125722_LF84P.thumb.700_0.jpeg&refer=http%3A%2F%2Fb-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1653146319&t=a23832fe5477d1841073d48a644ad6e6'),
fit: BoxFit.cover,
repeat: ImageRepeat.noRepeat,
),
),
),
方式2 container 下的子组件处理圆角显示
Container(
width: 50,
height: 50,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(200)),
child: Image.network(
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201609%2F07%2F20160907125722_LF84P.thumb.700_0.jpeg&refer=http%3A%2F%2Fb-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1653146319&t=a23832fe5477d1841073d48a644ad6e6',
fit: BoxFit.cover,
),
),
),
方式3 处理头像
leading: CircleAvatar(
backgroundImage: NetworkImage(
"https://www.itying.com/images/flutter/1.png",
),
),
方式4 使用container 结合ClipOval实现原型图片
Container(
child: ClipOval(
child: Image.network(
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201609%2F07%2F20160907125722_LF84P.thumb.700_0.jpeg&refer=http%3A%2F%2Fb-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1653146319&t=a23832fe5477d1841073d48a644ad6e6',
width: 100,
height: 100,
fit: BoxFit.cover,
),
),
),
1.根目录创建文件images 下创建 2.0x 3.0x 4.0x把文件分别放入文件夹 且再images下也要有;
2.然后,打开pubspec.yaml 声明一下添加的图片文件,注意要配置对 格式统一 对其
2.1 引入images文件下所有文件 用法:
assets:
- images/
3.在代码中使用
Image.asset(
'本地路径。。。',
alignment: Alignment.topLeft, // 图片位置
color: Colors.blue, // 图片颜色
colorBlendMode: BlendMode.color, // 颜色混合模式
fit: BoxFit.cover, //剪切图片
repeat: ImageRepeat.noRepeat, // 图片重复
),
class MyStatelessWidget extends StatelessWidget {
const MyStatelessWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ListView(
scrollDirection: Axis.horizontal, // 垂直布局,高固定,宽自适应
scrollDirection: Axis.vertical, // 水平布局 ,宽固定,高自适应
padding: const EdgeInsets.fromLTRB(20, 20, 20, 20),
children: <Widget>[
ListTile(
leading: const Icon( /// list 引导图标是个icon
Icons.set_meal_sharp, /// icon名称
size: 60, /// icon 大小
color: Colors.white, /// icon 颜色
textDirection: TextDirection.ltr,
),
tileColor: Colors.blue[600], /// listTile 背景色
title: const Text( /// 标题是个text
'东北华北华南', /// text内容
style: TextStyle( /// text样式
fontSize: 24.0,
color: Colors.white,
),
),
subtitle: const Text(
'武汉浙江长将等地发生特大洪灾武汉浙江长将等地发生特大洪灾武汉浙江长将等地发生特大洪灾武汉浙江长将等地发生特大洪灾',
style: TextStyle(
color: Colors.white38
),
),
),
],
);
}
}
class MyStatelessWidget extends StatelessWidget {
const MyStatelessWidget({Key? key}) : super(key: key);
List<Widget> getData() {// 在这个位置必须要声明返回值的类型是数组且值为Widget类型 ,如果调用的地方是明确类型的数据
List<Widget> lists = []; // 同上 类型必须明确
for (var i = 0; i < 20; i++) {
lists.add(ListTile(
title: Text(
'这是($i+1)条数据啊', // 变量直接加$
style: TextStyle(
color: Colors.blue.shade400,
),
),
));
}
return lists;
}
@override
Widget build(BuildContext context) {
return ListView(
scrollDirection: Axis.vertical,
children: getData(),// 这个位置需要类型的List所以在getData方法声明的时候必须要明确类型是Widget
);
}
}
class MyStatelessWidget extends StatelessWidget {
const MyStatelessWidget({Key? key}) : super(key: key);
List<Widget> getData() {
// 在这个位置必须要声明类型 ,如果调用的地方是明确类型的数据
List list = [ // 此处必须明确声明类型否则报错
{
"name": '张三',
"age": '12', // text只能接受字符串 数字不行报错
"src": 'https://www.juliangip.com/index/static/pc/img/pro_pic2.png'
},
{
"name": '张三',
"age": '12',
"src": 'https://www.juliangip.com/index/static/pc/img/pro_pic2.png'
},
{
"name": '张三',
"age": '12',
"src": 'https://www.juliangip.com/index/static/pc/img/pro_pic2.png'
},
];
var lists = list.map((item) {
return ListTile(
title: Text(item['name']), // 获取对象的值
leading: Image.network(item['src']),
subtitle: Text(item['age']),
);
});
return lists.toList();
}
@override
Widget build(BuildContext context) {
return ListView(
scrollDirection: Axis.vertical,
children: getData(),
); // 这个位置需要类型的List所以在getData方法声明的时候必须要明确类型是Widget
}
}
class MyStatelessWidget extends StatelessWidget {
List list = [];
MyStatelessWidget({Key? key}) : super(key: key) {
list = [
{
"name": '张三',
"age": '12',
"src": 'https://www.juliangip.com/index/static/pc/img/pro_pic2.png'
},
{
"name": '张三',
"age": '12',
"src": 'https://www.juliangip.com/index/static/pc/img/pro_pic2.png'
},
{
"name": '张三',
"age": '12',
"src": 'https://www.juliangip.com/index/static/pc/img/pro_pic2.png'
}
];
}
// 自定义方法用于抽离组件部分
Widget _getDataList(context, index) {
return ListTile(
title: Text(
list[index]['name'],
),
leading: Image.network(list[index]['src']),
subtitle: Text(list[index]['age'] + '岁数le '),
);
}
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: list.length,
itemBuilder: _getDataList,
);
/// 这个位置需要类型的List所以在getData方法声明的时候必须要明确类型是Widget
}
}
名称 | 类型 | 说明 |
---|---|---|
scrollDirection | Axis | 滚动方法 |
padding | EdgeInsetsGeometry | 内边距 |
resolve | bool | 组件反向排序 |
crossAxisSpacing | double | 水平子 Widget 之间间距 |
mainAxisSpacing | double | 垂直子 Widget 之间间距 |
crossAxisCount | int | int 一行的 Widget 数量 |
childAspectRatio | double | 子 Widget 宽高比例 |
children | [ ] | |
gridDelegate | SliverGridDelegateWithFix edCrossAxisCount(常用) SliverGridDelegateWithMax CrossAxisExtent |
控制布局主要用在 GridView.builder 里面 |
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('hello flutter'),
),
body: MyGridViewCount(),
),
);
}
}
// ignore: must_be_immutable
class MyGridViewCount extends StatelessWidget {
List lists = [
{
"title": 'goto其实很优雅',
"url":
'https://pics4.baidu.com/feed/03087bf40ad162d9f89f23927bf594e68b13cdfb.jpeg?token=c4a6b12d01a8ea4272c90772bec72e10',
},
{
"title": 'goto其实很优雅,居然一直被摒弃',
"url":
'https://pics4.baidu.com/feed/03087bf40ad162d9f89f23927bf594e68b13cdfb.jpeg?token=c4a6b12d01a8ea4272c90772bec72e10',
},
{
"title": 'goto其实很优雅,居然一直被摒弃',
"url":
'https://pics4.baidu.com/feed/03087bf40ad162d9f89f23927bf594e68b13cdfb.jpeg?token=c4a6b12d01a8ea4272c90772bec72e10'
},
{
"title": 'goto其实很优雅,居然一直被摒弃',
"url":
'https://pic.rmb.bdstatic.com/bjh/down/f1aa1dbb6e0d2eeabd1bcba92a3a67cf.jpeg@wm_2,t_55m+5a625Y+3L+WmmeS6ulNIT1c=,fc_ffffff,ff_U2ltSGVp,sz_26,x_17,y_17'
},
{
"title": 'goto其实很优雅,居然一直被摒弃',
"url":
'https://pic.rmb.bdstatic.com/bjh/down/f1aa1dbb6e0d2eeabd1bcba92a3a67cf.jpeg@wm_2,t_55m+5a625Y+3L+WmmeS6ulNIT1c=,fc_ffffff,ff_U2ltSGVp,sz_26,x_17,y_17'
},
{
"title": 'goto其实很优雅,居然一直被摒弃',
"url":
'https://pic.rmb.bdstatic.com/bjh/down/f1aa1dbb6e0d2eeabd1bcba92a3a67cf.jpeg@wm_2,t_55m+5a625Y+3L+WmmeS6ulNIT1c=,fc_ffffff,ff_U2ltSGVp,sz_26,x_17,y_17'
},
{
"title": 'goto其实很优雅,居然一直被摒弃',
"url":
'https://pic.rmb.bdstatic.com/bjh/down/f1aa1dbb6e0d2eeabd1bcba92a3a67cf.jpeg@wm_2,t_55m+5a625Y+3L+WmmeS6ulNIT1c=,fc_ffffff,ff_U2ltSGVp,sz_26,x_17,y_17'
},
{
"title": 'goto其实很优雅,居然一直被摒弃',
"url":
'https://pic.rmb.bdstatic.com/bjh/down/f1aa1dbb6e0d2eeabd1bcba92a3a67cf.jpeg@wm_2,t_55m+5a625Y+3L+WmmeS6ulNIT1c=,fc_ffffff,ff_U2ltSGVp,sz_26,x_17,y_17'
},
];
MyGridViewCount({Key? key}) : super(key: key);
List<Widget> _getData() {
return lists.map<Widget>((item) {
return Container(
padding: const EdgeInsets.all(10),
// container 样式
decoration: BoxDecoration(
// container 边框样式
border: Border.all(
color: Colors.grey.shade100,
),
// container redius
borderRadius: const BorderRadius.all(
Radius.circular(4),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,// 垂直居中
mainAxisAlignment: MainAxisAlignment.center, // 水平居中
mainAxisSize: MainAxisSize.max,
children: [
Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(4),
),
),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(10)),
child: Image.network(
item['url'],
height: 110,
width: 400,
fit: BoxFit.cover,
),
),
),
const SizedBox(
height: 10,
),
Text(
item['title'],
style: const TextStyle(
color: Colors.black87,
fontSize: 14,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
),
],
),
);
}).toList();
}
@override
Widget build(BuildContext context) {
return GridView.count(
padding: const EdgeInsets.all(10),
crossAxisSpacing: 10, // 水平子 widget 之间间距
mainAxisSpacing: 10, // 垂直子 widget 之间间距
crossAxisCount: 2, // 一行的 widget 数量
// childAspectRatio: 0.7, // 宽度和高度比例
children: _getData(),
);
}
}
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('hello flutter'),
),
body: MyGridViewCount(),
),
);
}
}
// ignore: must_be_immutable
class MyGridViewCount extends StatelessWidget {
List lists = [
{
"title": 'goto其实很优雅',
"url":
'https://pics4.baidu.com/feed/03087bf40ad162d9f89f23927bf594e68b13cdfb.jpeg?token=c4a6b12d01a8ea4272c90772bec72e10',
},
{
"title": 'goto其实很优雅,居然一直被摒弃',
"url":
'https://pics4.baidu.com/feed/03087bf40ad162d9f89f23927bf594e68b13cdfb.jpeg?token=c4a6b12d01a8ea4272c90772bec72e10',
},
{
"title": 'goto其实很优雅,居然一直被摒弃',
"url":
'https://pics4.baidu.com/feed/03087bf40ad162d9f89f23927bf594e68b13cdfb.jpeg?token=c4a6b12d01a8ea4272c90772bec72e10'
},
{
"title": 'goto其实很优雅',
"url":
'https://pic.rmb.bdstatic.com/bjh/down/f1aa1dbb6e0d2eeabd1bcba92a3a67cf.jpeg@wm_2,t_55m+5a625Y+3L+WmmeS6ulNIT1c=,fc_ffffff,ff_U2ltSGVp,sz_26,x_17,y_17'
},
{
"title": 'goto其实很优雅,居然一直被摒弃',
"url":
'https://pic.rmb.bdstatic.com/bjh/down/f1aa1dbb6e0d2eeabd1bcba92a3a67cf.jpeg@wm_2,t_55m+5a625Y+3L+WmmeS6ulNIT1c=,fc_ffffff,ff_U2ltSGVp,sz_26,x_17,y_17'
},
{
"title": 'goto其实很优雅,居然一直被摒弃',
"url":
'https://pic.rmb.bdstatic.com/bjh/down/f1aa1dbb6e0d2eeabd1bcba92a3a67cf.jpeg@wm_2,t_55m+5a625Y+3L+WmmeS6ulNIT1c=,fc_ffffff,ff_U2ltSGVp,sz_26,x_17,y_17'
},
{
"title": 'goto其实很优雅,居然一直被摒弃',
"url":
'https://pic.rmb.bdstatic.com/bjh/down/f1aa1dbb6e0d2eeabd1bcba92a3a67cf.jpeg@wm_2,t_55m+5a625Y+3L+WmmeS6ulNIT1c=,fc_ffffff,ff_U2ltSGVp,sz_26,x_17,y_17'
},
{
"title": 'goto其实很优雅,居然一直被摒弃',
"url":
'https://pic.rmb.bdstatic.com/bjh/down/f1aa1dbb6e0d2eeabd1bcba92a3a67cf.jpeg@wm_2,t_55m+5a625Y+3L+WmmeS6ulNIT1c=,fc_ffffff,ff_U2ltSGVp,sz_26,x_17,y_17'
},
];
MyGridViewCount({Key? key}) : super(key: key);
Widget _getListData(context, index) {
return Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
border: Border.all(
color: Colors.grey.shade100,
),
borderRadius: const BorderRadius.all(
Radius.circular(4),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(4),
),
),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(10)),
child: Image.network(
lists[index]['url'],
height: 110,
width: 400,
fit: BoxFit.cover,
),
),
),
const SizedBox(
height: 10,
),
Text(
lists[index]['title'],
style: const TextStyle(
color: Colors.black87,
fontSize: 14,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
),
],
),
);
}
@override
Widget build(BuildContext context) {
return GridView.builder(
padding: const EdgeInsets.all(10),
itemCount: lists.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisSpacing: 10, // 水平子 widget 之间间距
mainAxisSpacing: 10, // 垂直子 widget 之间间距
crossAxisCount: 2, // 一行的 widget 数量
),
itemBuilder: _getListData,
);
}
}
Padding 组件与 container 组件类似都相当于组件的 div
主要常用参数 | 说明 |
---|---|
child | 子组件 |
padding | 内边距 |
Padding(
padding: const EdgeInsets.all(8.0),
child: ClipOval(
child: Image.network(
"https://www.itying.com/images/flutter/2.png",
fit: BoxFit.cover,
),
),
),
row 使用方法与 listview 的区别:listview 是铺满整个屏幕,flutter中row 根据父组件的宽有多宽就铺多宽
主要常用参数 | 说明 |
---|---|
mainAxisAlignment | 主轴排序方式 |
crossAxisAlignment | 次轴排序方式 |
children | 子组件 |
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter 的 Row组件'),
),
body: const MyContent()),
);
}
}
class MyContent extends StatelessWidget {
const MyContent({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
height: 500,
width: 350,
color: Colors.pink,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
IconContent(
Icons.home,
color: Colors.blue,
size: 30,
),
IconContent(Icons.search),
IconContent(Icons.usb_rounded),
],
),
); //调用自定义组件
}
}
// 自己定义的自定义组件
class IconContent extends StatelessWidget {
final double size;
final Color color;
final IconData icon;
const IconContent(
this.icon, {
Key? key,
this.color = Colors.green,
this.size = 50,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
padding: const EdgeInsets.all(8.0),
color: color,
child: Icon(
icon,
color: Colors.white,
size: size,
),
);
}
}
水平布局相当于 flex的 column 使用方法与 listview 的区别:listview 是铺满整个屏幕,flutter 是 row 是有多宽就铺多宽
主要常用参数 | 说明 |
---|---|
mainAxisAlignment | 主轴排序方式 |
crossAxisAlignment | 次轴排序方式 |
children | 子组件 |
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter 的 Column组件'),
),
body: const MyContent()),
);
}
}
class MyContent extends StatelessWidget {
const MyContent({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
height: 500,
width: 350,
color: Colors.pink,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
IconContent(
Icons.home,
color: Colors.blue,
size: 30,
),
IconContent(Icons.search),
IconContent(Icons.usb_rounded),
],
),
); //调用自定义组件
}
}
// 自己定义的自定义组件
class IconContent extends StatelessWidget {
final double size;
final Color color;
final IconData icon;
const IconContent(
this.icon, {
Key? key,
this.color = Colors.green,
this.size = 50,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
padding: const EdgeInsets.all(8.0),
color: color,
child: Icon(
icon,
color: Colors.white,
size: size,
),
);
}
}
expanded 可以使用在 Row 和 Column 布局中
属性 | 说明 |
---|---|
child | 子组件 |
flex | 元素站整个父组件 Row、Column 的比例 |
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
Expanded(
flex: 1,
child: IconContent(
Icons.home,
color: Colors.blue,
size: 30,
),
),
Expanded(
flex: 2,
child: IconContent(
Icons.usb_rounded,
size: 30,
),
),
],
)
(有点像 ps 中的图层的意思,后写的参数会遮挡之前写的参数)
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('hello flutter demo'),
),
body: const MyContent()),
);
}
}
class MyContent extends StatelessWidget {
const MyContent({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: Stack(
alignment: Alignment.center, // 定义组件在什么位置
// alignment: const Alignment(0, -1),//或者这种00 是xy 的中心店,xy 的最大值为1,最小值为-1
children: [
Container(
color: Colors.red.shade400,
width: 300,
height: 500,
),
const Text(
'我是后写的字段',
style: TextStyle(
color: Colors.white,
fontSize: 24,
),
),
],
),
);
}
}
注意:alignment 会控制所有组件,出现位置,是 stack 下的所有子组件,
注意:因为 stack 下的 alignment 属性会操作 stack 下的所有子组件,想要每个子组件都有不同的位置,那就使用 Align 有 child 和 alignment(通过这个属性来实现子组件位置的变化)
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('hello flutter demo'),
),
body: const MyContent()),
);
}
}
class MyContent extends StatelessWidget {
const MyContent({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: Container(
width: 200,
height: 400,
color: Colors.green,
child: Stack(
children: const [
Align(
alignment: Alignment.topLeft,
child: Icon(Icons.home, size: 39, color: Colors.white),
),
Align(
alignment: Alignment.center,
child: Icon(Icons.search, size: 40, color: Colors.white),
),
Align(
alignment: Alignment.bottomRight,
child: Icon(Icons.architecture, size: 60, color: Colors.white),
),
],
),
),
);
}
}
注意 有四个值 left right top bottom 跟 js 中的 position 很像,默认值为0
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('hello flutter demo'),
),
body: const MyContent()),
);
}
}
class MyContent extends StatelessWidget {
const MyContent({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: Container(
width: 200,
height: 400,
color: Colors.green,
child: Stack(
children: const [
Positioned(
left: 10,
top: 100,
child: Icon(Icons.home, size: 39, color: Colors.white),
),
Positioned(
right: 10,
top: 100,
child: Icon(Icons.search, size: 40, color: Colors.white),
),
Positioned(
bottom: 100,
child: Icon(Icons.architecture, size: 60, color: Colors.white),
),
],
),
),
);
}
}
class MyContent extends StatelessWidget {
const MyContent({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 3.0 / 2.0, // 宽高比
child: Container(
color: Colors.red,
),
);
}
}
class MyContent extends StatelessWidget {
const MyContent({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ListView(
children: [
Card(
margin: const EdgeInsets.all(10),
child: Column(children: const [
ListTile(
title: Text(
'洛基',
style: TextStyle(color: Colors.black87),
),
subtitle: Text('高级软件工程师'),
),
ListTile(
title: Text('电话:xxx'),
),
ListTile(
title: Text('邮箱:xxx'),
),
]),
),
Card(
margin: const EdgeInsets.all(10),
child: Column(children: const [
ListTile(
title: Text(
'洛基',
style: TextStyle(color: Colors.black87),
),
subtitle: Text('高级软件工程师'),
),
ListTile(
title: Text('电话:xxx'),
),
ListTile(
title: Text('邮箱:xxx'),
),
]),
),
],
);
}
}
import 'package:flutter/material.dart';
import './res/listData.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('hello flutter demo'),
),
body: const MyContent()),
);
}
}
class MyContent extends StatelessWidget {
const MyContent({Key? key}) : super(key: key);
// 把自定义组件拆出去且遍历
List<Widget> _getData() {
return listData.map((val) {
return Card(
margin: const EdgeInsets.all(10.0),
shadowColor: Colors.black12,
child: Column(
children: [
AspectRatio(
aspectRatio: 16.0 / 9.0,
child: Image.network(
val["imageUrl"],
fit: BoxFit.cover,
height: 300,
),
),
ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(
val["imageUrl"],
),
),
title: Text(
val["title"],
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
subtitle: Text(
val["description"],
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
)
],
),
);
}).toList();
}
@override
Widget build(BuildContext context) {
return ListView(
children: _getData(),
);
}
}
数据
List listData=[
{
"title": 'Candy Shop',
"author": 'Mohamed Chahin',
"imageUrl": 'https://www.itying.com/images/flutter/1.png',
"description": 'Flutter is Google’s mobile UI framework for crafting high-quality native experiences on iOS and Android in record time. Flutter works with existing',
},
{
"title": 'Childhood in a picture',
"author": 'Google',
"imageUrl": 'https://www.itying.com/images/flutter/2.png',
"description": 'Flutter is Google’s mobile UI framework for crafting high-quality native experiences on iOS and Android in record time. Flutter works with existing',
},
{
"title": 'Alibaba Shop',
"author": 'Alibaba',
"imageUrl": 'https://www.itying.com/images/flutter/3.png',
"description": 'Dart is a client-optimized language for fast apps on any platform... Dart is a client-optimized language for fast apps on any platform Optimizedfor',
},
{
"title": 'Candy Shop',
"author": 'Mohamed Chahin',
"imageUrl": 'https://www.itying.com/images/flutter/4.png',
"description": 'Dart is a client-optimized language for fast apps on any platform... Dart is a client-optimized language for fast apps on any platform Optimizedfor',
},
{
"title": 'Tornado',
"author": 'Mohamed Chahin',
"imageUrl": 'https://www.itying.com/images/flutter/5.png',
"description": 'Flutter is Google’s mobile UI framework for crafting high-quality native experiences on iOS and Android in record time. Flutter works with existing',
},
{
"title": 'Undo',
"author": 'Mohamed Chahin',
"imageUrl": 'https://www.itying.com/images/flutter/6.png',
"description": 'Flutter is Google’s mobile UI framework for crafting high-quality native experiences on iOS and Android in record time. Flutter works with existing',
},
{
"title": 'white-dragon',
"author": 'Mohamed Chahin',
"imageUrl": 'https://www.itying.com/images/flutter/7.png',
"description": 'Dart is a client-optimized language for fast apps on any platform... Dart is a client-optimized language for fast apps on any platform Optimizedfor',
}
];
TextButton(
child: Text("TextButton按钮"),
//添加一个点击事件
onPressed: () {},
//设置按钮是否自动获取焦点
autofocus: true,
//定义一下文本样式
style: ButtonStyle(
//定义文本的样式 这里设置的颜色是不起作用的
textStyle: MaterialStateProperty.all(
TextStyle(fontSize: 18, color: Colors.red)),
//设置按钮上字体与图标的颜色
//foregroundColor: MaterialStateProperty.all(Colors.deepPurple),
//更优美的方式来设置
foregroundColor: MaterialStateProperty.resolveWith(
(states) {
if (states.contains(MaterialState.focused) &&
!states.contains(MaterialState.pressed)) {
//获取焦点时的颜色
return Colors.blue;
} else if (states.contains(MaterialState.pressed)) {
//按下时的颜色
return Colors.deepPurple;
}
//默认状态使用灰色
return Colors.grey;
},
),
//背景颜色
backgroundColor: MaterialStateProperty.resolveWith((states) {
//设置按下时的背景颜色
if (states.contains(MaterialState.pressed)) {
return Colors.blue[200];
}
//默认不使用背景颜色
return null;
}),
//设置水波纹颜色
overlayColor: MaterialStateProperty.all(Colors.yellow),
//设置阴影 不适用于这里的TextButton
elevation: MaterialStateProperty.all(0),
//设置按钮内边距
padding: MaterialStateProperty.all(EdgeInsets.all(10)),
//设置按钮的大小
minimumSize: MaterialStateProperty.all(Size(200, 100)),
//设置边框
side:
MaterialStateProperty.all(BorderSide(color: Colors.grey, width: 1)),
//外边框装饰 会覆盖 side 配置的样式
shape: MaterialStateProperty.all(StadiumBorder()),
),
);
IconButton(
onPressed: () {},
icon: const Icon(Icons.search),
),
ElevatedButton(
onPressed: () {},
child: const Text('ElevatedButton'),
),
Container(
width: 200,
height: 50,
child: ElevatedButton(
onPressed: () {},
child: Text(
'ElevatedButton',
),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.red), // 背景色
shadowColor: MaterialStateProperty.all(Colors.black), // 阴影
elevation: MaterialStateProperty.all(4), // 阴影范围
),
),
),
// 按钮前面加上icon
ElevatedButton.icon(
onPressed: () {},
icon: Icon(Icons.search),
label: Text('图标按钮'),
),
// 圆角按钮
shape: MaterialStateProperty.all(
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
),
// 圆形按钮
shape: MaterialStateProperty.all(CircleBorder(side: BorderSide(color: Colors.white))),
side: MaterialStateProperty.all(BorderSide(width: 4, color: Colors.blue)),
注意:
想改变 button 按钮大小,建议外面包一个 container 就可以了改变按钮大小了
,
设置按钮阴影同时要设置 evevation,否则无效果;
floatingActionButton: FloatingActionButton(
onPressed: () {
// Add your onPressed code here!
},
backgroundColor: Colors.green,
child: const Icon(Icons.navigation),
),
floatingActionButtonLocation: FloatingActionButtonLocation.startFloat, // 按钮位置
注意:放在 与 body 和 appBar同级,且在 Scaffold下;同样修改按钮大小用 container 包裹起来设置 container就相当于设置了按钮大小;
import 'package:flutter/material.dart';
import 'package:flutter_app_3/pages/search.dart';
import '../pages/tabs/chat.dart';
import '../pages/tabs/find.dart';
import '../pages/tabs/mine.dart';
import '../pages/tabs/tel.dart';
import '../pages/search2.dart';
class BottomTemplate extends StatefulWidget {
final int index; // 接到的参数可以为 null
const BottomTemplate({Key? key, this.index = 0}) : super(key: key);
@override
State<BottomTemplate> createState() => _BottomTemplateState(this.index);
}
class _BottomTemplateState extends State<BottomTemplate> {
List pageList = [
ChatPage(),
TelPage(),
Search2Page(),
FindPage(),
MinePage()
];
int currentIndex = 0;
String navigationBarName = '微信聊天页面';
_BottomTemplateState(index) {
this.currentIndex = index;
}
@override
Widget build(BuildContext context) {
return Scaffold(
drawer: Drawer(
child: Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
Navigator.of(context).pushNamed('/search2');
},
child: Text("跳转到 myapp 页面"),
),
),
),
appBar: AppBar(
title: Text(navigationBarName),
),
floatingActionButton: Container(
width: 60,
height: 60,
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(60.0),
color: Colors.white,
// border: Border.all(width: 1, color: Colors.green),
),
child: FloatingActionButton(
child: Icon(Icons.search),
backgroundColor:
this.currentIndex == 2 ? Colors.blue : Colors.grey.shade500,
onPressed: () {
setState(() {
this.currentIndex = 2;
});
},
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
body: pageList[currentIndex],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed, // 超过两个必须要写 type
currentIndex: currentIndex, // 当前是第几个页面下标从零开始
fixedColor: Colors.blue, // 默认选中的颜色
onTap: (int index) {
setState(() {
currentIndex = index;
switch (index) {
case 0:
navigationBarName = "微信聊天页面";
break;
case 1:
navigationBarName = "我的通讯录页面";
break;
case 2:
navigationBarName = "搜索";
break;
case 3:
navigationBarName = "发现页面";
break;
case 4:
navigationBarName = "我页面";
break;
}
});
},
items: const [
// 底部按钮每一项
BottomNavigationBarItem(
icon: Icon(Icons.home_mini_sharp),
label: "微信",
),
BottomNavigationBarItem(
icon: Icon(Icons.home_mini_sharp),
label: "通讯录",
),
BottomNavigationBarItem(
icon: Icon(Icons.home_mini_sharp),
label: "搜索",
),
BottomNavigationBarItem(
icon: Icon(Icons.home_mini_sharp),
label: "发现",
),
BottomNavigationBarItem(
icon: Icon(Icons.home_mini_sharp),
label: "我",
),
],
),
);
}
}
OutlinedButton(
onPressed: () {},
child: Text('OutlinedButton'),
),
DropdownButton(
value: 'One',
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
dropdownColor: Colors.red,
items: <String>['One', 'Two', 'Free', 'Four']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: const TextStyle(
color: Colors.white,
),
),
);
}).toList(),
onChanged: (String? s) {
print(s);
},
),
1.定义按钮组件
class MyButton extends StatelessWidget {
final double height;
final double width;
final text;
final onPressed;
MyButton(
{this.height = 0, this.width = 0, this.text = '', this.onPressed = null});
@override
Widget build(BuildContext context) {
return Container(
width: width,
height: height,
child: ElevatedButton(
onPressed: onPressed,
child: Text(text),
),
);
}
}
2.使用
MyButton(
width: 300,
height: 50,
text: '我是自定义的按钮1',
onPressed: () {
print('我是自定义按钮被触发了点击事件');
}),
spacing: 10, //横轴 元素与元素之间的距离
runSpacing: 0, //纵轴 元素与元素之间的距离
// alignment: WrapAlignment.end, // 主轴对齐方法 基本不用
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('hello flutter demo'),
),
body: const MyContent(),
),
);
}
}
class MyContent extends StatelessWidget {
const MyContent({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
//这是一个文本按钮 未设置点击事件下的样式
return Wrap(
spacing: 10, //横轴 元素与元素之间的距离
runSpacing: 0, //纵轴 元素与元素之间的距离
// alignment: WrapAlignment.end, // 主轴对齐方法 基本不用
children: const [
myButton('lisi'),
myButton('lisi2'),
myButton('lisi3'),
myButton('lisi4'),
myButton('lisi5'),
myButton('lisi6'),
myButton('lisi7'),
myButton('lisi8'),
myButton('lisi9'),
],
);
}
}
class myButton extends StatelessWidget {
final String name;
const myButton(this.name, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {},
child: Text(name),
);
}
}
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('hello flutter demo'),
),
body: const MyContent(),
),
);
}
}
class MyContent extends StatelessWidget {
const MyContent({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
//这是一个文本按钮 未设置点击事件下的样式
return Container(
width: 300,
height: 500,
color: Colors.green.shade100,
padding: const EdgeInsets.all(10),
child: Wrap(
spacing: 10, //横轴 元素与元素之间的距离
runSpacing: 10, //纵轴 元素与元素之间的距离
direction: Axis.vertical, // 横向排步,还是纵项布局,默认横向
alignment: WrapAlignment.end, // 主轴对齐方法 基本不用
children: const [
myButton('lisi'),
myButton('lisi2'),
myButton('lisi3'),
myButton('lisi4'),
myButton('lisi5'),
myButton('lisi6'),
myButton('lisi7'),
myButton('lisi8'),
myButton('lisi9'),
myButton('lisi'),
myButton('lisi2'),
myButton('lisi3'),
myButton('lisi4'),
myButton('lisi5'),
myButton('lisi6'),
myButton('lisi7'),
myButton('lisi8'),
myButton('lisi9'),
],
),
);
}
}
class myButton extends StatelessWidget {
final String name;
const myButton(this.name, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {},
child: Text(name),
);
}
}
在 Flutter 中自定义组件其实就是一个类,这个类需要继承 StatelessWidget/StatefulWidget。
StatelessWidget 是无状态组件,状态不可变的 widget ,
StatefulWidget 是有状态组件,持有的状态可能在 widget 生命周期改变。
通俗的讲:如果我 们想改变页面中的数据的话这个时候就需要用到 StatefulWidget
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('hello flutter demo'),
),
body: MyContent(),
),
);
}
}
class MyContent extends StatefulWidget {
MyContent({Key? key}) : super(key: key);
@override
State<MyContent> createState() => _MyContentState();
}
class _MyContentState extends State<MyContent> {
int count = 0;
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
const SizedBox(
height: 200,
),
Text("$count"),
const SizedBox(
height: 20,
),
ElevatedButton(
onPressed: () {
setState(() {
count++;
});
},
child: const Text('增加'),
),
],
),
);
}
}
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('hello flutter demo'),
),
body: const MyFriendsList(),
),
);
}
}
class MyFriendsList extends StatefulWidget {
const MyFriendsList({Key? key}) : super(key: key);
@override
State<MyFriendsList> createState() => _MyFriendsListState();
}
class _MyFriendsListState extends State<MyFriendsList> {
List friendList = [];
@override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
Column(
children: friendList.map((e) {
return ListTile(
leading: Image.network(
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201609%2F07%2F20160907125722_LF84P.thumb.700_0.jpeg&refer=http%3A%2F%2Fb-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1653146319&t=a23832fe5477d1841073d48a644ad6e6',
alignment: Alignment.topLeft, // 图片位置
color: Colors.blue, // 图片颜色
colorBlendMode: BlendMode.color, // 颜色混合模式
fit: BoxFit.cover, //剪切图片
repeat: ImageRepeat.noRepeat, // 图片重复
),
title: Text(e),
subtitle: const Text('我是新闻的描述'),
);
}).toList(),
),
Center(
child: ElevatedButton(
onPressed: () {
setState(() {
friendList.add('我是新闻${friendList.length + 1}');
friendList.add('我是新闻${friendList.length + 1}');
});
print(friendList);
},
child: const Text('新增'),
),
)
],
);
}
}
当底部导航页面超过三个时,必须要添加type属性(BottomNavigationBarType.fixed),否则的话底部导航会全白,看不到效果
items中是一个BottomNavigationBarItem的集合,里面包含图片及文字标签,在视频教程中给的文字使用的是title属性,我在使用时提示被弃用,用烂了替换title为 label !!!
currentIndex是BottomNavigationBar 中的属性,是为底部导航页面编的号,从零开始
我们要改变页面时在onTap中获取点击页面的编号
onTap: (int index) { setState(() { currentIndex = index; }); },
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed, // 超过两个必须要写 type
currentIndex: 0, // 当前是第几个页面下标从零开始
fixedColor: Colors.green, // 默认选中的颜色
onTap: (int index) {},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home_mini_sharp),
label: "微信",
),
BottomNavigationBarItem(
icon: Icon(Icons.home_mini_sharp),
label: "通讯录",
),
BottomNavigationBarItem(
icon: Icon(Icons.home_mini_sharp),
label: "发现",
),
BottomNavigationBarItem(
icon: Icon(Icons.home_mini_sharp),
label: "我",
),
],
),
1.创建文件夹 及文件夹对应文件,且 bottomTips.dart 装的文件用于处理底部几个文件;
w_chat.dart /
import 'package:flutter/material.dart';
// ignore: camel_case_types
class wChatPage extends StatelessWidget {
const wChatPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Text('我是 wChatPage');
}
}
tel.dart/
import 'package:flutter/material.dart';
// ignore: camel_case_types
class telPage extends StatelessWidget {
const telPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Text('我是 telPage');
}
}
found.dart/
import 'package:flutter/material.dart';
// ignore: camel_case_types
class findPage extends StatelessWidget {
const findPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Text('我是 findPage');
}
}
mine.dart
import 'package:flutter/material.dart';
// ignore: camel_case_types
class minePage extends StatelessWidget {
const minePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Text('我是 minePage');
}
}
bottomTips.dart
import 'package:flutter/material.dart';
import './bottom_componts/found.dart';
import './bottom_componts/w_chat.dart';
import './bottom_componts/mine.dart';
import './bottom_componts/tel.dart';
class BottomTemplate extends StatefulWidget {
const BottomTemplate({Key? key}) : super(key: key);
@override
State<BottomTemplate> createState() => _BottomTemplateState();
}
class _BottomTemplateState extends State<BottomTemplate> {
List pageList = [
const wChatPage(),
const telPage(),
const findPage(),
const minePage()
];
int currentIndex = 0;
String navigationBarName = '微信聊天页面';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(navigationBarName),
),
body: pageList[currentIndex],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed, // 超过两个必须要写 type
currentIndex: currentIndex, // 当前是第几个页面下标从零开始
fixedColor: Colors.blue, // 默认选中的颜色
onTap: (int index) {
setState(() {
currentIndex = index;
switch (index) {
case 0:
navigationBarName = "微信聊天页面";
break;
case 1:
navigationBarName = "我的通讯录页面";
break;
case 2:
navigationBarName = "发现页面";
break;
case 3:
navigationBarName = "我页面";
break;
}
});
},
items: const [ // 底部按钮每一项
BottomNavigationBarItem(
icon: Icon(Icons.home_mini_sharp),
label: "微信",
),
BottomNavigationBarItem(
icon: Icon(Icons.home_mini_sharp),
label: "通讯录",
),
BottomNavigationBarItem(
icon: Icon(Icons.home_mini_sharp),
label: "发现",
),
BottomNavigationBarItem(
icon: Icon(Icons.home_mini_sharp),
label: "我",
),
],
),
);
}
}
第二步在 mian.dart 中调用封装的 底部
import 'package:flutter/material.dart';
import './bottomTips/bottomTips.dart'; // 引入自己封装的外部组件
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: BottomTemplate(), // 调用自己封装的外部组件
);
}
}
ElevatedButton(
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (BuildContext context) {
return SearchPage();
}));
},
child: Text('跳转到搜索页面'),
),
FloatingActionButton(
onPressed: () {
Navigator.of(context).pop();
},
// enableFeedback: true,
child: Icon(Icons.backspace_outlined),
)
1.首先在跳转位置,import 引入 formPage组件协商自己要传参的内容
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => FormPage(title: '我是通过传参传过来的列表标题'),
),
);
},
child: Text('跳转到表单页面并传值'),
)
2.接收参数 - 通过构造函数来接收参数,且规定是必传参数还是非必传参数,放在{}就是非必传参数
import 'package:flutter/material.dart';
// ignore: must_be_immutable
class FormPage extends StatelessWidget {
String titleGet;
// 通过构造函数来接收参数,且规定是必传参数还是非必传参数,放在{}就是非必传参数
FormPage({Key? key, this.titleGet = '我是个默认表单'}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("表单..."),
),
body: Container(
color: Colors.green,
child: ListView(
children: [
ListTile(title: Text(titleGet)),
ListTile(title: Text(titleGet)),
ListTile(title: Text(titleGet)),
ListTile(title: Text(titleGet)),
ListTile(title: Text(titleGet)),
],
),
),
);
}
}
1.首先在跳转位置,import 引入 formPage组件协商自己要传参的内容
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => FormPage(title: '我是通过传参传过来的列表标题'),
),
);
},
child: Text('跳转到表单页面并传值'),
)
2.接收参数 - 通过构造函数来接收参数,且规定是必传参数还是非必传参数,放在{}就是非必传参数
传参
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (BuildContext context) {
return SearchPage(title: '普通传参过来搜索标题');
}));
},
接参
import 'package:flutter/material.dart';
// ignore: must_be_immutable
class FormPage extends StatelessWidget {
String titleGet;
// 通过构造函数来接收参数,且规定是必传参数还是非必传参数,放在{}就是非必传参数
FormPage({Key? key, this.titleGet = '我是个默认表单'}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("表单..."),
),
body: Container(
color: Colors.green,
child: ListView(
children: [
ListTile(title: Text(titleGet)),
ListTile(title: Text(titleGet)),
ListTile(title: Text(titleGet)),
ListTile(title: Text(titleGet)),
ListTile(title: Text(titleGet)),
],
),
),
);
}
}
1.在 main.dart 根目录下定义路由
Widget build(BuildContext context) {
return MaterialApp( // home:Tabs(),
initialRoute: '/', // 默认跳转路由
routes: { // 定义路由
'/':(contxt)=>Tabs(),
'/search':(contxt) =>SearchPage(),
'/form': (context) => FormPage(),
},
);
2.跳转路由
ElevatedButton(
child: Text("跳转到搜索页面"),
onPressed: (){ Navigator.pushNamed(context, '/search');},
)
1.新建一个 Routes.dart 文件
import 'package:flutter/material.dart';
import '../tabs/tabs.dart';
import '../pages/search2.dart';
// 这个位置写路由
final Map<String, Function> routes = {
'/': (contxt, {arguments}) => BottomTemplate(),
'/search2': (contxt, {arguments}) => Search2Page(arguments: arguments)
};
// 这个位置固定写法,无需担心,新手可以不用理解
var onGenerateRoute = (RouteSettings settings) {
// 统一处理
final String? name = settings.name;
final Function? pageContentBuilder = routes[name];
if (pageContentBuilder != null) {
final Route route = MaterialPageRoute(
builder: (context) =>
pageContentBuilder(context, arguments: settings.arguments));
return route;
}
};
2.在需要main.dart文件夹下引入
import 'package:flutter/material.dart';
import 'routes/Routes.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
// home:Tabs(), // 引入底部按钮
initialRoute: '/', // 默认跳转路由
onGenerateRoute: onGenerateRoute ); // 路由跳转
}
}
3.在需要引入跳转的地方使用
ElevatedButton(
child: Text("跳转商品详情页面 并传参 id"),
onPressed: (){
Navigator.pushNamed(context, '/search2',arguments: { "title":20 });
},
)
4.接收参数
方法一,是 StatelessWidget无状态组件接参数
import 'package:flutter/material.dart';
class Search2Page extends StatelessWidget {
final Map? arguments; // 接到的参数可以为 null
Search2Page({this.arguments});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("${arguments != null ? arguments!['title'] : '0'}"),
),
);
}
}
方法二,是StatefulWidget有状态组件接收参数
import 'package:flutter/material.dart';
class Search2Page extends StatefulWidget {
final Map? arguments;
Search2Page({Key? key, this.arguments}) : super(key: key);
@override
State<Search2Page> createState() => _Search2PageState(arguments: arguments);
}
class _Search2PageState extends State<Search2Page> {
Map? arguments;
_Search2PageState({this.arguments});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("${arguments != null ? arguments!['title'] : '0'}"),
),
);
}
}
Navigator.of(context).pop();
比如我们从用户中心页面跳转到了 registerFirst 页面,然后从 registerFirst 页面通过 pushReplacementNamed 跳转到了 registerSecond 页面。这个时候当我们点击 registerSecond 的返回按钮的时候它会直接返回到用户中心。
Navigator.of(context).pushReplacementNamed('/registerSecond');
比如我们从用户中心跳转到 registerFirst 页面,然后从 registerFirst 页面跳转到 registerSecond 页面,然后从 registerSecond 跳转到了 registerThird 页面。这个时候我们想的是 registerThird 注册成功后返回到用户中心。 这个时候就用到了返回到根路由的方法。
切记这里 Tabs()必须提前引入;
Navigator.of(context).pushAndRemoveUntil(
new MaterialPageRoute(builder: (context) => new Tabs(index:1)), //跳转路径携带参数 index
(route) => route == null // 清空路由
);
tabs 中处理接到的参数
import 'package:flutter/material.dart';
import '../pages/tabs/chat.dart';
import '../pages/tabs/find.dart';
import '../pages/tabs/mine.dart';
import '../pages/tabs/tel.dart';
class BottomTemplate extends StatefulWidget {
final int index; // 接到的参数
const BottomTemplate({Key? key, this.index = 0}) : super(key: key); // 赋初始值
@override
State<BottomTemplate> createState() => _BottomTemplateState(this.index); // 传参给构造函数
}
class _BottomTemplateState extends State<BottomTemplate> {
List pageList = [
const ChatPage(),
const TelPage(),
const FindPage(),
const MinePage()
];
int currentIndex = 0;
String navigationBarName = '微信聊天页面';
_BottomTemplateState(index) { // 处理构造函数接到的参数
this.currentIndex = index;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(navigationBarName),
),
body: pageList[currentIndex],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed, // 超过两个必须要写 type
currentIndex: currentIndex, // 当前是第几个页面下标从零开始
fixedColor: Colors.blue, // 默认选中的颜色
onTap: (int index) {
setState(() {
currentIndex = index;
switch (index) {
case 0:
navigationBarName = "微信聊天页面";
break;
case 1:
navigationBarName = "我的通讯录页面";
break;
case 2:
navigationBarName = "发现页面";
break;
case 3:
navigationBarName = "我页面";
break;
}
});
},
items: const [
// 底部按钮每一项
BottomNavigationBarItem(
icon: Icon(Icons.home_mini_sharp),
label: "微信",
),
BottomNavigationBarItem(
icon: Icon(Icons.home_mini_sharp),
label: "通讯录",
),
BottomNavigationBarItem(
icon: Icon(Icons.home_mini_sharp),
label: "发现",
),
BottomNavigationBarItem(
icon: Icon(Icons.home_mini_sharp),
label: "我",
),
],
),
);
}
}
appBar: AppBar(
backgroundColor: Colors.red,//导航背景色
leading: IconButton( // 左侧替换返回图标
icon: Icon(Icons.menu),
tooltip: "Search",
onPressed: () {
print('menu Pressed');
}),
title: Text('FlutterDemo'),
centerTitle: true, // 是否居中显示
actions: <Widget>[ // 定义右侧显示图标
IconButton(
icon: Icon(Icons.search),
tooltip: "Search",
onPressed: () {
print('Search Pressed');
}),
IconButton(
icon: Icon(Icons.more_horiz),
tooltip: "more_horiz",
onPressed: () {
print('more_horiz Pressed');
})
],
),
import 'package:flutter/material.dart';
class Register3Page extends StatelessWidget {
const Register3Page({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 5,
child: Scaffold(
appBar: AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
flex: 1,
child: TabBar(
enableFeedback: false,
isScrollable: true,
tabs: <Widget>[
Tab(text: "热门1"),
Tab(text: "推荐2"),
Tab(text: "推荐3"),
Tab(text: "推荐4"),
Tab(text: "推荐5"),
],
),
)
],
),
),
body: TabBarView(
children: [
ListView(
children: [
ListTile(title: Text("hello1")),
ListTile(title: Text("hello")),
ListTile(title: Text("hello")),
],
),
ListView(
children: [
ListTile(title: Text("hello2")),
ListTile(title: Text("hello")),
ListTile(title: Text("hello")),
],
),
ListView(
children: [
ListTile(title: Text("hello3")),
ListTile(title: Text("hello")),
ListTile(title: Text("hello")),
],
),
ListView(
children: [
ListTile(title: Text("hello4")),
ListTile(title: Text("hello")),
ListTile(title: Text("hello")),
],
),
ListView(
children: [
ListTile(title: Text("hello5")),
ListTile(title: Text("hello")),
ListTile(title: Text("hello")),
],
)
],
),
),
);
}
}
使用在Scaffold下,默认是左侧侧边栏
在 Scaffold 组件里面传入 drawer 参数可以定义左侧边栏,传入 endDrawer 可以定义右侧边 栏。侧边栏默认是隐藏的,我们可以通过手指滑动显示侧边栏,也可以通过点击按钮显示侧 边栏。
return Scaffold(
appBar: AppBar(
title: Text("Flutter App"),
),
drawer: Drawer(
child: Text('左侧边栏'),
),
endDrawer: Drawer(
child: Text('右侧侧边栏'),
),
);
drawer: Drawer(
child: Column(
children: <Widget>[
DrawerHeader(
decoration: BoxDecoration(
color: Colors.yellow,
image: DecorationImage(
image: NetworkImage(
"https://www.itying.com/images/flutter/2.png"),
fit: BoxFit.cover)),
child: ListView(
children: <Widget>[Text('我是一个头部')],
),
),
ListTile(
title: Text("个人中心"),
leading: CircleAvatar(child: Icon(Icons.people)),
),
Divider(),
ListTile(
title: Text("系统设置"),
leading: CircleAvatar(child: Icon(Icons.settings)),
)
],
)),
drawer: Drawer(
child: Column(
children: <Widget>[
UserAccountsDrawerHeader(
accountName: Text("luoji"),
accountEmail: Text("[email protected]"),
currentAccountPicture: CircleAvatar(
backgroundImage:
NetworkImage("https://www.itying.com/images/flutter/3.png"),
),
decoration: BoxDecoration(
color: Colors.yellow,
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"),
Image.network("https://www.itying.com/images/flutter/6.png")
],
),
onTap: (){
Navigator.of(context).pop();// 阻止侧边弹窗一直显示
Navigator.pushNamed(context, '/search');
}
import 'package:flutter/material.dart';
class Register3Page extends StatelessWidget {
const Register3Page({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 5,
child: Scaffold(
//左侧侧边栏
drawer: Drawer(
child: Column(
children: [
// 侧滑头部样式
Row(
children: [
Expanded(
child: DrawerHeader(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'hello zdx',
style: TextStyle(
color: Colors.white,
fontSize: 22,
fontWeight: FontWeight.w900),
),
SizedBox(
height: 10,
),
Container(
width: 60,
height: 60,
child: ClipOval(
child: Image.network(
"http://www.itying.com/images/flutter/1.png",
fit: BoxFit.cover,
),
),
),
],
),
// 背景图片
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
'https://www.itying.com/images/flutter/2.png'),
fit: BoxFit.cover,
opacity: .6,
),
),
),
)
],
),
ListTile(
leading: CircleAvatar(child: Icon(Icons.home)),
title: Text('我的电脑'),
),
ListTile(
leading: CircleAvatar(child: Icon(Icons.home)),
title: Text('我的电脑'),
),
],
),
),
// 右侧侧边栏
endDrawer: Drawer(
child: Column(
children: [
ListTile(
leading: CircleAvatar(child: Icon(Icons.home)),
title: Text('我的电脑'),
),
ListTile(
leading: CircleAvatar(child: Icon(Icons.home)),
title: Text('我的电脑'),
),
],
),
),
appBar: AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
flex: 1,
child: TabBar(
enableFeedback: false,
isScrollable: true,
tabs: <Widget>[
Tab(text: "热门1"),
Tab(text: "推荐2"),
Tab(text: "推荐3"),
Tab(text: "推荐4"),
Tab(text: "推荐5"),
],
),
)
],
),
),
body: TabBarView(
children: [
ListView(
children: [
ListTile(title: Text("hello1")),
ListTile(title: Text("hello")),
ListTile(title: Text("hello")),
],
),
ListView(
children: [
ListTile(title: Text("hello2")),
ListTile(title: Text("hello")),
ListTile(title: Text("hello")),
],
),
ListView(
children: [
ListTile(title: Text("hello3")),
ListTile(title: Text("hello")),
ListTile(title: Text("hello")),
],
),
ListView(
children: [
ListTile(title: Text("hello4")),
ListTile(title: Text("hello")),
ListTile(title: Text("hello")),
],
),
ListView(
children: [
ListTile(title: Text("hello5")),
ListTile(title: Text("hello")),
ListTile(title: Text("hello")),
],
)
],
),
),
);
}
}
跟 html 中的 hr 一个意思
Divider(),
Flutter 中常见的表单有 TextField 单行文本框,TextField 多行文本框、CheckBox、Radio、Switch CheckboxListTile、RadioListTile、SwitchListTile、Slide.
child: Column(
children: [
Text('表单组件'),
TextField(),
SizedBox(height: 29),
TextField(
maxLength: 150, // 文字最长
maxLines: 3, // 最多显示三行
decoration: InputDecoration(
hintText: "请输入文本内容", // 提示语的表单
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(4.0),
),
),
prefixIcon: Icon(Icons.search), //输入框内加图标
icon: Icon(Icons.home), // 输入框前面输入图标
),
),
SizedBox(height: 29),
TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: "用户名",
),
),
SizedBox(height: 29),
TextField(
obscureText: true,
obscuringCharacter: '*',
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: "密码",
),
),
],
),
1.有默认值必须这样写·
var username = new TextEditingController(text: '洛基是个猫'); // 初始化给表单赋值
或者这样写··
var username = new TextEditingController(); // 初始化给表单赋值
@override
void initState() {
super.initState();
username.text = '张三';
}
2.没有默认值这样写 var password; // 初始化不赋值
注意:理解定义方式为这个类(组件)的全局变量;
import 'package:flutter/material.dart';
class TextFieldDemoPage extends StatefulWidget {
TextFieldDemoPage({Key? key}) : super(key: key);
@override
State<TextFieldDemoPage> createState() => _TextFieldDemoPageState();
}
class _TextFieldDemoPageState extends State<TextFieldDemoPage> {
var username = new TextEditingController(text: '洛基是个猫'); // 初始化给表单赋值
var password; // 初始化不赋值
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("调试文本"),
),
body: Container(
padding: EdgeInsets.all(20),
child: Column(
children: [
TextField(
onChanged: (value) {
setState(() {
this.username.text = value;
});
},
decoration: InputDecoration(
border: OutlineInputBorder(),
label: Text('用户名'),
),
controller: username, // 接收初始化的值
),
SizedBox(height: 10),
TextField(
obscureText: true,
onChanged: (value) {
setState(() {
this.password = value;
});
},
decoration: InputDecoration(
border: OutlineInputBorder(),
label: Text('密码'),
),
),
SizedBox(height: 30),
ElevatedButton(
onPressed: () {
print(username.text);
print(password);
},
child: Container(
width: double.infinity,
height: 50,
alignment: Alignment.center,
child: Text('下一步'),
),
)
],
),
),
);
}
}
shape: CircleBorder(), //圆形 checkbox
import 'package:flutter/material.dart';
class CheckBoxDemo extends StatefulWidget {
CheckBoxDemo({Key? key}) : super(key: key);
@override
State<CheckBoxDemo> createState() => _CheckBoxDemoState();
}
class _CheckBoxDemoState extends State<CheckBoxDemo> {
var flag;
// @override
void initState() {
super.initState();
flag = true;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('CheckBoxDemo'),
),
body: Column(
children: [
Text('checkBox'),
Divider(),
Checkbox(
shape: CircleBorder(),
activeColor: Colors.red, // 选中颜色
value: flag,
onChanged: (val) {
setState(() {
this.flag = val;
});
}),
Divider(),
CheckboxListTile(
shape: CircleBorder(),
title: Text(
'洛基是猫',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
secondary: Container(
width: 50,
height: 50,
child: ClipOval(
child: Image.network(
'https://www.itying.com/images/flutter/1.png',
fit: BoxFit.cover,
),
),
),
value: flag,
onChanged: (val) {
setState(() {
this.flag = val;
});
},
),
Divider(),
],
),
);
}
}
import 'package:flutter/material.dart';
class RadioDemoPage extends StatefulWidget {
RadioDemoPage({Key? key}) : super(key: key);
@override
State<RadioDemoPage> createState() => _RadioDemoPageState();
}
class _RadioDemoPageState extends State<RadioDemoPage> {
int flag = 1;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('radio test'),
),
body: Container(
padding: EdgeInsets.all(30),
child: Column(
children: [
// radio
Row(
children: [
Text('男'),
Radio(
value: 1,
groupValue: this.flag,
onChanged: (val) {
print(val is int);
setState(() {
this.flag = (val as int);
});
},
),
SizedBox(width: 20),
Text('女'),
Radio(
value: 2,
groupValue: this.flag,
onChanged: (val) {
print(val);
setState(() {
this.flag = (val as int);
});
},
)
],
),
SizedBox(height: 30)
],
),
),
);
}
}
Row(
children: [
Text('开始禁言该群'),
Switch(
value: agree,
activeColor: Colors.green,
onChanged: (value) {
setState(() {
this.agree = value;
});
},
)
],
)
日期转化成时间戳:
var now = new DateTime.now();
print(now.millisecondsSinceEpoch);//单位毫秒,
时间戳转化成日期:
var now = new DateTime.now();
var a=now.millisecondsSinceEpoch; //时间戳
print(DateTime.fromMillisecondsSinceEpoch(a));
date_format 可以格式化日期 文档:https://pub.dev/packages/date_format
第一步.找到文件安装版本,flutter pub get 一下;
2.引入需要使用的地方
import 'package:date_format/date_format.dart';
3.使用找到官网文档的 example 下,查看使用方法
import 'package:date_format/date_format.dart'; // 调用第三方时间格式化库
import 'package:flutter/material.dart';
class DateFormatDemoPage extends StatefulWidget {
DateFormatDemoPage({Key? key}) : super(key: key);
@override
State<DateFormatDemoPage> createState() => _DateFormatDemoPageState();
}
class _DateFormatDemoPageState extends State<DateFormatDemoPage> {
var _nowDate = DateTime.now(); // 获取当前时间
var _nowTime = TimeOfDay(
hour: DateTime.now().hour, minute: DateTime.now().minute); // 获取当前时分秒
// 日期选择器调用方法
_getDate() async {
// 方法一
showDatePicker(
context: context,
initialDate: this._nowDate,
firstDate: DateTime(2022),
lastDate: DateTime(2100),
).then((value) {
setState(() {
this._nowDate = value!;
});
});
// 方法二
// var result = await showDatePicker(
// context: context,
// initialDate: this._nowDate,
// firstDate: DateTime(2022),
// lastDate: DateTime(2100),
// );
// setState(() {
// this._nowDate = result!;
// });
}
// 时间选择器调用方法
_getTime() async {
// 方法一
// showTimePicker(
// context: context,
// initialTime: this._nowTime,
// ).then((value) {
// print(value);
// setState(() {
// this._nowTime = value!;
// });
// });
// 方法二
var result = await showTimePicker(
context: context,
initialTime: this._nowTime,
);
setState(() {
this._nowTime = result!;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('使用第三方格式化时间库')),
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
InkWell(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 强制转化为年月日
Text(formatDate(_nowDate, [yyyy, '年', mm, '月', dd, '日'])),
Icon(Icons.arrow_drop_down_outlined),
],
),
onTap: () {
_getDate();
},
),
InkWell(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 强制转化为时分上午下午
Text('${_nowTime.format(context)}'),
Icon(Icons.arrow_drop_down_outlined),
],
),
onTap: () {
_getTime();
},
)
],
),
),
);
}
}
https://blog.csdn.net/qq_731335498/article/details/124618629
有个 child 属性和点击属性,有点类似与 html 中 block
InkWell(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('选择日期'),
Icon(Icons.arrow_drop_down_outlined),
],
),
onTap: () {},
)
弹窗处理
showDialog AlertDialogshowDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('提示'),
content: const Text('是否确认签到'),
actions: [
ElevatedButton(
onPressed: () {
Navigator.pop(context); // 关闭弹窗
},
child: const Text('确认')),
ElevatedButton(
onPressed: () {
Navigator.pop(context);// 关闭弹窗
},
child: const Text('取消')),
],
);
},
);
Flutter 官网 : https://flutter.dev/Flutter 插件下载地址 : https://pub.dev/packagesFlutter 开发文档 : https://flutter.cn/docs ( 强烈推荐 )官方 GitHub 地址 : https://github.com/flutter
Flutter 中文社区 : https://flutter.cn/
Flutter 实用教程 : https://flutter.cn/docs/cookbook
Flutter CodeLab : https://codelabs.flutter-io.cn/
Dart 中文文档 : https://dart.cn/
Dart 开发者官网 : https://api.dart.dev/
Flutter 中文网 : https://flutterchina.club/ , http://flutter.axuer.com/docs/
Flutter 相关问题 : https://flutterchina.club/faq/ ( 入门阶段推荐看一遍 )
GitHub 上的 Flutter 开源示例 : https://download.csdn.net/download/han1202012/15989510
Flutter 实战电子书 : https://book.flutterchina.club/chapter1/