Flutter開始干係列-一個完整的登入實踐

Joker_Fu 發表於 2019-10-14

直接開始幹,沒有為什麼~


基於上一篇實現一個登陸頁,使用 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…