表示一个应用了 Material 界面风格的应用程序,它封装了应用程序实现 Material Design 所需要的一些widget,大多数项目的界面都应该基于 MaterialApp
进行呈现。
该组件的基本用法如下:
MaterialApp(
// 指定应用程序在任务栏上显示的标题
title: 'Flutter初体验',
// 指定应用程序的主界面
home: Text('aaa'),
// 配置应用程序的主题
theme: ThemeData(primarySwatch: Colors.red),
)
用于在页面上渲染普通文本字符串,常见用法如下:
Text('文本内容', style: TextStyle(fontSize: 12))
其中,
参数1:将要渲染的文本内容
参数2:文本内容的样式
该组件是页面结构的脚手架,包含了页面的基本组成单元,例如:
Scaffold(
appBar: AppBar(
title: Text('页面标题'),
),
body: Center(
child: Text('主体内容'),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
drawer: Drawer(),
),
// 主题颜色
theme: ThemeData(primarySwatch: Colors.red),
)
常用的属性:
child【子节点】
padding【内容距离盒子边界的距离】
举例:padding: EdgeInsets.all(10)
margin 【盒子边界之外的距离】
举例:margin: EdgeInsets.all(10)
decoration【盒子的装饰】
举例:
decoration: BoxDecoration(
color: Colors.red,
border: Border(bottom: BorderSide(width: 5, color: Colors.cyan)))
使内部的 children 子元素横向布局
属性:
children【子元素】
mainAxisAlignment【横向对其方式】
crossAxisAlignment【纵向对其方式】
使内部的 children 子元素纵向布局
属性:
children【子元素】
mainAxisAlignment【纵向对其方式】
crossAxisAlignment【横向对其方式】
主要用来控制 flex 布局的占位宽度。
需要用在 Row
或 Column
子组件内部。
举例:
Row(
children: [
Expanded(child: Text('主体内容1'), flex: 2,),
Expanded(child: Text('主体内容2'), flex: 1,)
]
)
页面的导航条区域,示例代码如下:
appBar: AppBar(
// 标题
title: Text('页面标题'),
// 标题是否居中显示
centerTitle: true,
// 右侧的按钮
actions: [
Padding( // 为 IconButton 添加右 padding 为 10px
padding: EdgeInsets.only(right: 10),
child: IconButton( // 右侧的搜索按钮
icon: Icon(Icons.search),
onPressed: () {},
),
)
],
)
在页面上渲染 TabBar
组件,通常配合 Scaffold
组件的 bottomNavigationBar
属性一起使用,用来渲染底部的 TabBar 效果。
注意:
TabBar 最好和有状态页面配合使用
TabBar 必须指定 TabController
控制器,用来控制 TabBar 的切换
如若不指定 TabController
控制器,也可以使用 DefaultTabController
组件,把 TabBar
组件包裹起来,同时提供需要切换的页面个数即可,代码示例如下:
DefaultTabController(
// 指定需要切换的页面个数
length: tablist.length,
child: Scaffold(
appBar: AppBar(),
// 被 tabbar 控制切换的页面
body: TabBarView(),
// 指定 tab 项
bottomNavigationBar: TabBar(
tabs: tablist
)
),
)
TabController 必须用在拥有 TickerProviderStateMixin
或 SingleTickerProviderStateMixin
特征的类中,因此,必须让 TabController 所在的类实现此特征,代码示例如下:
class _HomePageState extends State with SingleTickerProviderStateMixin { }
创建对应的 Tab 项:
List tablist = [
Tab(
text: '正在热映',
icon: Icon(Icons.movie),
),
Tab(
text: '即将上映',
icon: Icon(Icons.movie_filter),
),
Tab(
text: 'Top250',
icon: Icon(Icons.local_movies),
),
];
创建 TabController 对象:
TabController tabCtrl;
在 initState 生命周期函数中,初始化 tabCtrl
对象:
@override
void initState() {
super.initState();
tabCtrl = TabController(vsync: this, length: tablist.length);
}
为 Scaffold 添加 bottomNavigationBar
选项如下:
bottomNavigationBar: Container(
// 设置 tabbar 的背景色
decoration: BoxDecoration(color: Colors.black),
// 设置 tabbar 的高度
height: 50,
// 指定 tabbar 组件
child: TabBar(
// 指定 tabbar 的控制器,必须提供控制器,否则 tabbar 不能正常工作
controller: tabCtrl,
// 设置 tabbar 中文本的样式,注意,height 属性很重要,必须设置为 0,否则文本和图标之间的距离太大,不美观
labelStyle: TextStyle(height: 0, fontSize: 10),
// 设置指示器的颜色
indicatorColor: Colors.red,
// 指定当前 tabbar 总共有几个按钮
tabs: tablist,
),
)
侧边栏抽屉区域,用法比较简洁,主要代码示例如下:
drawer: Drawer(
// 抽屉可能在高度上超出屏幕,所以使用 ListView 组件包裹起来,实现纵向滚动效果
child: ListView(
// 干掉顶部灰色区域
padding: EdgeInsets.all(0),
// 所有抽屉中的子组件都定义到这里:
children: [],
))
提供类似于下图这样的效果,一般都用在侧边栏 Drawer
组件中:
其中,accountName
和 accountEmail
是必选项。
另外,头像区域使用 currentAccountPicture
进行指定;
背景区域使用 decoration
属性进行指定;
详细代码示例如下:
UserAccountsDrawerHeader(
accountName: Text('刘龙宾'),
accountEmail: Text('[email protected]'),
currentAccountPicture: CircleAvatar(
backgroundImage: NetworkImage(
'https://images.gitee.com/uploads/91/465191_vsdeveloper.png'),
),
decoration: BoxDecoration(
// 设置背景图片
image: DecorationImage(
// 控制图片填充效果
fit: BoxFit.fill,
// 指定图片地址
image: NetworkImage(
'http://www.liulongbin.top:3005/images/bg1.jpg'))),
)
滚动列表,可以很单纯中填充固定个数的内容,也可以循环渲染列表数据。
用法最简单,将需要的内容节点,直接写入到 ListView
组件的 children
属性中即可,代码示例如下:
ListView(
children: [
ListTile(
title: Text('我要发布'),
trailing: Icon(Icons.send),
),
Divider(),
ListTile(
title: Text('注销'),
trailing: Icon(Icons.exit_to_app),
)
]
)
如果要把列表数据,通过循环渲染之后,得到 UI 结构类似的列表页面,推荐使用 ListView.builder()
,基础用法如下:
ListView.builder(
// 必须指定列表项的长度
itemCount: 列表项的长度,
// Item 项的构建器
itemBuilder: (BuildContext ctx, int i) {
return Text('aaa');
}
)
如果存在多个列表页之间的动态切换,默认无法保持每个列表项的滚动距离、数据状态等信息,此时需要实现 AutomaticKeepAliveClientMixin
特征,来保持列表项的滚动状态,示例代码如下:
class _MovieListState extends State
with AutomaticKeepAliveClientMixin {
// 重写 wantKeepAlive 函数
@override
bool get wantKeepAlive => true;
}
有时候,需要实现列表滚动到页面底部之后,自动加载下一页数据,此时需要借助于 ScrollController
实现滚动监听,示例代码如下:
// 定义私有变量 _scrollCtrl
ScrollController _scrollCtrl;
// 重写 initState 生命周期函数
@override
void initState() {
// 此行为默认代码,不能删除
super.initState();
// 初始化一个 ScrollController 滚动控制器
_scrollCtrl = new ScrollController();
// 为 _scrollCtrl 滚动控制器添加监听事件
_scrollCtrl.addListener(() {
// _scrollCtrl.position.pixels 当前列表滚动的距离
// _scrollCtrl.position.maxScrollExtent 列表的最大滚动距离
if (_scrollCtrl.position.pixels == _scrollCtrl.position.maxScrollExtent) {
// 调用 setState 函数,让页码值 +1
setState(() {
_page++;
});
// 获取新页面的数据
_getMovieList();
}
});
}
如果想监听 ListView
组件的滚动效果,可以为 ListView.builder()
提供 controller
属性,值为 ScrollController
的示例对象,示例代码如下:
ListView.builder(
controller: _scrollCtrl,
itemCount: _mlist.length,
itemBuilder: (BuildContext ctx, int i) {}
)
当页面被销毁的时候,最好主动销毁对应的滚动控制器,主动释放内存,提高性能:
// 重新 dispose 函数
@override
void dispose() {
super.dispose();
// 主动销毁滚动控制器
_scrollCtrl.dispose();
}
如果分页加载数据,则应该让旧数组主动拼接新数组,代码示例如下:
setState(() {
_total = result.data['count'];
// 调用数组的 addAll 方法,可以主动合并另一个数组
_mlist.addAll(result.data['subjects']);
});
提供圆形的用户头像区域,使用起来比较简单,示例代码如下:
CircleAvatar(
backgroundImage: NetworkImage(
'https://images.gitee.com/uploads/91/465191_vsdeveloper.png'),
)