目标:
1.怎么构建抽屉菜单效果?
2.抽屉菜单怎么定制?
一、抽屉菜单
侧滑抽屉菜单效果
1.1 抽屉菜单入口
Flutter 的脚手架Scaffold,默认提供了抽屉菜单效果入口。
主页面采用一个简单的页面,侧滑菜单首先使用一个Image,先看看侧滑效果。
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:myflutter/drawer/widget/main_drawer.dart';
void main() => runApp(DrawerApp());
/**
* 构建抽屉菜单APP
*
* @author zhouronghua
* @time 2024/6/20 上午9:47
*/
class DrawerApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("抽屉菜单"),
),
drawer: Drawer(
/// 自定义抽屉菜单Widget
child: MainDrawer(),
),
body: new MainPage(),
),
);
}
}
/**
* 主页面
*
* @author zhouronghua
* @time 2024/6/20 上午10:02
*/
class MainPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 主页面简单一个容器
return Container(
child: Text("我是主页面"),
);
}
}
显示效果:
1.2 定制抽屉菜单
抽屉菜单显示用户未登录状态,允许用户点击,点击跳转登录页。
添加用户头像Header,和收藏菜单项
import 'package:flutter/material.dart';
class MainDrawer extends StatefulWidget {
@override
_MainDrawerState createState() {
return _MainDrawerState();
}
}
class _MainDrawerState extends State<MainDrawer> {
/// 用户名:标记用户是否登录
late String _userName;
@override
Widget build(BuildContext context) {
// 顶部用户信息
Widget userHeader = DrawerHeader(
decoration: BoxDecoration(
color: Colors.orangeAccent,
),
child: InkWell(
/// 纵向视图
child: Column(
/// 设置内边距
children: [
Padding(
padding: EdgeInsets.only(bottom: 20.0),
// 采用一个圆形图像
child: CircleAvatar(
backgroundImage: AssetImage('assets/images/logo.png'),
/// 设置图片半径
radius: 40.0,
),
),
Text("请先登录", style: TextStyle(fontSize: 20.0)),
],
),
));
return ListView(
// 列表项子项
children: [
/// 用户头
userHeader,
/// 收藏
InkWell(
onTap: () => {},
child: ListTile(
leading: Icon(Icons.favorite),
title: Text('收藏列表', style: TextStyle(fontSize: 16.0)),
),
),
],
);
}
}
assets的图像可能加载不出来。
怎么解决呢?
1.2.1 解决assets图片加载不出来
添加assets图片,需要注册一下图片。
1)注册图片资源
在项目pubspec.yaml 中,将需要使用的图片进行注册
2)引用图片
路径就是 assets/图片路径。
// 采用一个圆形图像
child: CircleAvatar(
backgroundImage: AssetImage('assets/images/logo.png'),
/// 设置图片半径
radius: 40.0,
),
热重载项目,可以看到图片已经加载出来了。
1.3 抽屉菜单点击事件监听
InkWell 对应的点击事件:onTap
点击用户头像,如果没有登录,则导航跳转登录页面。
// 顶部用户信息
Widget userHeader = DrawerHeader(
decoration: BoxDecoration(
color: Colors.orangeAccent,
),
child: InkWell(
/// 点击事件
onTap: () {
/// 点击跳转登录页面
if (_userName == null) {
/// 用户未登录跳转登录页面
Navigator.push(context, MaterialPageRoute(builder: (context) {
return LoginPage();
}));
debugPrint("open login page");
}
},
/// 纵向视图
child: Column(
/// 设置内边距
children: [
Padding(
padding: EdgeInsets.only(bottom: 20.0),
// 采用一个圆形图像
child: CircleAvatar(
backgroundImage: AssetImage('assets/images/logo.png'),
/// 设置图片半径
radius: 40.0,
),
// child: ClipRRect(
// borderRadius: BorderRadius.circular(40.0),
// child: Image.network(
// 'http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028&app=3028&f=JPEG&fmt=auto?w=960&h=1280',
// fit: BoxFit.cover,
// ),
// ),
),
Text("请先登录", style: TextStyle(fontSize: 20.0)),
],
),
));
二、用户登录页面
用户登录页,输入用户名+密码。
2.1 Form表单
Flutter提供了表单组件,可以定制表单内容。
登录需要输入用户名,输入密码,以及登录按钮,因此可以采用一个列表结构展示。
import 'package:flutter/material.dart';
class LoginPage extends StatefulWidget {
@override
LoginPageState createState() {
return LoginPageState();
}
}
class LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>();
FocusNode _pwdNode = new FocusNode();
late String? _username = null, _password = null;
bool _isObscure = true;
late Color? _pwdIconColor = null;
@override
void dispose() {
_pwdNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("登录"),
),
body: Form(
key: _formKey,
child: ListView(
padding: EdgeInsets.symmetric(horizontal: 22.0),
children: <Widget>[
_buildUserName(),
_buildPwd(),
_buildLogin(),
_buildRegister(),
],
)));
}
Widget _buildRegister() {
return Padding(
padding: EdgeInsets.only(top: 10.0),
child: Row(
///孩子居中对齐
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('没有账号?'),
GestureDetector(
child: Text(
'点击注册',
style: TextStyle(color: Colors.green),
),
onTap: () async {
///进入注册
// Navigator.push(context, MaterialPageRoute(builder: (_) {
// return RegisterPage();
// }));
},
),
],
),
);
}
Widget _buildLogin() {
return Container(
height: 45.0,
margin: EdgeInsets.only(top: 18.0, left: 8.0, right: 8.0),
child: RaisedButton(
child: Text(
'登录',
style: TextStyle(fontSize: 18.0, color: Colors.white),
),
color: Theme.of(context).primaryColor,
onPressed: _doLogin,
),
);
}
Widget _buildPwd() {
return TextFormField(
focusNode: _pwdNode,
///是否隐藏
obscureText: _isObscure,
validator: (String? value) {
if (value?.trim().isEmpty == true) {
return '请输入密码';
}
_password = value;
},
textInputAction: TextInputAction.done,
onEditingComplete: _doLogin,
decoration: InputDecoration(
labelText: '密码',
///输入框尾部图标
suffixIcon: IconButton(
icon: Icon(
Icons.remove_red_eye,
color: _pwdIconColor,
),
onPressed: () {
setState(() {
_isObscure = !_isObscure;
///密码隐藏 图标颜色控制
_pwdIconColor = (_isObscure
? Colors.grey
: Theme.of(context).iconTheme.color)!;
});
})),
);
}
Widget _buildUserName() {
return TextFormField(
autofocus: true,
decoration: InputDecoration(
labelText: '用户名',
),
initialValue: _username,
/// 从注册返回username
///设置键盘回车为下一步
textInputAction: TextInputAction.next,
onEditingComplete: () {
///点击下一步
FocusScope.of(context).requestFocus(_pwdNode);
},
validator: (String? value) {
if (value?.trim().isEmpty == true) {
return '请输入用户名';
}
_username = value;
},
);
}
void _doLogin() async {
_pwdNode.unfocus();
///输入的内容通过验证
if (_formKey.currentState?.validate() == true) {
//TODO 执行登录方法
// var result = await Api.login(_username, _password);
// if (result['errorCode'] == -1) {
// Toast.show(result['errorMsg'], context,
// duration: Toast.LENGTH_LONG, gravity: Toast.BOTTOM);
// } else {
// AppManager.eventBus.fire(LoginEvent(_username));
// Navigator.pop(context);
// }
}
}
}
2.2 输入框
用户名是一个输入框,采用 TextFormField 控件。
Widget _buildUserName() {
return TextFormField(
autofocus: true,
decoration: InputDecoration(
labelText: '用户名',
),
initialValue: _username,
/// 从注册返回username
/// 设置键盘回车为下一步
textInputAction: TextInputAction.next,
onEditingComplete: () {
/// 软键盘点击下一步
FocusScope.of(context).requestFocus(_pwdNode);
},
validator: (String? value) {
if (value?.trim().isEmpty == true) {
return '请输入用户名';
}
_username = value;
},
);
}
2.2.1 属性说明
- autofocus:是否自动获得焦点
- decoration:控件修饰。创建用于的边框、标签、图标和样式的束
装饰“材质设计”文本字段。 - initialValue:输入框初始值
- textInputAction:设置键盘回车为下一步
- onEditingComplete:编辑完成时的回调。软件盘点击下一步进入密码输入框获得焦点
- validator:表单字段有效性校验。如果的内容为空的话,提示“请输入用户名”。点击“登录”的时候,校验输入的用户名内容。这个相当于输入内容的校验逻辑。return 返回的就是错误提示内容。