Flutter入门-组件学习
基础组件
- Text
- Button
- ElevatedButton,漂浮按钮,默认有背景颜色、阴影效果,按下后阴影会变大。
- TextButton,文本按钮,就是可点击的文本。默认背景透明、不带阴影,按下后会有背景色。
- OutlineButton,边框按钮,默认有一个边框、背景透明、不带阴影,按下后边框颜色会变亮、出现背景(弱)、阴影(弱)。
- IconButton,图标按钮,没有文字,默认没有背景,点击会出现背景。
- FloatingActionButton,悬浮按钮
- 自定义Button,所有的按钮都是直接或间接对RawMaterialButton组件的包装定制。
- 说明:ElevatedButton、TextButton、OutlineButton都可以添加icon。
- Image
- Dialog
- Date
- 表单组件
- TextField
- Checkbox、CheckboxListTile
- Radio、RadioListTile
- Switch
Text
查看源代码
示例1
const Text("hello world")
示例2
const Text(
"hello world",
maxLines: 3,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.black),
),
Button
查看源代码
示例1
ElevatedButton(
onPressed: () {},
style: ButtonStyle(
//背景颜色
backgroundColor: MaterialStateProperty.all(Colors.red),
//字体颜色
foregroundColor: MaterialStateProperty.all(Colors.white),
//阴影颜色
shadowColor: MaterialStateProperty.all(Colors.blue),
//阴影大小
elevation: MaterialStateProperty.all(20.0),
//按钮圆角
shape: MaterialStateProperty.all(
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
)),
child: const Text("按钮"),
),
示例2
TextButton(
onPressed: () {},
child: const Text("按钮"),
),
示例3
OutlinedButton(
onPressed: () {},
style: ButtonStyle(
//字体颜色
foregroundColor: MaterialStateProperty.all(Colors.black),
//边框粗细、颜色
side: MaterialStateProperty.all(
const BorderSide(width: 1, color: Colors.red),
)),
child: const Text("按钮"),
),
示例4
IconButton(
onPressed: () {},
icon: const Icon(Icons.home),
),
示例5
FloatingActionButton(
child: const Text("按钮"),
onPressed: () {},
),
示例6,scaffold属性组件
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.red,
onPressed: () {},
child: const Icon(Icons.add),
),
Image
示例1,加载网络图片
Image.network(
"https://xxx.png",
width: 100,
height: 100,
fit: BoxFit.cover, //使用最多。可能拉伸或裁剪,但是不会变形且填满容器。
//repeat: ImageRepeat.repeatX,
),
示例2,加载本地图片
Image.asset('images/lc_farm1.jpeg', height: 100),
加载本地图片需要进行配置,同一张图片可以配置不同分辨率的,方便适配。
示例3,加载等比例图片
AspectRatio(
aspectRatio: 16.0 / 9.0,
child: Image.network(
"https://www.itying.com/images/flutter/1.png",
fit: BoxFit.cover,
),
),
Dialog
示例1:AlertDialog
/*
* showDialog是一个Future方法,也就是异步方法,我们可以使用async和await
*/
void _alertDialog() async {
var result = await showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text("提示信息"),
content: const Text("确定删除吗?"),
actions: [
TextButton(
onPressed: () {
debugPrint("click 确定");
//pop可以传值,也可以不传值,传值的话可以传递状态给外面。
Navigator.of(context).pop("OK");
},
child: const Text("确定")),
TextButton(
onPressed: () {
debugPrint("click 取消");
Navigator.of(context).pop("Cancle");
},
child: const Text("取消")),
],
);
});
debugPrint("result=$result");
}
示例2
/*
* 如果需要在外部接收对话框的值,可以使用async和await来异步处理,使用pop传递点击结果。
*/
void _simpleDialog() {
showDialog(
context: context,
//点击对话框外面对话框是否消失,默认true
barrierDismissible: true,
builder: (context) {
return SimpleDialog(
title: const Text("请选择语言"),
children: [
SimpleDialogOption(
child: const Text("简体中文"),
onPressed: () {
debugPrint("简体中文");
Navigator.of(context).pop();
},
),
const Divider(),
SimpleDialogOption(
child: const Text("繁体中文"),
onPressed: () {
debugPrint("繁体中文");
Navigator.of(context).pop();
},
),
const Divider(),
SimpleDialogOption(
child: const Text("英文"),
onPressed: () {
debugPrint("英文");
Navigator.of(context).pop();
},
),
],
);
});
}
示例3:
/*
* 如果需要在外部接收对话框的值,可以使用async和await来异步处理,使用pop传递点击结果。
*/
void _bottomSheet() {
showModalBottomSheet(
context: context,
builder: (context) {
return SizedBox(
height: 240,
child: Column(
children: [
ListTile(
title: const Text("分享"),
onTap: () {
debugPrint("click 分享");
Navigator.of(context).pop();
},
),
const Divider(),
ListTile(
title: const Text("收藏"),
onTap: () {
debugPrint("click 收藏");
Navigator.of(context).pop();
},
),
const Divider(),
ListTile(
title: const Text("取消"),
onTap: () {
debugPrint("click 取消");
Navigator.of(context).pop();
},
),
],
),
);
});
}
date
示例1,日期选择器
void _showDatePicker() {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2001),
lastDate: DateTime(2050),
//locale: const Locale('zh'),
).then((value) {
debugPrint("获取选中的日期:$value");
});
}
示例2,时间选择器
void _showTimePicker() async {
var result = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
);
setState(() {
if (result != null) {
_choiceTime = result;
}
});
}
表单型组件
示例1,Checkbox
child: Column(
children: [
Checkbox(
value: c1,
onChanged: (v) {
setState(() => {c1 = v!});
}),
Text(c1 ? "选中" : "未选中"),
CheckboxListTile(
value: c1,
onChanged: (v) => {
setState(() => {c1 = v!})
},
title: const Text("标题"),
subtitle: const Text("二级标题"),
),
const Divider(height: 1.0),
CheckboxListTile(
value: c1,
onChanged: (v) => {
setState(() => {c1 = v!})
},
title: const Text("标题"),
subtitle: const Text("二级标题"),
//设置图片,类似ListTitle的leading属性
secondary: const Icon(Icons.home),
),
],
),
示例2,Radio
Row(
children: [
const Text("男"),
Radio(
value: 1,
groupValue: sex,
onChanged: (v) {
setState(() => {sex = v!});
}),
const Text("女"),
Radio(
value: 0,
groupValue: sex,
onChanged: (v) {
setState(() => {sex = v!});
}),
const SizedBox(width: 50),
Text("选中性别:${sex == 1 ? '男' : '女'}"),
],
),
示例3,RadioListtile
RadioListTile(
value: 0,
groupValue: choice,
title: const Text("标题"),
subtitle: const Text("二级标题"),
secondary: const Icon(Icons.help),
selected: choice == 0,
onChanged: (v) {
setState(() {
choice = v!;
});
}),
示例4,switch
Switch(value: s1, onChanged: _switchChanged),
示例5,TextField
body: Column(
children: [
const SizedBox(height: 10),
const MyTextFieldDemo(),
const SizedBox(height: 10),
TextField(
decoration: const InputDecoration(hintText: "请输入用户名"),
controller: username,
),
TextField(
obscureText: true,
decoration: const InputDecoration(hintText: "请输入密码"),
onChanged: (value) {
password = value;
},
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
debugPrint("用户名=${username.text},密码=$password");
},
child: const Text("按钮"))
],
),
容器类组件
- 框架组件:
- MaterialApp,程序入口组件
- Scaffold,带主题样式标题栏的组件
- 容器:
- Container
- SizeBox
- Padding,给组件设置内边距,因为很多组件没有padding属性。
- 流式布局:
- Wrap
- 线性布局:
- Row,水平布局
- Column,垂直布局
- Expanded,在Row和Column中使用,弹性布局,均分/占满剩余空间
- 层叠布局
- Stack,层叠布局
- Stack+Align,实现定位布局,本质还是层叠布局
- Stack+Positioned,实现定位布局,本质还是层叠布局
Padding/Container/SizeBox
示例1,Padding
Padding(
padding: EdgeInsets.fromLTRB(0, 0, 0, 5),
child: Image.network(
"https://www.itying.com/images/flutter/1.png",
),
),
示例2,Container
SizedBox(height: 5),
示例3,Container
return Container(
width: 100.0,
height: 100.0,
color: Colors.red,
child: Center(
child: Icon(
icon,
color: color,
size: size,
),
),
);
Row/Column/Expanded
示例1,Row
//自定义icon容器组件
class IconContainer extends StatelessWidget {
double size = 32;
Color color = Colors.white;
IconData icon;
IconContainer(this.color, this.size, this.icon);
Widget build(BuildContext context) {
return Container(
width: 100.0,
height: 100.0,
color: Colors.red,
child: Center(
child: Icon(
icon,
color: color,
size: size,
),
),
);
}
}
//使用Row进行布局
class HomeContent extends StatelessWidget {
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, //均分空间
children: [
IconContainer(Colors.white, 32, Icons.home), //自定义组件
IconContainer(Colors.white, 32, Icons.settings),
IconContainer(Colors.white, 32, Icons.search),
],
);
}
}
示例2,Column
class HomeContent extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
width: 2000, //分配较大数值,就可以占满屏幕空间
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, //均分空间
crossAxisAlignment: CrossAxisAlignment.center, //重心
children: [
IconContainer(Colors.white, 32, Icons.home),
IconContainer(Colors.white, 32, Icons.settings),
IconContainer(Colors.white, 32, Icons.search),
],
),
);
}
}
示例3,Expanded,右侧固定大小,左侧自适应占满剩余空间,主要就是flex属性
class HomeContent extends StatelessWidget {
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: IconContainer(Colors.black, Colors.red, 15, Icons.home),
flex: 1),
IconContainer(Colors.black, Colors.green, 15, Icons.home),
],
);
}
}
Wrap
示例
class HomeContent extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
width: 500,
height: 500,
color: Colors.grey,
child: Wrap(
spacing: 10,
runSpacing: 10,
alignment: WrapAlignment.start,
runAlignment: WrapAlignment.end,
children: [
MyButton('button1'),
MyButton('button22222'),
MyButton('button3'),
MyButton('button4444'),
MyButton('button5'),
MyButton('button66666666'),
MyButton('button1'),
MyButton('button22222'),
MyButton('button3'),
MyButton('button4444'),
MyButton('button5'),
MyButton('button66666666'),
],
),
);
}
}
Stack
层叠布局,类似FrameLayout
class HomeContent extends StatelessWidget {
Widget build(BuildContext context) {
return Center(
child: Stack(
alignment: Alignment.center,
children: [
Container(
width: 300,
height: 400,
color: Colors.red,
),
Text('我是文本'),
],
),
);
}
}
功能型组件
- 列表
- ListView,线性布局
- GridView,网络布局
- AnimatedList,动画列表,列表item增加删除时有动画效果
- 图片轮播:Pageview
- 固定宽高比:AspectRatio
- 卡片:Card
- 头像组件:CircleAvatar,显示为圆形的头像效果
- 日期选择器和时间选择器:
- showDatePicker()、DateTime.now()
- showTimePicker()、TimeOfDay.now()
- 对话框
- 提示对话框,在showDialog中返回AlertDialog
- 选择对话框,在showDialog中返回SimpleDialog
- 底部对话框,在showModalBottomSheet中返回Widget
- 自定义对话框,在showDialog中返回MyDialog
- 说明:如果想获取对话框中的按钮点击状态,也就是点击了哪个按钮,可以使用async和await来异步获取,因为showDialog的返回值是一个Future类型的,是异步的。
- 点击事件组件,InkWell,给组件包裹上,组件就有了点击事件。
- 手势组件,GestureDetector,给组件包裹上,组件就有了单击事件、双击事件、长按事件等。
ListView
示例1,基本列表
return ListView(
children: <Widget>[
ListTile(
title: Text('列表标题'),
subtitle: Text('列表二级标题'),
),
ListTile(
title: Text('列表标题'),
subtitle: Text('列表二级标题'),
),
ListTile(
title: Text('列表标题'),
subtitle: Text('列表二级标题'),
),
],
);
示例2,图文混排列表
return ListView(
children: <Widget>[
ListTile(
leading: Image.network(
'https://xxx.png',
width: 50,
height: 50,
),
title: Text('列表标题'),
subtitle: Text('列表二级标题'),
),
ListTile(
leading: Image.network(
'https://xxx.png',
width: 50,
height: 50,
),
title: Text('列表标题'),
subtitle: Text('列表二级标题'),
),
],
);
GridView
body: GridView.count(
crossAxisCount: 2,
crossAxisSpacing: 10.0,
mainAxisSpacing: 10.0,
padding: const EdgeInsets.all(10.0),
children: _getListData(),
),
图片轮播
return Scaffold(
appBar: AppBar(title: const Text("PageView")),
body: PageView(
scrollDirection: Axis.horizontal, //滑动的方向,默认水平
children: [
Center(child: Text("第一屏", style: Theme.of(context).textTheme.headline1)),
Center(child: Text("第二屏", style: Theme.of(context).textTheme.headline1)),
Center(child: Text("第三屏", style: Theme.of(context).textTheme.headline1)),
Center(child: Text("第四屏", style: Theme.of(context).textTheme.headline1)),
],
),
);
Scaffold属性组件
- appBar,标题栏
- floatingActionbar,悬浮按钮
- bottomnavigationBar,底部导航栏
- drawer,左抽屉
- Drawer,抽屉组件
- DrawerHeader,抽屉头部组件
- UserAccountsDrawerHeader,专门定义用户信息的抽屉头部组件
- endDrawer,右抽屉,同理
- title,Tabbar,Tab导航,结合body设置TabBarView
标题栏
appBar: AppBar(title: const Text("PageView")),
Tab导航
import 'package:flutter/material.dart';
class TabBarSample1 extends StatelessWidget {
const TabBarSample1({super.key});
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
title: const Text("自定义APpBar"),
bottom: const TabBar(tabs: [
Tab(text: "热门"),
Tab(text: "推荐"),
]),
),
//TabbarView的children数量和顺序必须和TabBar对应
body: TabBarView(children: [
ListView(
children: const [
ListTile(title: Text("第一个tab")),
ListTile(title: Text("第一个tab")),
ListTile(title: Text("第一个tab")),
],
),
ListView(
children: const [
ListTile(title: Text("第二个tab")),
ListTile(title: Text("第二个tab")),
ListTile(title: Text("第二个tab")),
],
),
]),
));
}
}
底部导航
return Scaffold(
body: _pageList[_currentIndex], //点击标签,显示不同的页面
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
debugPrint("当前选择index:$index");
setState(() {
_currentIndex = index;
});
},
iconSize: 30.0, //icon大小
//fixedColor: Colors.red, //选中颜色
//type: BottomNavigationBarType.fixed, //配置底部可以放多个按钮
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: "首页"),
BottomNavigationBarItem(icon: Icon(Icons.category), label: "分类"),
BottomNavigationBarItem(icon: Icon(Icons.settings), label: "设置"),
],
),
);
抽屉
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
class DrawerPage extends StatelessWidget {
const DrawerPage({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("抽屉页面"),
),
drawer: Drawer(
child: Column(
children: [
const UserAccountsDrawerHeader(
accountName: Text("章三"),
accountEmail: Text("admin@gmail.com"),
currentAccountPicture: CircleAvatar(
backgroundImage: NetworkImage("https://xxx.png"),
),
otherAccountsPictures: [
CircleAvatar(backgroundImage: NetworkImage("https://xxx.png")),
CircleAvatar(backgroundImage: NetworkImage("https://xxx.png")),
],
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
"https://xxx.png"),
fit: BoxFit.cover),
),
),
ListTile(leading: Icon(Icons.home), title: Text("主页")),
Divider(),
ListTile(
leading: Icon(Icons.info),
title: Text("用户中心"),
onTap: () {
Navigator.of(context).pop(); //关闭侧边栏
Navigator.pushNamed(context, '/userCenter'); //跳转
},
),
Divider(),
ListTile(leading: Icon(Icons.settings), title: Text("设置")),
Divider(),
],
),
),
);
}
}