Flutter☞网络封装

第一章 学习指南

1、AS 快捷键

  快捷创建Widget: 在dart文件中输入stf或stl出现提示后按回车即可
  快捷修复: option + 回车
  自动生成构造函数: 选中 final 参数, 快捷键: option + 回车
  添加父组件、变为子组件、删除子组件: option + 回车
  万能的搜索: 双击shift
  查看最近打开的文件: command + E
  重命名: fn + shift + f6
  查看当前类结构: command + fn + f12
  查看源码: 将光标放到要查看源码的类名或方法名上, 长按command然后的点击
  查看类的子类: 选中要查看的类, 然后: command + B 或 option + command + B
  将代码更新到模拟器上: 选中模拟器然后 command + R
  导入类的快捷键: 将光标放在要导入类的上面, 然后按 option + enter
  前进后退: 当跟踪代码的时候, 经常跳转到其他类, 后退快捷键: option + command+ 方向左键, 前进快捷键: option + command+ 方向右键
  全局搜索: command + shift + F
  全局替换: command + shift + R
  查找引用: option + shift + F7
Cmd + Option + L : 代码格式化。
Cmd + “-” 或者 “+” :收起和展开代码。
Cmd + Shift + “-” 或者 “+” : 收起和展开全部代码。
Cmd + Shift + Enter :代码快速补全。
Cmd + F12 :快速查看当前文件所有方法。
Cmd + Shift + F :全局搜索。
Cmd + Shift + R :全局替换。
Cmd + F :当前文件搜索。
Cmd + R :当前文件替换。
Cmd + Option + M :方法抽离或重构。
Cmd + Option + V : 抽离局部变量。
Cmd + Option + F :抽离成员变量。
Cmd + O :全局搜索类/文件/关键字/(包括系统类,自定义类)。
Cmd + Option + W :抽取代码为单独的方法,或者抽取成get方法。
Cmd + Option + E :创建文件,抽取代码为单独的方法,或者抽取成get方法。
Cmd + Option + B :查看抽象类的实现。
Cmd + D :复制单行。
Cmd+ Delete :删除行。
Cmd + \ :热重载(hot reload) 。
Cmd + Option + \ :热重启(hot restart)。
Cmd+ Shift + Enter :if后面自动加(){ }。
Cmd+ J :快速生成模版代码块。
Ctl+ R :运行项目。
Option + Enter :在widget包裹一个新的widget。
Option + Enter :自动修正错误(但是要把光标移动到错误上面)。
Option + Enter :将StatelessWidget转 StatefulWidget。
Option + Up :选择这个widget。
Option + Ctl + O :清除无效包引用。
Option + Ctl + I :自动缩进对齐/代码对齐。
Option + Shift + Up/Down :上下移动代码。
Option + 双击 Up :选择区域。
选中代码 + tab :选中代码缩进。
选中代码 + shift + tab :选中代码缩进。

2、自动补全

可以在Android Studio的Plugin中搜素 Flutter Snippets这个插件然后进行安装

3、代码提取

选中代码-->右键选择"Refactor"-->Extract Method...(提取成方法)

4、代码自动格式化

虽然我们可以通过快捷键option(alt)+command(ctrl)+L来在提交代码时格式化, 但是这种手动方式显然还不够效率. 下面我们来借助AS的保存时代码自动格式化功能来释放双手: 在Settings-->Language&Frameworks-->Flutter中选上"Format Code on Save"这个选项. (保存代码时自动格式化)

第二章 Flutter网络和数据存储框架搭建

1、 Flutter网络架构架构设计

支持网络库插拔设计, 且不干扰业务层
简洁易用, 支持配置来进行请求
Adapter设计, 扩展性强
统一异常和返回处理
  
https://github.com/szy0syz/flutter_bilibili
HitNet
Dao层 首页、详情、点赞、收藏、登录、注册、通知、个人中心、排行...
REST ful支持 GET、POST、DELETE
统一逻辑处理 Request Processing、Response Processing、Error Processing
适配层 Dio Adapter、Http Adapter、Mock Adapter
三方库 dio、http、Mock

2、基于配置的请求封装与hinet架构搭建

在lib目录下, 新建http目录; 在http目录下分别创建core和request目录; 在request目录下新建"base_request.dart"文件, 在base_request.dart中:

enum HttpMethod { GET, POST, DELETE }

///抽象类封装--基础请求
abstract class BaseRequest {
  // curl -X GET "http://api.devio.org/uapi/test/test?requestPrams=11" -H "accept: */*"
  // curl -X GET "https://api.devio.org/uapi/test/test/1
  var pathParams;
  var useHttps = true; // 用于切换http和https

  String authority() {//设置域名
    return "api.devio.org";
  }

  HttpMethod httpMethod();

  String path();

  String url() {
    Uri uri;
    var pathStr = path();
    //拼接path参数
    if (pathParams != null) {
      if (path().endsWith("/")) {
        pathStr = "${path()}$pathParams";
      } else {
        pathStr = "${path()}/$pathParams";
      }
    }
    //http和https切换
    if (useHttps) {
      uri = Uri.https(authority(), pathStr, params);
    } else {
      uri = Uri.http(authority(), pathStr, params);
    }
    if (needLogin()) {
      //给需要登录的接口携带登录令牌
      addHeader(LoginDao.BOARDING_PASS, LoginDao.getBoardingPass());
    }
    print('url:${uri.toString()}');
    return uri.toString();
  }

  bool needLogin();// 用于判断是否需要登录

  Map params = Map();

  ///添加参数
  BaseRequest add(String k, Object v) {
    params[k] = v.toString();
    return this;
  }

  Map header = {
    HiConstants.authTokenK: HiConstants.authTokenV,
      HiConstants.courseFlagK: HiConstants.courseFlagV,
    // 'course-flag': 'fa',
    // "auth-token": "ZmEtMjAyMS0wNC0xMiAyMToyMjoyMC1mYQ==fa",
  };

  ///添加header
  BaseRequest addHeader(String k, Object v) {
    header[k] = v.toString();
    return this;
  }
}

3、基于配置的请求封装与hinet架构搭建-2

在http目录-->request目录--> 新建 test_request.dart 文件, 用于测试接口:

import 'package:flutter_bilibili/http/request/base_request.dart';

//测试排程类
class TestRequest extends BaseRequest {
  @override
  HttpMethod httpMethod() {
    return HttpMethod.GET;
  }

  @override
  bool needLogin() {
    return false;
  }

  @override
  String path() {
    return 'uapi/test/test';
  }
}

在http目录-->core目录-->新建 hi_net.dart 文件:

import 'package:flutter_bilibili/http/core/dio_adapter.dart';

import '../request/base_request.dart';
import 'hi_error.dart';
import 'hi_net_adapter.dart';

///1.支持网络库插拔设计,且不干扰业务层
///2.基于配置请求请求,简洁易用
///3.Adapter设计,扩展性强
///4.统一异常和返回处理
class HiNet {
  HiNet._();
  static HiNet? _instance;//创建单例

  static HiNet getInstance() {//静态方法获取单例
    if (_instance == null) {
      _instance = HiNet._();
    }
    return _instance!;
  }

  // 获取请求结果
  Future fire(BaseRequest request) async {
    HiNetResponse? response;
    var error;

    try {
      response = await send(request);
    } on HiNetError catch (e) {
      error = e;
      response = e.data;
      printLog(e.message);
    } catch (e) {
      //其它异常
      error = e;
      printLog(e);
    }

    if (response == null) {
      printLog(error);
    }

    var result = response?.data;
    printLog(result);

    var status = response?.statusCode;
    switch (status) {
      case 200:
        return result;
      case 401:
        throw NeedLogin();
      case 403:
        throw NeedAuth(result.toString(), data: result);
      default:
        throw HiNetError(status ?? -1, result.toString(), data: result);
    }
  }

  //发送请求
  Future> send(BaseRequest request) async {
    ///使用Dio发送请求
    HiNetAdapter adapter = DioAdapter();
    return adapter.send(request);
  }

  void printLog(log) {//用于测试打印
    print('hi_net:' + log.toString());
  }
}

在随便一个文件内, 构建测试请求:

void _incrementCounter() async {
  TestRequest request = TestRequest();
  request.add("aa", "ddd").add("bb", "333");
  var result = await HiNet.getInstance().fire(request);
  print(result);
}

4、 hinet统一异常和返回处理与Adapter模式设计

在http-->core-->新建 hi_error.dart, 用来统一处理异常:

///需要登录的异常
class NeedLogin extends HiNetError {
  NeedLogin({int code: 401, String message: '请先登录'}) : super(code, message);
}

///需要授权的异常
class NeedAuth extends HiNetError {
  NeedAuth(String message, {int code: 403, dynamic data})
      : super(code, message, data: data);
}

///网络异常统一格式类
class HiNetError implements Exception {
  final int code;
  final String message;
  final dynamic data;

  HiNetError(this.code, this.message, {this.data});
}

在http-->core-->新建 hi_net_adapter.dart, 用来统一处理网络层返回格式:

import 'dart:convert';

import 'package:flutter_bilibili/http/request/base_request.dart';

///网络请求抽象类
abstract class HiNetAdapter {
  Future> send(BaseRequest request);
}

/// 统一网络层返回格式
class HiNetResponse {
  // 构造方法: 非必填
  HiNetResponse({
    this.data,
    required this.request,
    this.statusCode,
    this.statusMessage,
    this.extra,
  });

  /// Response body. may have been transformed, please refer to [ResponseType].
  T? data;

  /// The corresponding request info.
  BaseRequest request;

  /// Http status code.
  int? statusCode;

  /// Returns the reason phrase associated with the status code.
  /// The reason phrase must be set before the body is written
  /// to. Setting the reason phrase after writing to the body.
  String? statusMessage;

  /// Custom field that you can retrieve it later in `then`.
  late dynamic extra;

  /// We are more concerned about `data` field.
  @override
  String toString() {
    if (data is Map) {
      return json.encode(data);
    }
    return data.toString();
  }
}

在http-->core-->新建 mock_adapter.dart, 用来新建测试适配器:

import 'package:flutter_bilibili/http/core/hi_net_adapter.dart';
import 'package:flutter_bilibili/http/request/base_request.dart';

///测试适配器,mock数据
class MockAdapter extends HiNetAdapter {
  @override
  Future> send(BaseRequest request) async {
    return Future.delayed(Duration(milliseconds: 1000), () {
      return HiNetResponse(
          request: request,
          data: {"code": 0, "message": 'success'} as T,
          statusCode: 403);
    });
  }
}

在hi_net.dart中使用:

    //发送请求
  Future> send(BaseRequest request) async {
    ///使用Dio发送请求
    HiNetAdapter adapter = MockAdapter();
    return adapter.send(request);
  }

5、扩展hinet添加对dio的支持

在pubspec.yaml中导入dio三方库:

dependencies:
  flutter:
    sdk: flutter

  ...
  cupertino_icons: ^1.0.2
  dio: ^4.0.0

在http-->core-->新建 dio_adapter.dart ,用于适配Dio:

import 'package:dio/dio.dart';
import 'package:flutter_bilibili/http/core/hi_error.dart';
import 'package:flutter_bilibili/http/core/hi_net_adapter.dart';
import 'package:flutter_bilibili/http/request/base_request.dart';

///Dio适配器
class DioAdapter extends HiNetAdapter {
  @override
  Future> send(BaseRequest request) async {
    // 搞两个变量,一个没初始化赋值,一个初始化赋值
    // options 是Dio的可选参数
    var response, options = Options(headers: request.header);
    var error;
    try { //发送网络请求
      if (request.httpMethod() == HttpMethod.GET) {
        response = await Dio().get(request.url(), options: options);
      } else if (request.httpMethod() == HttpMethod.POST) {
        response = await Dio()
            .post(request.url(), data: request.params, options: options);
      } else if (request.httpMethod() == HttpMethod.DELETE) {
        response = await Dio()
            .delete(request.url(), data: request.params, options: options);
      }
    } on DioError catch (e) {
      error = e;
      response = e.response;
    }
    if (error != null) {
      ///抛出HiNetError
      throw HiNetError(response?.statusCode ?? -1, error.toString(),
          data: await buildRes(response, request));
    }
    return buildRes(response, request);
  }

  ///构建HiNetResponse
  Future> buildRes(
      Response? response, BaseRequest request) {
    return Future.value(HiNetResponse(
        //?.防止response为空
        data: response?.data,
        request: request,
        statusCode: response?.statusCode,
        statusMessage: response?.statusMessage,
        extra: response));
  }
}

6、Dart JSON编码器和解码器剖析

void test(){
  const jsonString = "{\"name\":\"flutter\",}"
  //json 转map
  Map jsonMap = jsonDecode(jsonString);
  print('name:${jsonMap['name']}');
  
  //map 转JSON
  String json = jsonEncode(jsonMap);
  print('json:$json');
}

7、三种JSON解析技巧带你解放生产力

  • 手写实体类

  • 网页自动生成实体类✅

    通过: https://www.devio.org/io/tools/json-to-dart/ 在线生成实体类

  • json_serializable使用技巧

    官方demo: https://github.com/google/json_serializable.dart/tree/master/example

Step1: 安装插件

dependencies:
  ...
  dio: ^4.0.0
  json_annotation: ^4.0.1
  
dev_dependencies:
  ...
  json_serializable: ^4.1.3
  build_runner: ^2.0.4

Step2: 配置实体类

示例: 比如在result.dart中:
// result.g.dart 将在我们运行生成命令后自动生成
import 'package:json_annotation/json_annotation.dart';

part 'result.g.dart'; //运行命令后自动生成的文件名

@JsonSerializable()
class Result {
  //定义字段
  int code;
  String method;
  String requestPrams;

  //构造方法
  Result(this.code, this.method, this.requestPrams);

  //固定格式,不同的类使用不同的mixin即可
  factory Result.fromJson(Map json) => _$ResultFromJson(json);

  //固定格式,
  Map toJson() => _$ResultToJson(this);
}

因为实体类的生成代码还不存在, 所以上面代码会提示一些错误是正常现象

Step3 : 执行build生成实体类

$ flutter packages pub run build_runner build

11 统一缓存管理框架hicache设计

添加依赖插件: shared_preferences

shared_preferences: ^0.5.12+4

hi_cache封装

import 'package:shared_preferences/shared_preferences.dart';

///缓存管理类
class HiCache {
  SharedPreferences? prefs;

  HiCache._() {
    init();
  }

  static HiCache? _instance;

  HiCache._pre(SharedPreferences prefs) {
    this.prefs = prefs;
  }

  ///预初始化,防止在使用get时,prefs还未完成初始化
  static Future preInit() async {
    if (_instance == null) {
      var prefs = await SharedPreferences.getInstance();
      _instance = HiCache._pre(prefs);
    }
    return _instance!;
  }

  static HiCache getInstance() {
    if (_instance == null) {
      _instance = HiCache._();
    }
    return _instance!;
  }

  //初始化
  void init() async {
    if (prefs == null) {
      prefs = await SharedPreferences.getInstance();
    }
  }

  setString(String key, String value) {
    prefs?.setString(key, value);
  }

  setDouble(String key, double value) {
    prefs?.setDouble(key, value);
  }

  setInt(String key, int value) {
    prefs?.setInt(key, value);
  }

  setBool(String key, bool value) {
    prefs?.setBool(key, value);
  }

  setStringList(String key, List value) {
    prefs?.setStringList(key, value);
  }

  remove(String key) {
    prefs?.remove(key);
  }

  // 通过泛型的方式来获取数据, 提高复用性
  T? get(String key) {
    var result = prefs?.get(key);
    if (result != null) {
      return result as T;
    }
    return null;
  }
}

测试使用封装的hi_cache

在main.dart中:
class _MyHomePageState extends State{
  @voerride
  void initState() {
    super.initState();
    
    HiCache.preInit(); //初始化
  }
}

/// 保存与读取本地信息
void test(){
  HiCache.getInstance().setString("aa","1234");
  var value = HiCache.getInstance().get("aa");
  print('value:$value')
}

你可能感兴趣的:(Flutter☞网络封装)