Flutter开始干系列-一个完整的登录实践

7,814 阅读3分钟

直接开始干,没有为什么~


基于上一篇实现一个登陆页,使用 connectivityshared_preferencesDio 实现一个完整的登录操作,包含网络连接检测、请求、数据存储等~

当然,登录接口并不是我实现,是由玩Android友情提供。首先看效果:

效果图

插件简介

connectivity

用于发现网络连接并进行相应配置。它可以区分蜂窝连接和WiFi连接,iOS和Android适用。

注意 在Android上,这不保证可以连接到Internet

shared_preferences

包装NSUserDefaults(在iOS上)和SharedPreferences(在Android上),为简单数据提供持久存储。

Dio

dio是一个强大的Dart Http请求库,支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时、自定义适配器等。

依赖配置

在 pubspec.yaml 中添加插件依赖,下面插件版本是文章编写时的最新版。

  dependencies:
  # https://github.com/flutterchina/dio
  dio: ^3.0.3
  
  # https://github.com/flutter/plugins/tree/master/packages/connectivity
  connectivity: ^0.4.4
  
  # https://github.com/flutter/plugins/tree/master/packages/shared_preferences
  shared_preferences: ^0.5.3+4

登录操作

今天的所有操作都基于上一篇实现一个登陆页,有必要的可以先跳转过去查看。

使用 Dio 构建登录请求

登录按钮的 onPressed 回调中,替换为 _doLogin() ,实现如下:

  Future _doLogin() async {
    Dio dio = Dio();

    dio.options..baseUrl = 'https://www.wanandroid.com/';

    // 添加拦截器
    dio.interceptors
      ..add(LogInterceptor(
        requestHeader: true,
        requestBody: true,
        responseHeader: true,
        responseBody: true,
      ));

    // 发起请求
    Response response = await dio.post('user/login',
        data: FormData.fromMap({
          "username": _accountController.text.trim(),
          "password": _pwdController.text.trim(),
        }));

    if (response.statusCode == 200) {
      UserEntity user = UserEntity.fromJson(response.data);
      if (user.errorCode == 0) {
        _showInfoDialog('登录成功');
      } else {
        _showInfoDialog('登录失败:${user.errorMsg}');
      }
    } else {
      _showInfoDialog('网络请求异常:${response.statusMessage}');
    }
  }

点击登录,账号密码正确的情况下,你将看到登录成功!

登录成功效果图

发起请求前检测网络

在发起网络请求前,我们一般会检测网络,有网络就发起,没网络就算逑... 需要注意这个库在 Android 上还有个注意事项。

所在在 post 发起之前加上如下代码,校验网络连接检测:

    // 检测网络连接
    var connectivityResult = await (Connectivity().checkConnectivity());
    if (connectivityResult == ConnectivityResult.none) {
      _showInfoDialog('网络连接异常');
      return;
    }

存储登录结果

一般登录成功后,我们会保存用户信息,以便在后续操作使用到时方便获取。

所以在确认真正登录成功后,添加如下代码保存用户信息:

    //登录成功后 保存信息
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setString('user', jsonEncode(user.data));

connectivity 、shared_preferences 插件使用相对简单,就不再废话了。到这一步如果网络连接正常,且账号密码正确,将看到上面第一张图示效果。否则:

网络连接异常

拦截器添加请求头

登录成功后,接下来需要在所有的请求中加上 token ,当然可能还有一些硬件信息什么的。这里通过添加 Dio 拦截器实现:

    // 添加拦截器
    dio.interceptors
      ..add(InterceptorsWrapper(
        onRequest: (RequestOptions options) async {
          var prefs = await SharedPreferences.getInstance();
          var userJson = prefs.getString('user');
          if (userJson != null && userJson.isNotEmpty) {
            UserData user = UserData.fromJson(jsonDecode(userJson));
            options.headers
              ..addAll({
                'userId': user.id ?? '',
                'token': user.token ?? '',
              });
          }
          return options;
        },
      ))

这样网络请求就会被拦截,添加 userId 和 token ...

请求提示

一般进行网络请求或者耗时操作时,会给用户一个友好的提示,表示我们没有卡死。我这里的处理是封装了一个 LoadingDialog 的 Widget,使用 showDialog() 弹出,效果如下,具体实现请看代码。

加载效果

结尾

这个登录实践比较简单,没有对请求做统一的封装,也没有对异常进行处理。一般都是封装后统一处理,调用的地方简单很多。

最终 _doLogin 方法中的全部代码,

  Future _doLogin() async {
    Dio dio = Dio();

    dio.options..baseUrl = 'https://www.wanandroid.com/';

    // 添加拦截器
    dio.interceptors
      ..add(InterceptorsWrapper(
        onRequest: (RequestOptions options) async {
          var prefs = await SharedPreferences.getInstance();
          var userJson = prefs.getString('user');
          if (userJson != null && userJson.isNotEmpty) {
            UserData user = UserData.fromJson(jsonDecode(userJson));
            options.headers
              ..addAll({
                'userId': user.id ?? '',
                'token': user.token ?? '',
              });
          }
          return options;
        },
      ))
      ..add(LogInterceptor(
        requestHeader: true,
        requestBody: true,
        responseHeader: true,
        responseBody: true,
      ));

    // 检测网络连接
    var connectivityResult = await (Connectivity().checkConnectivity());
    if (connectivityResult == ConnectivityResult.none) {
      _showInfoDialog('网络连接异常');
      return;
    }

    LoadingDialog.show(context);

    // 发起请求
    Response response = await dio.post('user/login',
        data: FormData.fromMap({
          "username": _accountController.text.trim(),
          "password": _pwdController.text.trim(),
        }));

    if (response.statusCode == 200) {
      UserEntity user = UserEntity.fromJson(response.data);
      if (user.errorCode == 0) {
        //登录成功后 保存信息
        SharedPreferences prefs = await SharedPreferences.getInstance();
        prefs.setString('user', jsonEncode(user.data));
        _showInfoDialog('登录成功');
      } else {
        _showInfoDialog('登录失败:${user.errorMsg}');
      }
    } else {
      _showInfoDialog('网络请求异常:${response.statusMessage}');
    }
    LoadingDialog.hide(context);
  }

最后附上Github地址github.com/joker-fu/fl…