App开发之账号系统实战篇
数据库操作
创建数据库[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 {@overridecreateState() {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;@overridevoid dispose() {super.dispose();if (timerCountDown != null) timerCountDown.cancel();}@overrideWidget 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 flagshowCountryOnly: false,optional. Shows only country name and flag when popup is closed.showOnlyCountryWhenClosed: false,optional. aligns the flag and the Text leftalignLeft: 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() + '秒后重新发送';});//倒计时testtimerCountDown =: 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 hereCountry 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 {StatecreateState() { 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 hereprint("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 flagshowCountryOnly: false,// optional. Shows only country name and flag when popup is closed.showOnlyCountryWhenClosed: false,// optional. aligns the flag and the Text leftalignLeft: 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 {StatecreateState() { 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上,方面查阅。其中很多细节没法一篇介绍完,后边逐一介绍。
评论
