flutterFlutter

flutter登陆机制

2019-02-02  本文已影响0人  MakerChin

登录流程

登陆机制的流程其实很简单,简单来说就是:

  1. 通过手机号获取验证码
  2. 输入验证码,根据手机号和验证码去登陆
  3. 登陆的时候设置用户的登录状态,下次打开以后免登录

1.验证码获取

TextField(
  controller: _codeController
)

使用文本框小部件中的controller的回调函数获取文本框中的内容

getCodeBuilder() {
    _presenter.getCode(_phoneNumberController.text);
  }

调用LoginPresent类中的getCode方法根据手机号去获取验证码,如果返回的是ture,表示获取验证码成功,如果返回的是null,表示获取验证码失败。
这里的Boast.show()Boast类里面的一个静态方法,方法接收一个必选参数(显示信息)和一个可选参数(延迟时间,默认是一秒)。

getCode(String mobile) async {
    try {
      List result = await Future.wait([
        _securityCodeDatasourceApi.getSecurityCodeByMobile(mobile.trim()),
      ]);
      bool isSuccess = result[0];
      if (isSuccess == null) {
        Boast.show('验证码发送失败');
        return;
      }
      Boast.show('验证码已发送');
    } catch (e) {
      Boast.show('验证码发送失败');
    }
  }

下面这段代码就是调用API类的getSecurityCodeByMobile方法,调用接口,通过手机号去获取验证码。

 _securityCodeDatasourceApi.getSecurityCodeByMobile(mobile.trim())

getSecurityCodeByMobile方法的具体内容如下:
通过NetworkUtil类的get方法调用接口,获得数据并返回;
then里面的(dynamic res)res就是get方法得到的结果数据。

Future<bool> getSecurityCodeByMobile(String mobile) {
    return _netUtil
        .get('member', '/api/xxx/xxx?mobile=$mobile')
        .then((dynamic res) {
      if (res != null) {
        return res['isSuccess'];
      }
      return null;
    });
  }

解释一下上面的get方法

Future<dynamic> get(String b, String url) {
    if (b != null) {
      //获取biz和url将url进行拼接,biz_domains是一个配置文件
      url = b_domains[b] + url;
    }
    //getHeaders()方法完善请求头信息,然后
    return getHeaders().then((headers) {
      return http.get(url, headers: headers).then((response) {
        //响应体内容
        final String res = response.body;
        //响应的状态
        final int statusCode = response.statusCode;
        //根据状态判断该抛出什么异常
        if (statusCode < 200 || statusCode > 400 || json == null) {
          throw new Exception("Error while fetching data");
        }
        //返回一个Map<String, dynamic>类型的数据
        return json.decode(res);
      });
    });
  }

上面代码中用到的getHeaders()方法

Future<Map<String, String>> getHeaders() {
    //AuthStateProvider类其实就是一恶搞用户登录状态监听的类,
    return (new AuthStateProvider()).getLoginInfo().then((loginInfo) {
      //定义响应头的Content-Type类型是json类型
      Map<String, String> headers = {'Content-Type': 'application/json'};
      //如果登陆信息不为空,响应头部添加token信息
      if (loginInfo != null) {
        headers['Authorization'] = 'Bearer ${loginInfo.token}';
      }
      return headers;
    });
  }

2.手机、验证码验证登陆

用户输入手机号和收到的验证码,点击登陆,触发点击事件,点击事件通过LoginPresent类中的loginByMobile方法去验证登陆

//点击事件的回调函数
void _submit() {
    _presenter.loginByMobile(_phoneNumberController.text, _codeController.text);
  }
//点击按钮
RaisedButton(
  onPressed: _submit,
);

loginByMobile函数解析:

loginByMobile(String mobile, String code) async {
    AuthStateProvider provider = new AuthStateProvider();
    try {
      //通过API获取用户登录信息
      LoginInfo loginInfo = await _securityCodeDatasourceApi.loginByMobile(
          mobile.trim(), code.trim());
      //如果token为空,登陆失败
      if (loginInfo.token.isEmpty) {
        BMToast.show('网络连接失败');
        return;
      }
      //登陆成功以后设置登陆信息
      await provider.setLoginInfo(loginInfo);
      //调用api获取用户信息
      User user = await api.getUserInfo();
      //设置用户信息
      await provider.setUserInfo(user);
    } catch (e) {
      BMToast.show('网络连接失败');
    }
  }

登陆的流程其实和验证码获取的流程差不多的,多出来的一部分就是如果用户登陆成功,那么就要记住用户已经登陆过的这个状态,下次打开app的时候就不用再去登陆了。
要做到这些,就需要刚才的两步:

//登陆成功以后设置登陆信息
await provider.setLoginInfo(loginInfo);
//调用api获取用户信息
User user = await api.getUserInfo();
 //设置用户信息
await provider.setUserInfo(user);

这里的setLoginInfo方法是AuthStateProvider类里的一个方法,用来设置登陆信息,这里的PRES_LOGIN_INFO_KEY其实就是token的前缀,loginInfo.toJson()这里面其实就只有token,最后 this.notify(AuthState.LOGGED_IN);将用户的登录状态变成了“登陆状态”。

void setLoginInfo(LoginInfo loginInfo) async {
    this.loginInfo = loginInfo;
    await StorageUtil.set(PRES_LOGIN_INFO_KEY, loginInfo.toJson());
    this.notify(AuthState.LOGGED_IN);
  }

StorageUtil.set()方法则是存储用户登陆信息的一个类:
这个方法主要是根据data的类型类分别决定要用prefs里的哪个方法,而这个SharedPreferences类暂时没有看懂

static set(String key, data) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    if (data is int) {
      prefs.setInt(key, data);
    } else if (data is double) {
      prefs.setDouble(key, data);
    } else if (data is String) {
      prefs.setString(key, data);
    } else if (data is List<String>) {
      prefs.setStringList(key, data);
    } else if (data is bool) {
      prefs.setBool(key, data);
    } else if (data is Map) {
      prefs.setString(key, json.encode(data));
    } else {
      throw new Exception('cannt found data type');
    }
  }

最后要说的是

await provider.setUserInfo(user);

首先看provider.setUserInfo()方法

void setUserInfo(User userInfo) async {
    this.userInfo = userInfo;
    await StorageUtil.set(PRES_USER_INFO_KEY, userInfo.toJson());
  }

这个方法做的事情有

  1. 为本类中的userInfo赋值
  2. 存储用户信息,以key:value的形式。
    这里的StorageUtil虽然看不懂,但是可以根据await判断,这个数据应该是存储到了远程。同样登陆信息也是存储在了远程。

至此,登陆流程因该是走通了。

上一篇下一篇

猜你喜欢

热点阅读