App開發(fā)之賬號系統(tǒng)實(shí)戰(zhàn)篇
數(shù)據(jù)庫操作
創(chuàng)建數(shù)據(jù)庫[songbei]

創(chuàng)建用戶表[user]


3.驗(yàn)證碼表[validate]

注冊功能

注冊用到兩個(gè)協(xié)議:注冊和發(fā)送驗(yàn)證碼
客戶端注冊頁
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 = '發(fā)送驗(yàn)證碼';bool _checkboxSelected = false;bool b_phone_clear = false;bool b_pwd_clear = false;bool b_code_clear = false;TimerUtil timerCountDown;///倒計(jì)時(shí)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: '請輸入手機(jī)號',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: '請輸入手機(jī)號',filled: true,fillColor: AppTheme.loginFillColor),onChanged: (String text) {通過onChanged事件更新_isComposing 標(biāo)志位的值{調(diào)用setState函數(shù)重新渲染受到_isComposing變量影響的IconButton控件b_phone_clear = text.length >//new 如果文本輸入框中的字符串長度大于0則允許發(fā)送消息//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: '請輸入驗(yàn)證碼',helperText: '驗(yàn)證碼'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, //選中時(shí)的顏色onChanged: (value) {{_checkboxSelected = value;});},),Text('已閱讀并同意'),GestureDetector(child: Text('《用戶服務(wù)協(xié)議》',style: TextStyle(color: AppTheme.mainColor),),onTap: () {Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => AppWebPage('用戶服務(wù)協(xié)議',CHttp.getAppWeb(CHttp.APPH5_AGREEMENT))));},)],),)],)],),)),);}void _onSignup(BuildContext context) {if (phoneController.text.length == 0) {'請輸入手機(jī)號');return;}if (pwdController.text.length == 0) {'請輸入密碼');return;}if (codeController.text.length == 0) {'請輸入驗(yàn)證碼');return;}if (!_checkboxSelected) {'請閱讀并同意用戶服務(wù)協(xié)議');return;}_reqSingnup();}void _onVeriCode() {if (phoneController.text.length == 0) {'請輸入手機(jī)號');return;}if (_verifyCodeCD > 0) {_verifyCodeCD.toString() + '秒后可重新發(fā)送');return;}_reqVerifyCode();}verifycode{CHttp.post(CHttp.USER_VALIDATE,{if (!mounted) {return;}_verifyCodeCD = data['time'];{_verifyCodeTips = _verifyCodeCD.toString() + '秒后重新發(fā)送';});//倒計(jì)時(shí)testtimerCountDown =: 1000, mTotalTime: _verifyCodeCD * 1000);value) {double tick = (value / 1000);LogUtil.e("CountDown: " + tick.toInt().toString());if (value == 0) {{_verifyCodeCD = tick.toInt();_verifyCodeTips = '發(fā)送驗(yàn)證碼';});else {{_verifyCodeCD = tick.toInt();_verifyCodeTips = tick.toInt().toString() + '秒后重新發(fā)送';});}});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);'請前往登錄頁進(jìn)行登錄');},params: PUserSignup(country_code, phoneController.text,codeController.text).toJson(),errorCallback: (err) {err);});}}
服務(wù)器注冊協(xié)議
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, '參數(shù)錯(cuò)誤!');} else if (!utils.checkPhone(phone)) {ru.resError(res, '請?zhí)顚懻_的手機(jī)號格式');} else {userDao.queryUser(phone, function(err, result) {if (err) {ru.resError(res, err)} else {if (result.length > 0) {ru.resError(res, '用戶已經(jīng)注冊')} 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, '驗(yàn)證碼錯(cuò)誤');}} else {ru.resError(res, '驗(yàn)證碼錯(cuò)誤');}}});} 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);}});
數(shù)據(jù)庫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 驗(yàn)證碼登錄*/int login_type = 0;String _verifyCodeTips = '發(fā)送驗(yàn)證碼';TimerUtil timerCountDown;///倒計(jì)時(shí)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(// '驗(yàn)證碼登錄',// 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: '手機(jī)號',filled: true,fillColor: AppTheme.loginFillColor),onChanged: (String text) {//new 通過onChanged事件更新_isComposing 標(biāo)志位的值setState(() {//new 調(diào)用setState函數(shù)重新渲染受到_isComposing變量影響的IconButton控件b_phone_clear =text.length > 0; //new 如果文本輸入框中的字符串長度大于0則允許發(fā)送消息}); //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: '請輸入手機(jī)號',filled: true,fillColor: AppTheme.loginFillColor),onChanged: (String text) {//new 通過onChanged事件更新_isComposing 標(biāo)志位的值setState(() {//new 調(diào)用setState函數(shù)重新渲染受到_isComposing變量影響的IconButton控件b_phone_clear =text.length > 0; //new 如果文本輸入框中的字符串長度大于0則允許發(fā)送消息}); //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: '請輸入驗(yàn)證碼',// helperText: '驗(yàn)證碼'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, '請輸入手機(jī)號');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, '請輸入手機(jī)號');return;}if (codeController.text.length == 0) {ToastUtil.showToast(context, '請輸入驗(yàn)證碼');return;}_reqPhoneLogin();}/*** 手機(jī)驗(yàn)證碼登錄*/_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, '請輸入手機(jī)號');return;}if (_verifyCodeCD > 0) {ToastUtil.showToast(context, _verifyCodeCD.toString() + '秒后可重新發(fā)送');return;}_reqVerifyCode();}///請求驗(yàn)證碼 verifycode_reqVerifyCode() {// CHttp.post(// CHttp.USER_VERIFYCODE,// (data) {// ResUserVerifyCode resUserVerifyCode =// ResUserVerifyCode.fromJson(data);// setState(() {// _verifyCodeTips = resUserVerifyCode.time.toString() + '秒后重新發(fā)送';// });// _verifyCodeCD = resUserVerifyCode.time;// //倒計(jì)時(shí)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 = '發(fā)送驗(yàn)證碼';// });// } else {// setState(() {// _verifyCodeCD = tick.toInt();// _verifyCodeTips = tick.toInt().toString() + '秒后重新發(fā)送';// });// }// });// 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, '登錄成功');}}}
服務(wù)器登錄協(xié)議邏輯
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, '密碼錯(cuò)誤');}} else {ru.resError(res, '用戶未注冊');}}})} catch (err) {ru.resError(res, err.message);}});
登錄成功后,[我的]頁面會顯示個(gè)人信息

完善資料功能

客戶端頁面
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('點(diǎn)擊更換頭像'),)],),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, //是否點(diǎn)擊空白區(qū)域關(guān)閉對話框,默認(rèn)為true,可以關(guān)閉context: context,builder: (BuildContext context) {var list = List();list.add('相冊');list.add('相機(jī)');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();//監(jiān)聽上傳進(jìn)度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}');});}}
服務(wù)器邏輯
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, '參數(shù)錯(cuò)誤')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);}});
至此,賬號系統(tǒng)基本完成,代碼部分并沒有完整展示,因?yàn)闀粩嘈薷耐晟啤_@個(gè)等項(xiàng)目寫的七七八八的時(shí)候,我會放到github上,方面查閱。其中很多細(xì)節(jié)沒法一篇介紹完,后邊逐一介紹。
評論
圖片
表情
