Flutter - 基础Widget
Flutter 中万物皆 Widget,基础Widget 同步对应 Android View.
普通文本 Text
/** * 控制文本样式统一使用 style:TextStyle, 例:fontSize(字体大小),color(颜色),shadows(阴影)等等 * 控制文本布局需单独设置: * textAlign(文不对齐方式) * textDirection(文不排版方向) * maxLines(最大行数) * overflow(文本截断规则) */ class MyStatelessWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Text( "赵钱孙李,周吴郑王。冯陈褚卫,蒋沈韩杨。朱秦尤许,何吕施张。孔曹严华,金魏陶姜。戚谢邹喻,柏水窦章。云苏潘葛,奚范彭郎。鲁韦昌马,苗凤花方。俞任袁柳,酆鲍史唐。", style: TextStyle( fontSize: 20, color: Colors.blueGrey, decoration: TextDecoration.none), maxLines: 3, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, ); } }
按钮 Button
/** * Button * 最基础最常用:ElevatedButton,TextButton 和 OutlinedButton (Material Design的按钮) * 注:旧版的 FlatButton,RaisedButton,OutLineButton 已被上面控件取代 * 还有显示图标IconButton 和 悬浮按钮FloatingActionButton * 最后用于弹出菜单的 PopupMenuButton * * 主次操作: * ElevatedButton: 凸起的按钮,适合主要操作(如确认、提交) * TextButton: 文本按钮 * OutlinedButton: 带边框的按钮 * * 图标类操作: * IconButton: 显示图标的按钮 * FloatingActionButton: 悬浮按钮,用于突出核心操作 */ class MyStatelessWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Column( children: [ ElevatedButton( onPressed: () { print("Click --- ElevatedButton"); }, child: Text("ElevatedButton")), TextButton( child: Text( "TextButton", style: TextStyle(color: Colors.black87), ), style: TextButton.styleFrom(backgroundColor: Colors.blue), // onHover: Colors.blue, onPressed: () { print("Click --- TextButton"); }, ), OutlinedButton( onPressed: () { print("Click --- OutlinedButton"); }, child: Text("OutlinedButton")), IconButton( onPressed: () { print("Click --- IconButton"); }, icon: Icon(Icons.add)), FloatingActionButton( child: Text("悬浮按钮"), backgroundColor: Colors.teal, onPressed: () { print("Click --- FloatingActionButton"); }), PopupMenuButton( itemBuilder: (context) => [ PopupMenuItem(child: Text("选项1")), PopupMenuItem(child: Text("选项2")), PopupMenuItem(child: Text("选项3")), PopupMenuItem(child: Text("选项3")), ]), ], ); } }
图片 Image
width、height:用于设置图片的宽、高,当不指定宽高时,图片会根据当前父容器的限制, 尽可能的显示其原始大小,如果只设置width、height的其中一个,那么另一个属性默认会按比例缩放, 但可以通过下面介绍的 fit属性 来指定适应规则。 fill:会拉伸填充满显示空间,图片本身长宽比会发生变化,图片会变形。 cover:会按图片的长宽比放大后居中填满显示空间,图片不会变形,超出显示空间部分会被剪裁。 contain:这是图片的默认适应规则,图片会在保证图片本身长宽比不变的情况下缩放以适应当前显示空间,图片不会变形。 fitWidth:图片的宽度会缩放到显示空间的宽度,高度会按比例缩放,然后居中显示,图片不会变形,超出显示空间部分会被剪裁。 fitHeight:图片的高度会缩放到显示空间的高度,宽度会按比例缩放,然后居中显示,图片不会变形,超出显示空间部分会被剪裁。 none:图片没有适应策略,会在显示空间内显示图片,如果图片比显示空间大,则显示空间只会显示图片中间部分。
加载网络图片
class MyStatelessWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Container(
child: Image.network(
"https://img0.baidu.com/it/u=546698500,87821893&fm=253&fmt=auto&app=138&f=PNG?w=1008&h=500",
alignment: Alignment.topCenter,
// color: Colors.redAccent,
// fit: BoxFit.cover,
),
width: 300,
height: 250,
color: Colors.yellow,
),
);
}
}
加载本地图片
/**
* 1 在项目根目录创建 assets 文件夹用于存放资源文件(可创建 images 用于区分资源)
* 2 在 pubspec.yaml 进行配置
* 3 Image.asset 使用图片
*/
class MyStatelessWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Container(
child: Image.asset("assets/images/aaa.png"),
width: 300,
height: 250,
color: Colors.yellow,
),
);
}
}
圆形头像
/**
* 圆形头像(正圆)
* 使用 CircleAvatar 组件,这是最简单的内置圆形头像组件
* 加载网络图片:backgroundImage: NetworkImage(url)
* 加载本地图片:backgroundImage: AssetImage("assets/images/aaa.png")
*/
class MyStatelessWidget extends StatelessWidget {
final imgUrl =
"https://img0.baidu.com/it/u=2428089966,1135217866&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500";
@override
Widget build(BuildContext context) {
return Center(
child: CircleAvatar(
backgroundImage: NetworkImage(imgUrl),
// 控制大小
radius: 100,
// 无背景色
backgroundColor: Colors.transparent,
));
}
}
/**
* 圆形头像(正圆)
* 使用 ClipOval + Image,通过裁剪实现圆形,更灵活
*/
class MyStatelessWidget extends StatelessWidget {
final imgUrl =
"https://img0.baidu.com/it/u=2428089966,1135217866&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500";
@override
Widget build(BuildContext context) {
return Center(
child: ClipOval(
child: Image.network(
imgUrl,
width: 140,
height: 140,
// 确保图片填充
fit: BoxFit.cover
),
)
);
}
}
表单 Widget
TextField
- decoration:用于设置输入框相关的样式
- icon:设置左边显示的图标
- labelText:在输入框上面显示一个提示的文本
- hintText:显示提示的占位文字
- border:输入框的边框,默认底部有一个边框,可以通过InputBorder.none删除掉
- filled:是否填充输入框,默认为false
- fillColor:输入框填充的颜色
- onChanged:监听输入框内容的改变,传入一个回调函数
- onSubmitted:点击键盘中右下角的down时,会回调的一个函数
class TextFieldDemo extends StatefulWidget {
@override
State<TextFieldDemo> createState() => _TextFieldDemoState();
}
class _TextFieldDemoState extends State<TextFieldDemo> {
@override
Widget build(BuildContext context) {
return TextField(
decoration: InputDecoration(
icon: Icon(Icons.people),
labelText: "Tel",
hintText: "请输入手机号",
border: InputBorder.none,
filled: true,
fillColor: Colors.teal
),
onChanged: (value){
print("onChanged --- $value");
},
onSubmitted: (value){
print("onSubmitted --- $value");
},
);
}
}
I/flutter (31441): onChanged --- 1
I/flutter (31441): onChanged --- 11
I/flutter (31441): onChanged --- 110
I/flutter (31441): onSubmitted --- 1
Controller
/**
* 需求:文本框默认输入“10”,
* 当用户修改内容为“111”时,内容自动变更为“123”,且光标移动至最前
*/
class TextFieldDemo extends StatefulWidget {
@override
State<TextFieldDemo> createState() => _TextFieldDemoState();
}
class _TextFieldDemoState extends State<TextFieldDemo> {
// TextEditingController 是一个控制器类,它提供了对 TextField 的更细粒度控制。
final _controller = TextEditingController();
@override
void initState() {
super.initState();
// 设置默认值
_controller.text = "10";
// 1 添加监听,可通过 TextEditingController.text 获取当前文本
_controller.addListener((){
// 2 读取 修改文本内容
if(_controller.text == "111"){
_controller.text = "123";
// 3 控制光标位置
_controller.selection = TextSelection.collapsed(offset: 0);
}
});
}
@override
Widget build(BuildContext context) {
return TextField(
// 设置 Controller
controller: _controller,
decoration: InputDecoration(
icon: Icon(Icons.people),
labelText: "Tel",
hintText: "请输入手机号",
border: InputBorder.none,
filled: true,
fillColor: Colors.teal
),
);
}
@override
void dispose() {
// 必须释放资源,防止内存泄漏
_controller.dispose();
super.dispose();
}
}
Form组件
点击登录时,同时获取 用户名 和 密码
class FormDemo extends StatefulWidget {
const FormDemo({super.key});
@override
State<FormDemo> createState() => _FormDemoState();
}
/**
* 点击登录时,同时获取 用户名 和 密码
* 通过绑定一个 GlobalKey 实现
* 1 初始化 GlobalKey:首先定义一个 GlobalKey<FormState> 类型的变量 _formKey。这个变量将作为表单的唯一标识符。
* 2 设置 GlobalKey:在 Form 的构造函数中,通过 key 参数将 _formKey 分配给 Form
* 3 使用 GlobalKey 操作 Form
* validate():验证所有包含 validator 回调的 TextFormField 是否都有效。
* save():保存所有包含 onSaved 回调的 TextFormField 数据。
* reset():重置所有 TextFormField 到初始状态,清除错误提示等。
*/
class _FormDemoState extends State<FormDemo> {
final _formKey = GlobalKey<FormState>();
String user = "";
String pwd = "";
void loginForm() {
_formKey.currentState?.save();
print("user:$user pwd:$pwd");
}
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
decoration: InputDecoration(
icon: Icon(Icons.people), labelText: "用户名或手机号"),
onSaved: (value) {
user = value.toString();
},
),
TextFormField(
obscureText: true,
decoration:
InputDecoration(icon: Icon(Icons.lock), labelText: "密码"),
onSaved: (value) {
pwd = value.toString();
},
),
SizedBox(
height: 16,
),
Container(
width: double.infinity,
height: 48,
child: TextButton(
style: TextButton.styleFrom(backgroundColor: Colors.teal),
child: Text(
"登 录",
style: TextStyle(fontSize: 16, color: Colors.black87),
),
onPressed: () {
loginForm();
},
),
)
],
));
}
}