Flutter 的 UI 渲染是基于一系列组件完成,这里从基础简单的组件开始学习,重点熟悉其常用属性方法,后期会不断完善部分不常用用的属性。
FlutterLogo
FlutterLogo(
duration: Duration(microseconds: 1000),
//textColor: Colors.red,
size: 200,
style: FlutterLogoStyle.stacked,
),
Text
Column(
children: [
Text(text1,textAlign:
TextAlign.left, //左对齐
),
Text(text2,
maxLines: 2, //最多2行展示
),
Text(text3,
textAlign: TextAlign.left, //文本不足一行时对齐式没有意义
style: const TextStyle(
color: Colors.red,
fontSize: 18,
height: 1.2, //行高因子行高=fontSize*height
decoration: TextDecoration.underline,
backgroundColor: Colors.yellow),
),
],
),
文字间距
Text("第五段测试文字 Word",
style: TextStyle(
fontSize: 24,
letterSpacing: 1, //字符间距
wordSpacing: 4, //单词间距
height: 1.5,
)),
文字阴影
Text("第六段测试文字",
style: TextStyle(
fontSize: 22,
height: 1.5,
shadows: [
//文字阴影可以叠加多个
BoxShadow(offset: Offset(2, 2), color: Colors.red, blurRadius: 8),
BoxShadow(offset: Offset(-2, -2), color: Colors.green, blurRadius: 8)
]
)),
富文本
Text.rich(TextSpan(children: [
TextSpan(text: "头部文字", style: TextStyle(color: Colors.red)),
TextSpan(text: "尾部文字", style: TextStyle(color: Colors.green, fontSize: 20))
])),
图片
通过Image组件来加载并显示图片,Image的数据源可以是asset、文件、内存以及网络.
ImageProvider 是一个抽象类,主要定义了图片数据获取的接口load(),从不同的数据源获取图片需要实现不同的ImageProvider ,如AssetImage是实现了从Asset中加载图片的 ImageProvider,而NetworkImage 实现了从网络加载图片的 ImageProvider
- 本地图片
Image(image:AssetImage("assets/images/news/news_list_clicked@2x.png"));
Image.asset("assets/images/news/news_list_clicked@2x.png");
- 网络图片
图片只会小于给定尺寸,但为避免失真图片周边会留白不会根据给定尺寸放大
Image(image:
NetworkImage("https://avatars2.githubusercontent.com/u/20411648?s=460&v=4"),
width: 100,
),
Image.network(
"https://avatars2.githubusercontent.com/u/20411648?s=460&v=4",
width: 100,
),
- FadeInImage 加载图片时需要占位图片,并在图片加载完成后无缝切换展示,就要用FadeInImage
FadeInImage(
placeholder: AssetImage('xxxx'),
image: NetworkImage("xxxx")
)
FadeInImage.assetNetwork(
placeholder: "xxx",
image: 'xxx'
)
一般会考虑使用第三方库cached_network_image
CachedNetworkImage(
//进度条占位 线性进度条LinearProgressIndicator
placeholder: (context, url) => CircularProgressIndicator(),
//本地占位图
placeholder: (context, url) => Image.asset(imageName),
//请求错误后显示
errorWidget: (context, url, error) => Image.asset(imageName),
width: 200,
height: 200,
imageUrl: imageUrl,
);
- 字体图标
Icon(Icons.back_hand),
Icon(CupertinoIcons.refresh),
Icon(Icons.share),
Icon(CupertinoIcons.share),
Icon(Icons.camera),
Icon(CupertinoIcons.camera)],
Text("\uE03e",
style: TextStyle(
fontFamily: "MaterialIcons",
fontSize: 24.0,
color: Colors.green)
),
单选开关和复选框
组件本身不会保存当前选中状态,选中状态都是由父组件来管理的
Checkbox的大小是固定的,无法自定义,而Switch只能定义宽度,高度也是固定的
CupertinoSwitch(
value: _switchSelected,
activeColor: Colors.orange,
onChanged: (isOpen) {
_switchSelected = !_switchSelected;
setState(() {});
}),
Switch(
value: _switchSelected,
activeColor: Colors.red,
onChanged: (isOpne) {
_switchSelected = !_switchSelected;
setState(() {});
}),
Checkbox(
value: _checkboxSelected,
activeColor: Colors.blue,
onChanged: (isCheck) {
_checkboxSelected = !_checkboxSelected;
setState(() {});
}),
按钮
ElevatedButton(
child: Text("normal"),
//label: Text("发送"),
onPressed: () {},
),
TextButton(
child: Text("submit"),
onPressed: () {},
),
OutlinedButton(
onPressed: (){},
child: Text("outline"));
IconButton(
icon: Icon(Icons.back_hand),
onPressed: () {},
)
button样式需要根据不同的状态进行设置,
Container(
//相设置一个左右边距40的按钮,先设置外边
margin: EdgeInsets.only(top: 10, left: 40, right: 40),
//再设置一个紧约束,并且宽度为无限大
child: SizedBox(
width: double.infinity,
height: 40,
child: ElevatedButton(
onPressed: () {},
//样式设置<MaterialStateProperty>
style: ButtonStyle(
//不区分状态统一设置
//backgroundColor: MaterialStateProperty.all(const Color(0xffFCB605)),
//根据不同的状态设置不同颜色
backgroundColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.pressed)) {
return Colors.deepPurple;
} else {
return Colors.green;
}
},),
//前景色
foregroundColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.pressed)) {
return Colors.deepPurple;
} else {
return Colors.red;
}
},),
//设置水波纹颜色
//overlayColor: MaterialStateProperty.all(Colors.yellow),
//设置阴影 不适用于这里的TextButton
//elevation: MaterialStateProperty.all(10),
//设置边框
side: MaterialStateProperty.all(BorderSide(color: Colors.red, width: 1)),
//外边框装饰
//shape: MaterialStateProperty.all(StadiumBorder()),
shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius:BorderRadius.circular(20))),
),
child: const Text(
"登 录",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
),
),
),
);
button样式快速设置
SizedBox(
height: 45,
width: 200,
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
elevation: 10, shape: const StadiumBorder()),
child: const Center(child: Text('Elevated Button')),
),
)
SizedBox(
height: 45,
width: 200,
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
elevation: 10, shape: const StadiumBorder()),
child: const Center(child: Text('Elevated Button')),
),
);
进度指示器
//进度条显示50%
LinearProgressIndicator(
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation(Colors.blue),
value: .5,
)
//进度条显示50%,会显示一个半圆
CircularProgressIndicator(
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation(Colors.blue),
value: .5,
),
输入框
TextField(
//先设置控制器
controller: textController,
//装饰
decoration: InputDecoration(
//左侧外图标
icon: const Icon(Icons.lock),
//左侧内图档
// prefix: //前缀
// prefixText: "前缀文字",
prefixIcon: const Icon(Icons.person),
//右侧图片后缀
// suffix:,
// suffixText: ,
// suffixIcon: Icon(Icons.clear, color: Colors.red),
suffixIcon: IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
textController.clear();
},
style: ButtonStyle(),
),
//填充 false 时填充颜色无效
filled: false,
fillColor: Colors.red,
//边框
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
// 未获得焦点下划线设为灰色
// enabledBorder: UnderlineInputBorder(
// borderSide: BorderSide(color: Colors.grey),
// ),
//获得焦点下划线设为蓝色
// focusedBorder: UnderlineInputBorder(
// borderSide: BorderSide(color: Colors.blue),
// ),
//获得焦点时边框颜色
// focusedBorder: OutlineInputBorder(
// borderSide: BorderSide(color: Colors.orange),
// borderRadius: BorderRadius.all(Radius.circular(20)),
// ),
//未获得焦点时边框颜色
// enabledBorder: OutlineInputBorder(
// borderSide: BorderSide(color: Colors.red),
// borderRadius: BorderRadius.all(Radius.circular(20)),
// ),
//悬浮提示文字
// labelText: " ",
// labelStyle: TextStyle(color: Colors.red),
//内间距,内间距调整会更改输入框高度
// contentPadding: const EdgeInsets.all(0),
isDense: false,
hintText: "提示文字 ",
hintStyle: const TextStyle(color: Color(0xff999999)),
),
//正在编辑文字的样式
style: const TextStyle(
color: Color.fromARGB(255, 80, 68, 68),
fontWeight: FontWeight.bold,
),
//text 字符串 number 数字 phone 手机号url 链接 emailAddress邮箱 datetime 时间
keyboardType: TextInputType.text,
//完成按钮样式
textInputAction: TextInputAction.search,
autofocus: false, //是否自动获取取焦点
obscureText: false, //是否加密展示
maxLines: 1, //最大展示行数,默认1
//最大字符数,设置后输入框下面会有一定间距放置counter
// maxLength: 11,
// maxLengthEnforcement: MaxLengthEnforcement.enforced,
//长按或鼠标右击时出现的菜单
toolbarOptions: const ToolbarOptions(copy: false, paste: false),
onChanged: (value) {},
onEditingComplete: () {},
onSubmitted: (value) {},
//输入格式
inputFormatters: [
//只允许输入数字
//FilteringTextInputFormatter.digitsOnly,
],
//是否可用
enabled: true,
// cursorColor: ColorRes.public_FFFF0035, //光标颜色
// cursorHeight: 22, //光标高度
// cursorWidth: 5, //光标宽度
)
另外关于计数器,可以进行重构
//没有最大字数限制,但希望显示counter,可以设置最大字数为-1
//有字数限制,但不希望显示counter,可以重构build 方法,返回值为Container()或空。
TextField(maxLength: 10,
buildCounter: (context, {int ? currentLength, bool? isFocused, int? maxLength}) {
return Container();
},
),