App开发之账号系统实战篇
漂泊80
共 54278字,需浏览 109分钟
·
2020-11-20 06:28
数据库操作
创建数据库[songbei]
创建用户表[user]
3.验证码表[validate]
注册功能
注册用到两个协议:注册和发送验证码
客户端注册页
import 'package:common_utils/common_utils.dart';
import 'package:flutter/material.dart';
import 'package:country_code_picker/country_code_picker.dart';
import 'package:flutter_songbei/custom/login_head.dart';
import 'package:flutter_songbei/network/chttp.dart';
import 'package:flutter_songbei/network/params.dart';
import 'package:flutter_songbei/utils/toast_util.dart';
import '../../app_theme.dart';
import '../app_web_page.dart';
class SignupPage extends StatefulWidget {
@override
createState() {
return _SignupState();
}
}
class _SignupState extends State
{ TextEditingController phoneController = TextEditingController();
TextEditingController pwdController = TextEditingController();
TextEditingController codeController = TextEditingController();
String country_code = '+86';
String _verifyCodeTips = '发送验证码';
bool _checkboxSelected = false;
bool b_phone_clear = false;
bool b_pwd_clear = false;
bool b_code_clear = false;
TimerUtil timerCountDown;
///倒计时
int _verifyCodeCD = 0;
@override
void dispose() {
super.dispose();
if (timerCountDown != null) timerCountDown.cancel();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('注册'),
centerTitle: true,
),
body: Container(
decoration: BoxDecoration(),
padding: EdgeInsets.fromLTRB(20.0, 40.0, 20.0, 0),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children:
[ LoginHead(),
Column(
children:
[ TextField(
controller: phoneController,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
hintText: '请输入手机号',
filled: true,
fillColor: AppTheme.textWhite)),
Stack(
children:
[
[ :Container(
color: Colors.white,
child: CountryCodePicker(
onChanged: _onCountryChange,
Initial selection and favorite can be one of code ('IT') OR dial_code('+39')
initialSelection: 'CN',
favorite: ['+86', 'china'],
optional. Shows only country name and flag
showCountryOnly: false,
optional. Shows only country name and flag when popup is closed.
showOnlyCountryWhenClosed: false,
optional. aligns the flag and the Text left
alignLeft: false,
),
),
Expanded(
child: TextField(
controller: phoneController,
keyboardType: TextInputType.phone,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
hintText: '请输入手机号',
filled: true,
fillColor: AppTheme.loginFillColor),
onChanged: (String text) {
通过onChanged事件更新_isComposing 标志位的值
{
调用setState函数重新渲染受到_isComposing变量影响的IconButton控件
b_phone_clear = text.length >
//new 如果文本输入框中的字符串长度大于0则允许发送消息
//new
},
),
)
]),
Positioned(
right: 0,
child: Offstage(
offstage: !b_phone_clear,
child: IconButton(
iconSize: 20.0,
icon: ImageIcon(
AssetImage('assets/user/qingchu.png'),
color: AppTheme.mainColor,
),
onPressed: () {
''; =
{
b_phone_clear = false;
});
},
),
),
)
],
),
Padding(
padding: EdgeInsets.fromLTRB(0, 10, 0, 0),
child: Stack(
children:
[ TextField(
controller: pwdController,
obscureText:true,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
hintText: '请输入密码',
filled: true,
fillColor: AppTheme.loginFillColor),
onChanged: (String text) {
{
b_pwd_clear = text.length > 0;
});
},
),
Positioned(
right: 0,
child: Offstage(
offstage: !b_pwd_clear,
child: IconButton(
iconSize: 20.0,
icon: ImageIcon(
AssetImage('assets/user/qingchu.png'),
color: AppTheme.mainColor,
),
onPressed: () {
''; =
{
b_pwd_clear = false;
});
},
),
),
)
],
),
),
Row(
mainAxisSize: MainAxisSize.min,
children:
[ Expanded(
flex: 1,
child: Stack(
children:
[ TextField(
controller: codeController,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
hintText: '请输入验证码',
helperText: '验证码'
filled: true,
fillColor: AppTheme.loginFillColor),
onChanged: (String text) {
{
b_code_clear = text.length > 0;
});
},
),
Positioned(
right: 0,
child: Offstage(
offstage: !b_code_clear,
child: IconButton(
iconSize: 20.0,
icon: ImageIcon(
AssetImage('assets/user/qingchu.png'),
color: AppTheme.mainColor,
),
onPressed: () {
''; =
{
b_code_clear = false;
});
},
),
),
)
],
)),
Container(
margin: EdgeInsets.all(10.0),
child: OutlineButton(
borderSide: BorderSide(color: AppTheme.mainColor),
child: Text(
_verifyCodeTips,
style: TextStyle(color: AppTheme.mainColor),
),
onPressed: _onVeriCode,
),
)
],
),
Container(
margin: EdgeInsets.fromLTRB(0, 20, 0, 0),
width: 260,
child: RaisedButton(
color: AppTheme.mainColor,
child: Text(
'注册',
style: AppTheme.loginButtonStyle,
),
onPressed: () {
_onSignup(context);
},
)),
Container(
alignment: Alignment.center,
child: Row(
mainAxisSize: MainAxisSize.min,
children:
[ Checkbox(
value: _checkboxSelected,
activeColor: AppTheme.mainColor, //选中时的颜色
onChanged: (value) {
{
_checkboxSelected = value;
});
},
),
Text('已阅读并同意'),
GestureDetector(
child: Text(
'《用户服务协议》',
style: TextStyle(color: AppTheme.mainColor),
),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => AppWebPage(
'用户服务协议',
CHttp.getAppWeb(CHttp.APPH5_AGREEMENT))));
},
)
],
),
)
],
)
],
),
)),
);
}
void _onSignup(BuildContext context) {
if (phoneController.text.length == 0) {
'请输入手机号');
return;
}
if (pwdController.text.length == 0) {
'请输入密码');
return;
}
if (codeController.text.length == 0) {
'请输入验证码');
return;
}
if (!_checkboxSelected) {
'请阅读并同意用户服务协议');
return;
}
_reqSingnup();
}
void _onVeriCode() {
if (phoneController.text.length == 0) {
'请输入手机号');
return;
}
if (_verifyCodeCD > 0) {
_verifyCodeCD.toString() + '秒后可重新发送');
return;
}
_reqVerifyCode();
}
verifycode
{
CHttp.post(
CHttp.USER_VALIDATE,
{
if (!mounted) {
return;
}
_verifyCodeCD = data['time'];
{
_verifyCodeTips = _verifyCodeCD.toString() + '秒后重新发送';
});
//倒计时test
timerCountDown =
1000, mTotalTime: _verifyCodeCD * 1000); :
value) {
double tick = (value / 1000);
LogUtil.e("CountDown: " + tick.toInt().toString());
if (value == 0) {
{
_verifyCodeCD = tick.toInt();
_verifyCodeTips = '发送验证码';
});
else {
{
_verifyCodeCD = tick.toInt();
_verifyCodeTips = tick.toInt().toString() + '秒后重新发送';
});
}
});
timerCountDown.startCountDown();
},
params: PUserVerifyCode(country_code, phoneController.text, 1).toJson(),
errorCallback: (err) {
err);
});
}
void _onCountryChange(CountryCode countryCode) {
manipulate the selected country code here :
Country selected: " + countryCode.toString());
country_code = countryCode.toString();
}
/**
注册用户
*/
{
CHttp.post(
CHttp.USER_REGISTER,
{
if (!mounted) {
return;
}
LogUtil.e(data);
String userid = data['userid'];
Navigator.of(context).pop(userid);
'请前往登录页进行登录');
},
params: PUserSignup(country_code, phoneController.text,
codeController.text)
.toJson(),
errorCallback: (err) {
err);
});
}
}
服务器注册协议
router.post('/register', function(req, res, next) {
try {
ru.logReq(req);
var country_code = req.body.country_code;
var phone = req.body.phone;
var password = req.body.password;
var code = req.body.code;
var os = req.body.os;
var userid = utils.getUserid(phone);
if (!phone || !password || !code || !country_code) {
ru.resError(res, '参数错误!');
} else if (!utils.checkPhone(phone)) {
ru.resError(res, '请填写正确的手机号格式');
} else {
userDao.queryUser(phone, function(err, result) {
if (err) {
ru.resError(res, err)
} else {
if (result.length > 0) {
ru.resError(res, '用户已经注册')
} else {
if (conf_validate) {
validateDao.queryValidate(country_code, phone, 1, function(err, objs) {
if (err) {
ru.resError(res, err);
} else {
var length = objs.length;
if (length > 0) {
var validate = objs[0];
if (validate.phone == phone && validate.code == code) {
userDao.addUser(userid, country_code, phone, password, os, function(err, result) {
if (err) {
ru.resError(res, err);
} else {
var title = deftext.register_title;
var content = deftext.register_content;
var data = {
type: 0,
userid: userid,
title: title,
content: content,
extend: {},
};
httputil.requstPSPost('/message/actionmsg', data, function(err, result) {
if (err) {
logger.error(err)
}
})
var user = result.length > 0 ? result[0] : {};
ru.resSuccess(res, user);
}
});
} else {
ru.resError(res, '验证码错误');
}
} else {
ru.resError(res, '验证码错误');
}
}
});
} else {
userDao.addUser(userid, country_code, phone, password, os, function(err, result) {
if (err) {
ru.resError(res, err);
} else {
var title = deftext.register_title;
var content = deftext.register_content;
var data = {
type: 0,
userid: userid,
title: title,
content: content,
extend: {},
};
httputil.requstPSPost('/message/actionmsg', data, function(err, result) {
if (err) {
logger.error(err)
}
})
var user = result.length > 0 ? result[0] : {};
ru.resSuccess(res, user);
}
});
}
}
}
});
}
} catch (err) {
ru.resError(res, err.message);
}
});
数据库userDao.js
queryUser:function(phone,callback){
console.log("phone:",phone)
var sql = 'SELECT * FROM user WHERE phone = ? LIMIT 1';
pool.getConnection(function(err, connection) {
try{
connection.query(sql, phone, function(err, result) {
callback(err, result)
connection.release();
});
}catch(err){
logger.error(err);
callback(err,null);
}
});
},
/**
* 添加用户
*/
addUser:function(userid,country_code,phone,password,os,callback){
var sql = 'INSERT INTO user(userid,country_code,phone,password,os) VALUES(?,?,?,?,?)';
pool.getConnection(function(err, connection) {
connection.query(sql, [userid,country_code,phone,password,os], function(err, result) {
if(err){
callback(err, result)
connection.release();
}else{
var id = result.insertId;
sql = 'SELECT * FROM '+USER_TABLE+' WHERE id = ?';
connection.query(sql,[id],function(err, result){
callback(err, result)
connection.release();
})
}
});
});
},
登录功能
客户端登录页
import 'dart:ui';
import 'package:common_utils/common_utils.dart';
import 'package:country_code_picker/country_code_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_songbei/custom/login_head.dart';
import 'package:flutter_songbei/custom/network_loading.dart';
import 'package:flutter_songbei/network/chttp.dart';
import 'package:flutter_songbei/network/params.dart';
import 'package:flutter_songbei/provider/app.dart';
import 'package:flutter_songbei/provider/user.dart';
import 'package:flutter_songbei/shared/app_shared.dart';
import 'package:flutter_songbei/utils/toast_util.dart';
import 'package:provider/provider.dart';
import '../../app_theme.dart';
import 'forgot_password_page.dart';
import 'modify_info_page.dart';
import 'signup_page.dart';
class LoginPage extends StatefulWidget {
State
createState() { return _LoginState();
}
}
class _LoginState extends State<LoginPage> {
TextEditingController phoneController = TextEditingController();
TextEditingController pwdController = TextEditingController();
TextEditingController codeController = TextEditingController();
String country_code = '+86';
/**
* 登录方式 0 密码登录 1 验证码登录
*/
int login_type = 0;
String _verifyCodeTips = '发送验证码';
TimerUtil timerCountDown;
///倒计时
int _verifyCodeCD = 0;
bool b_visible = false;
bool b_clear = false;
bool b_phone_clear = false;
void dispose() {
super.dispose();
if (timerCountDown != null) timerCountDown.cancel();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('登录'),
centerTitle: true,
),
body: Container(
decoration: BoxDecoration(),
padding: EdgeInsets.fromLTRB(20.0, 40.0, 20.0, 0),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children:
[ LoginHead(),
Column(
children:
[ // Row(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// children:
[ // FlatButton(
// child: Text(
// '密码登录',
// style: TextStyle(
// color: login_type == 0
// ? AppTheme.mainColor
// : AppTheme.grayColor,
// decoration: TextDecoration.underline,
// decorationStyle: TextDecorationStyle.solid,
// fontSize: 16.0,
// ),
// ),
// onPressed: () {
// setState(() {
// login_type = 0;
// phoneController.text = '';
// pwdController.text = '';
// });
// },
// ),
// FlatButton(
// child: Text(
// '验证码登录',
// style: TextStyle(
// color: login_type == 1
// ? AppTheme.mainColor
// : AppTheme.grayColor,
// decoration: TextDecoration.underline,
// decorationStyle: TextDecorationStyle.solid,
// fontSize: 16.0,
// ),
// ),
// onPressed: () {
// setState(() {
// login_type = 1;
// phoneController.text = '';
// pwdController.text = '';
// });
// },
// ),
// ]
// ),
_buildLoginType(),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children:
[ FlatButton(
child: Text(
'注册',
style: TextStyle(
color: AppTheme.mainColor,
decoration: TextDecoration.underline,
decorationStyle: TextDecorationStyle.solid,
fontSize: 16.0,
),
),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) =>
SignupPage()));
},
),
// FlatButton(
// child: Text(
// '邮箱注册',
// style: TextStyle(
// color: AppTheme.mainColor,
// decoration: TextDecoration.underline,
// decorationStyle: TextDecorationStyle.solid,
// fontSize: 16.0,
// ),
// ),
// onPressed: () {
//// Navigator.of(context).push(MaterialPageRoute(
//// builder: (BuildContext context) => EmailSignupPage()));
// },
// ),
FlatButton(
child: Text(
'忘记密码',
style: TextStyle(
color: AppTheme.mainColor,
decoration: TextDecoration.underline,
decorationStyle: TextDecorationStyle.solid,
fontSize: 16.0,
),
),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) =>
ForgotPasswordPage()));
},
)
],
)
],
)
],
),
)),
);
}
void _onCountryChange(CountryCode countryCode) {
//TODO : manipulate the selected country code here
print("New Country selected: " + countryCode.toString());
country_code = countryCode.toString();
}
_buildLoginType() {
if (login_type == 0) {
return Column(
children:
[ Stack(
children:
[ Row(children:
[ Expanded(
child: TextField(
controller: phoneController,
keyboardType: TextInputType.phone,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
hintText: '手机号',
filled: true,
fillColor: AppTheme.loginFillColor),
onChanged: (String text) {
//new 通过onChanged事件更新_isComposing 标志位的值
setState(() {
//new 调用setState函数重新渲染受到_isComposing变量影响的IconButton控件
b_phone_clear =
text.length > 0; //new 如果文本输入框中的字符串长度大于0则允许发送消息
}); //new
},
),
)
]),
Positioned(
right: 0,
child: Offstage(
offstage: !b_phone_clear,
child: IconButton(
iconSize: 20.0,
icon: ImageIcon(
AssetImage('assets/user/qingchu.png'),
color: AppTheme.mainColor,
),
onPressed: () {
phoneController.text = '';
setState(() {
b_phone_clear = false;
});
},
),
),
)
],
),
Padding(
padding: EdgeInsets.fromLTRB(0, 20, 0, 0),
child: Stack(
children:
[ TextField(
controller: pwdController,
obscureText: !b_visible,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
hintText: '请输入密码',
filled: true,
fillColor: AppTheme.loginFillColor),
onChanged: (value) {
setState(() {
b_clear = value.length > 0;
});
},
),
Positioned(
right: 0,
child: IconButton(
iconSize: 25.0,
icon: ImageIcon(
AssetImage(b_visible
? 'assets/user/kejian.png'
: 'assets/user/bukejian.png'),
color: AppTheme.mainColor,
),
onPressed: () {
setState(() {
b_visible = !b_visible;
});
},
),
),
Positioned(
right: 35,
child: Offstage(
offstage: !b_clear,
child: IconButton(
iconSize: 20.0,
icon: ImageIcon(
AssetImage('assets/user/qingchu.png'),
color: AppTheme.mainColor,
),
onPressed: () {
setState(() {
pwdController.text = '';
b_clear = false;
});
},
),
),
)
],
),
),
Container(
margin: EdgeInsets.fromLTRB(0, 20, 0, 0),
width: 260,
child: RaisedButton(
color: AppTheme.mainColor,
child: Text(
'登录',
style: AppTheme.loginButtonStyle,
),
onPressed: () {
_onLogin(context);
},
)),
],
);
} else if (login_type == 1) {
return Column(
children:
[ Stack(
children:
[ Row(children:
[ Container(
color: Colors.white,
child: CountryCodePicker(
onChanged: _onCountryChange,
// Initial selection and favorite can be one of code ('IT') OR dial_code('+39')
initialSelection: 'CN',
favorite: ['+86', 'china'],
// optional. Shows only country name and flag
showCountryOnly: false,
// optional. Shows only country name and flag when popup is closed.
showOnlyCountryWhenClosed: false,
// optional. aligns the flag and the Text left
alignLeft: false,
),
),
Expanded(
child: TextField(
controller: phoneController,
keyboardType: TextInputType.phone,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
hintText: '请输入手机号',
filled: true,
fillColor: AppTheme.loginFillColor),
onChanged: (String text) {
//new 通过onChanged事件更新_isComposing 标志位的值
setState(() {
//new 调用setState函数重新渲染受到_isComposing变量影响的IconButton控件
b_phone_clear =
text.length > 0; //new 如果文本输入框中的字符串长度大于0则允许发送消息
}); //new
},
),
)
]),
Positioned(
right: 0,
child: Offstage(
offstage: !b_phone_clear,
child: IconButton(
iconSize: 20.0,
icon: ImageIcon(
AssetImage('assets/user/qingchu.png'),
color: AppTheme.mainColor,
),
onPressed: () {
phoneController.text = '';
setState(() {
b_phone_clear = false;
});
},
),
),
)
],
),
Padding(
padding: EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 0.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children:
[ Expanded(
flex: 1,
child: TextField(
controller: codeController,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
hintText: '请输入验证码',
// helperText: '验证码'
filled: true,
fillColor: AppTheme.loginFillColor)),
),
Container(
margin: EdgeInsets.all(10.0),
child: OutlineButton(
borderSide: BorderSide(color: AppTheme.mainColor),
child: Text(
_verifyCodeTips,
style: TextStyle(color: AppTheme.mainColor),
),
onPressed: _onVeriCode,
),
)
],
),
),
Container(
margin: EdgeInsets.fromLTRB(0, 20, 0, 0),
width: 260,
child: RaisedButton(
color: AppTheme.mainColor,
child: Text(
'登录',
style: AppTheme.loginButtonStyle,
),
onPressed: () {
_onPhoneLogin(context);
},
)),
],
);
}
}
void _onLogin(BuildContext context) {
if (phoneController.text.length == 0) {
ToastUtil.showToast(context, '请输入手机号');
return;
}
if (pwdController.text.length == 0) {
ToastUtil.showToast(context, '请输入密码');
return;
}
_reqLogin();
}
///密码登录
_reqLogin() {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return NetworkLoading();
});
CHttp.post(
CHttp.USER_LOGIN,
(data) {
if (!mounted) {
return;
}
LogUtil.v(data);
Navigator.pop(context);
onLoginSuccess(data);
},
params: PUserLogin(phoneController.text, pwdController.text).toJson(),
errorCallback: (err) {
// Fluttertoast.showToast(msg:err);
Navigator.pop(context);
ToastUtil.showToast(context, err);
},
completeCallback: () {
// Navigator.pop(context);
});
}
_onPhoneLogin(BuildContext context) {
if (phoneController.text.length == 0) {
ToastUtil.showToast(context, '请输入手机号');
return;
}
if (codeController.text.length == 0) {
ToastUtil.showToast(context, '请输入验证码');
return;
}
_reqPhoneLogin();
}
/**
* 手机验证码登录
*/
_reqPhoneLogin() {
// CHttp.post(
// CHttp.USER_PHONELOGIN,
// (data) {
// LogUtil.v(data);
// onLoginSuccess(data);
// },
// params:
// PUserPhoneLogin(country_code,phoneController.text, codeController.text).toJson(),
// errorCallback: (err) {
// ToastUtil.showToast(context, err);
// });
}
void _onVeriCode() {
if (phoneController.text.length == 0) {
ToastUtil.showToast(context, '请输入手机号');
return;
}
if (_verifyCodeCD > 0) {
ToastUtil.showToast(context, _verifyCodeCD.toString() + '秒后可重新发送');
return;
}
_reqVerifyCode();
}
///请求验证码 verifycode
_reqVerifyCode() {
// CHttp.post(
// CHttp.USER_VERIFYCODE,
// (data) {
// ResUserVerifyCode resUserVerifyCode =
// ResUserVerifyCode.fromJson(data);
// setState(() {
// _verifyCodeTips = resUserVerifyCode.time.toString() + '秒后重新发送';
// });
// _verifyCodeCD = resUserVerifyCode.time;
// //倒计时test
// timerCountDown =
// new TimerUtil(mInterval: 1000, mTotalTime: _verifyCodeCD * 1000);
// timerCountDown.setOnTimerTickCallback((int value) {
// double tick = (value / 1000);
//// LogUtil.e("CountDown: " + tick.toInt().toString());
// if (value == 0) {
// setState(() {
// _verifyCodeCD = tick.toInt();
// _verifyCodeTips = '发送验证码';
// });
// } else {
// setState(() {
// _verifyCodeCD = tick.toInt();
// _verifyCodeTips = tick.toInt().toString() + '秒后重新发送';
// });
// }
// });
// timerCountDown.startCountDown();
// },
// params: PUserVerifyCode(country_code,phoneController.text, 2).toJson(),
// errorCallback: (err) {
// ToastUtil.showToast(context, err);
// });
}
_reqPushId(userid, rid) {
// CHttp.post(CHttp.NOTICE_PUSHID, (data) {},
// params: PPush(userid, rid).toJson(), errorCallback: (err) {
// ToastUtil.showToast(context, err);
// });
}
onLoginSuccess(data) {
print('----data:${data}');
String userid = data['userid'];
print('----userid:${userid}');
AppShared.saveUserid(userid);
AppShared.getRid().then((rid) {
if (rid != null && rid.length > 0) {
// JPush jpush = new JPush();
// jpush.resumePush();
_reqPushId(userid, rid);
}
});
Provider.of
(context, listen: false).saveUserId(userid); Provider.of
(context, listen: false).upUser(data); var perfect = data['perfect'] != null && data['perfect'] == 1 ? 1 : 0;
ToastUtil.showToast(context, '登录成功');
print('perfect:${perfect}');
Navigator.of(context).pop(perfect);
if (perfect == 0) {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => ModifyInfoPage()));
} else {
ToastUtil.showToast(context, '登录成功');
}
}
}
服务器登录协议逻辑
router.post('/login', function(req, res, next) {
try {
ru.logReq(req);
var phone = req.body.phone;
var password = req.body.password;
var os = req.body.os;
userDao.queryUser(phone, function(err, users) {
if (err) {
ru.resError(res, err);
} else {
var length = users.length;
if (length > 0) {
var user = users[0];
if (user.password == password) {
if (user.os != os) {
userDao.updateUserOs(user.userid, os, function(err, result) {
})
}
ru.resSuccess(res, user);
} else {
ru.resError(res, '密码错误');
}
} else {
ru.resError(res, '用户未注册');
}
}
})
} catch (err) {
ru.resError(res, err.message);
}
});
登录成功后,[我的]页面会显示个人信息
完善资料功能
客户端页面
import 'dart:io';
import 'package:common_utils/common_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_songbei/custom/common_bottom_sheet.dart';
import 'package:flutter_songbei/custom/network_loading.dart';
import 'package:flutter_songbei/manager/ui_manager.dart';
import 'package:flutter_songbei/network/chttp.dart';
import 'package:flutter_songbei/network/params.dart';
import 'package:flutter_songbei/provider/app.dart';
import 'package:flutter_songbei/provider/user.dart';
import 'package:flutter_songbei/utils/qiniu.dart';
import 'package:flutter_songbei/utils/toast_util.dart';
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';
import 'package:provider/provider.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:sy_flutter_qiniu_storage/sy_flutter_qiniu_storage.dart';
import '../../app_theme.dart';
class ModifyInfoPage extends StatefulWidget {
State
createState() { return _ModifyInfoState();
}
}
class _ModifyInfoState extends State<ModifyInfoPage> {
User user = User();
TextEditingController nameController = TextEditingController();
TextEditingController profileController = TextEditingController();
String token = '';
double _process = 0.0;
String qiniu_key = '';
bool b_head_file = false;
final ImagePicker _picker = ImagePicker();
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
user = Provider.of
(context, listen: false); nameController.text = user.nickname;
profileController.text = user.profile;
_reqQiniuToken();
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'完善资料',
style: TextStyle(color: Colors.white),
),
centerTitle: true,
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
onPressed: () {
Navigator.pop(context);
},
),
actions:
[ FlatButton(
child: Text(
'保存',
style: TextStyle(color: AppTheme.loginFillColor),
),
onPressed: () {
_onUpPerson();
},
)
]),
body: SingleChildScrollView(
child: Container(
color: Colors.white,
padding: EdgeInsets.all(10.0),
child: Column(
children:
[ Column(
children: [
Container(
// padding: EdgeInsets.all(2),
width: 82,
height: 82,
// color: Colors.white,
decoration: new BoxDecoration(
border: new Border.all(color: Colors.black26, width: 1),
// 边色与边宽度
color: Colors.white,
// 底色
// borderRadius: new BorderRadius.circular((20.0)), // 圆角度
// borderRadius: BorderRadius.circular(50), // 也可控件一边圆角大小
),
child: GestureDetector(
child: _buildHead(),
onTap: (){
showPhotos();
},
),
),
Padding(
padding: EdgeInsets.fromLTRB(0, 5, 0, 5),
child: Text('点击更换头像'),
)
],
),
Theme(
data: Theme.of(context).copyWith(
hintColor: Colors.grey[800], //定义下划线颜色
),
child: TextField(
controller: nameController,
decoration: InputDecoration(
hintText: '请输入昵称',
hintStyle: TextStyle(
color: Colors.grey[800],
fontSize: 14,
),
filled: true,
fillColor: Colors.white,
),
onChanged: (str) {
// setState(() {
// user.nickname = str;
// });
},
autofocus: false,
style: AppTheme.f_s14_grey,
),
),
Row(
children:
[ Text(
'性别',
style: AppTheme.f_s14_grey,
),
Center(
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children:
[ Theme(
data: Theme.of(context).copyWith(
unselectedWidgetColor: Colors.grey[800],
),
child: Radio(
value: 1,
groupValue: user.gender,
onChanged: (int rval) {
setState(() {
user.gender = rval;
});
},
activeColor: AppTheme.mainColor,
),
),
Text(
'男',
style: TextStyle(color: Colors.grey[800]),
),
Theme(
data: Theme.of(context).copyWith(
unselectedWidgetColor: Colors.grey[800],
),
child: Radio(
value: 0,
groupValue: user.gender,
onChanged: (int rval) {
setState(() {
user.gender = rval;
});
},
activeColor: AppTheme.mainColor,
)),
Text(
'女',
style: TextStyle(color: Colors.grey[800]),
),
],
),
)
],
),
Row(
children:
[ Text(
'出生日期',
style: AppTheme.f_s14_grey,
),
FlatButton(
child: Text(
user.birth_date != null && user.birth_date.length > 0
? user.birth_date
: '请选择出生日期',
style: AppTheme.f_s14_blue,
),
onPressed: () {
_showDatePicker(context);
},
)
],
),
Theme(
data: Theme.of(context).copyWith(
hintColor: Colors.grey[800], //定义下划线颜色
),
child: TextField(
style: AppTheme.f_s14_grey,
controller: profileController,
decoration: InputDecoration(
hintText: '请输入简介',
hintStyle: TextStyle(
color: Colors.grey[800],
fontSize: 14,
),
filled: true,
fillColor: Colors.white,
),
cursorColor: Colors.white,
onChanged: (str) {
setState(() {
user.profile = str;
});
},
autofocus: false,
maxLines: 3,
),
),
],
),
),
),
);
}
_buildHead() {
print('----_buildHead:${user.head}');
if (user.head != null && user.head.length > 0) {
return Container(
height: 80,
child: b_head_file
? Image.file(File(user.head))
: CachedNetworkImage(imageUrl: UIManager.getHeadurl(user.head)),
);
} else {
return Icon(Icons.add_a_photo);
}
}
_showDatePicker(BuildContext content) async {
var _dateTime = DateTime(1990);
final DateTime _picked = await showDatePicker(
context: content,
initialDate: _dateTime,
firstDate: DateTime(1900),
lastDate: DateTime.now(),
);
if (_picked != null) {
LogUtil.v(_picked);
LogUtil.v(_picked.year);
LogUtil.v(_picked.month);
LogUtil.v(_picked.day);
setState(() {
user.birth_date = _picked.year.toString() +
'-' +
_picked.month.toString() +
'-' +
_picked.day.toString();
});
}
}
void showPhotos() {
showDialog(
barrierDismissible: true, //是否点击空白区域关闭对话框,默认为true,可以关闭
context: context,
builder: (BuildContext context) {
var list = List();
list.add('相册');
list.add('相机');
return CommonBottomSheet(
list: list,
onItemClickListener: (index) async {
print('-----index:$index');
if (index == 0 || index == 2) {
var source = ImageSource.gallery;
if (index == 2) source = ImageSource.camera;
_onUpload(source);
}
Navigator.pop(context);
},
);
});
}
_onUpload(ImageSource source) async {
print('---_onUpload:${token}');
PickedFile imageFile = await _picker.getImage(source: source);
File file = await ImageCropper.cropImage(
sourcePath: imageFile.path,
aspectRatio: CropAspectRatio(ratioX: 1, ratioY: 1),
aspectRatioPresets: [
CropAspectRatioPreset.square,
// CropAspectRatioPreset.ratio3x2,
// CropAspectRatioPreset.original,
// CropAspectRatioPreset.ratio4x3,
// CropAspectRatioPreset.ratio16x9
],
androidUiSettings: AndroidUiSettings(
toolbarTitle: '裁剪',
toolbarColor: Colors.deepOrange,
toolbarWidgetColor: Colors.white,
initAspectRatio: CropAspectRatioPreset.square,
lockAspectRatio: true),
iosUiSettings: IOSUiSettings(
minimumAspectRatio: 1.0,
));
setState(() {
user.head = file.path;
b_head_file = true;
});
if (file == null) {
ToastUtil.showToast(context, '选择图片失败');
return;
}
if (token == null || (token != null && token.length == 0)) {
ToastUtil.showToast(context, 'token 无效!');
return;
}
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return NetworkLoading();
});
final syStorage = new SyFlutterQiniuStorage();
//监听上传进度
syStorage.onChanged().listen((dynamic percent) {
double p = percent;
setState(() {
_process = p;
});
// print(percent);
LogUtil.e('---percent:${percent}');
if (p >= 1.0) {
// LogUtil.e('---qiuniu_link:${qiniu_link}');
setState(() {
user.head = Qiniu.getUpUrl(qiniu_key);
b_head_file = false;
});
Navigator.pop(context);
}
});
String key = Qiniu.getUpPath(file.path);
// LogUtil.e('---key:${key}');
setState(() {
qiniu_key = key;
});
//上传文件
UploadResult result = await syStorage.upload(file.path, token, key);
}
_onUpPerson() {
if(nameController.text.length == 0){
ToastUtil.showToast(context, '请输入昵称');
return;
}
if(b_head_file == true){
ToastUtil.showToast(context, '头像上传中...');
return;
}
user.nickname = nameController.text;
user.profile = profileController.text;
if (user.gender != 0 && user.gender != 1) {
ToastUtil.showToast(context, '请选择性别');
return;
}
_reqUpUserInfo();
}
_reqUpUserInfo() {
user.userid = Provider.of
(context, listen: false).userid; CHttp.post(
CHttp.USER_UPINFO,
(data) {
LogUtil.v(data);
Provider.of
(context, listen: false).upInfo(data); ToastUtil.showToast(context, '修改成功');
Navigator.of(context).pop();
},
params: PUpUser(
Provider.of
(context, listen: false).userid, user.head,
nameController.text,
profileController.text,
user.gender,
user.birth_date)
.toJson(),
errorCallback: (err) {
ToastUtil.showToast(context, err);
});
}
_reqQiniuToken() {
CHttp.post(CHttp.QINIU_UPTOKEN, (data) {
LogUtil.e('-----data:${data}');
setState(() {
token = data;
});
}, errorCallback: (err) {
LogUtil.e('-----err:${err}');
});
}
}
服务器逻辑
router.post('/upinfo', function(req, res, next) {
try {
ru.logReq(req);
var body = req.body;
var userid = body.userid;
var nickname = body.nickname;
var head = body.head;
var gender = body.gender;
var birth_date = body.birth_date;
var profile = body.profile;
if (!userid) {
ru.resError(res, '参数错误')
return;
}
userDao.upUser(userid, nickname, head, gender, birth_date, profile, function(err, result) {
if (err) {
ru.resError(res, err);
return;
}
userDao.queryUserInfo(userid, function(err, result) {
if (err) {
ru.resError(res, err);
return;
}
console.log(result)
var user = {};
if (result.length > 0) {
user = result[0][0];
user.myfollow = result[1][0].count;
user.followme = result[2][0].count;
}
ru.resSuccess(res, user);
})
})
} catch (err) {
ru.resError(res, err.message);
}
});
至此,账号系统基本完成,代码部分并没有完整展示,因为会不断修改完善。这个等项目写的七七八八的时候,我会放到github上,方面查阅。其中很多细节没法一篇介绍完,后边逐一介绍。
评论