<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          App開發(fā)之賬號系統(tǒng)實(shí)戰(zhàn)篇

          共 54278字,需瀏覽 109分鐘

           ·

          2020-11-20 06:28

          數(shù)據(jù)庫操作

          1. 創(chuàng)建數(shù)據(jù)庫[songbei]


          2. 創(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 { @override State createState() { 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;
          @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: '請輸入手機(jī)號',// filled: true,// fillColor: AppTheme.textWhite)), 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: '請輸入手機(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, 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) { setState(() { 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: () { pwdController.text = ''; setState(() { 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) { setState(() { 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: () { codeController.text = ''; setState(() { 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) { setState(() { _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) { ToastUtil.showToast(context, '請輸入手機(jī)號'); return; } if (pwdController.text.length == 0) { ToastUtil.showToast(context, '請輸入密碼'); return; } if (codeController.text.length == 0) { ToastUtil.showToast(context, '請輸入驗(yàn)證碼'); return; } if (!_checkboxSelected) { ToastUtil.showToast(context, '請閱讀并同意用戶服務(wù)協(xié)議'); return; } _reqSingnup(); }
          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_VALIDATE, (data) { if (!mounted) { return; } _verifyCodeCD = data['time']; setState(() { _verifyCodeTips = _verifyCodeCD.toString() + '秒后重新發(fā)送'; }); //倒計(jì)時(shí)test timerCountDown = 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, 1).toJson(), errorCallback: (err) { ToastUtil.showToast(context, err); }); }
          void _onCountryChange(CountryCode countryCode) { //TODO : manipulate the selected country code here print("New Country selected: " + countryCode.toString()); country_code = countryCode.toString(); }
          /** * 注冊用戶 */ _reqSingnup() { CHttp.post( CHttp.USER_REGISTER, (data) { if (!mounted) { return; } LogUtil.e(data); String userid = data['userid']; Navigator.of(context).pop(userid); ToastUtil.showToast(context, '請前往登錄頁進(jìn)行登錄'); }, params: PUserSignup(country_code, phoneController.text, pwdController.text, codeController.text) .toJson(), errorCallback: (err) { ToastUtil.showToast(context, 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 { @override State createState() { 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;
          @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: [// 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 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: '手機(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 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: '請輸入手機(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 { @override 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();
          @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { user = Provider.of(context, listen: false); nameController.text = user.nickname; profileController.text = user.profile; _reqQiniuToken(); }); }
          @override 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é)沒法一篇介紹完,后邊逐一介紹。

          瀏覽 55
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  啪啪啪免费在线观看 | 永久免费 看片直接下载 | 成人午夜激情片 | 久99久在线观看视频 | 肏屄在线观看 |