preface

After learning Dart and writing flutter for a while, I realized that most of the errors encountered with FLUTTER were related to NULL.

An accidental if statement returns a null error. To avoid this error, write the following

if( true == (boolVar ?? false)) {// balabala....
}
Copy the code

Why is this so? Because if(null == true) it will report an error. If you’re not careful, you can write code that you think won’t be null when it’s null at some point and the actual error is lost in a bunch of wrappers, which makes debugging a pain in the neck.

Dio on pit

This code converts Dio requests into Debug records. Finally, the interceptor prints out the log.

String getResponseDebug(Response resp) {
  if (resp == null) return 'getResponseDebug()->null';
  //debug
  var debug = getRequestDebug(resp.request);
  debug += '\nresponse--> ${resp.statusCode} ${resp.statusMessage}';
  if (resp.data is Map) {
    debug += '\n\tdata-->\n${prettyJson(resp.data)}\n';
  } else if (resp.data is List) {
    var data = (resp.data as List);
    var json = data.length >= 1 ? [data.first] : data;
    debug += '\n\tdata-->\n${prettyJson(json)}\n';
  } else {
    debug += '\n\tdata-->\n${resp.data}\n';
  }
  if (resp.isRedirect) {  // Pay attention to this line!!
    debug += '\n\tredirects-->\n${prettyJson(resp.redirects)}\n';
  }
  return debug;
}
Copy the code

The code, which seems to be fine, fails when I use dio_http_cache for caching.

In order to address this problem, I also found out the mechanism of interceptor except for Dio, which is quite fruitful.

Dude, go inside the isRedirect property and take a look

  /// Whether this response is a redirect.
  /// ** Attention **: Whether this field is available depends on whether the
  /// implementation of the adapter supports it or not.
  final bool isRedirect;
Copy the code

There was a lot of attention, but by the time I noticed it, the problem had already been fixed.

Next time, I wonder if there is a possibility of null for such an obvious attribute. It just means I’m too young,too simple.

About the null

The earliest contact with null is the NULL pointer in C language, which has different differences between C and C++. In C, it’s just a zero, it’s a pointer to address zero; In C++, it is null(typed).

Therefore, I will understand null as a pointer in C language.

In the Dart Language tutorial, there is a sentence like this

Everything you can place in a variable is an object, and every object is an instance of a class. Even numbers, functions, and null are objects. All objects inherit from the Object class.

In DART, anything that can be put inside a variable is an Object, and that Object is just an instance of a class. All objects inherit from Object, even values, functions, and NULL.

// nullStr is a pointer to String
// If no value is assigned after the declaration, its value is null
String nullStr;
Copy the code

To make nullStr usable, we need to manually point to an object, such as nullStr=”Hello”, when nullStr is no longer null and it is safe to use.

So I can understand that DART is a pure object-oriented language, and you can find the toString() method for any type because it inherits from Object, so you can assign null to variables of any type.

The benefits of doing this are obvious, personally understood, because it reduces the programmer’s mental burden of no longer having to distinguish between pass-by-value and pass-by-reference in function arguments, and can focus on the specific business.

But the reality may not be as rosy as I think, as in the following code

//exchange a and b, does it work?
void exchange(int a,int b){
    var c = a;
    a = b;
    b = c;
}

void exchange2(int a){
    a = 100;
}
Copy the code

In fact, it doesn’t work, and I can’t even find a way to switch between A and B.

But the following code works

class Student {
  String no;
  String name;

  Student({this.no, this.name});

  String toString() {
    return '{ no: $no, name: $name} '; }}void updateName(Student s, String newName) {
  s.name = newName;
}

void main(List<String> args) {
  var stu = Student(
    no: '1001',
    name: 'Jack Li',);print('Student: $stu');
  updateName(stu, 'New Name');
  print('Student: $stu');
}

/* output
Student: { no: 1001, name: Jack Li }
Student: { no: 1001, name: New Name }
*/
Copy the code

As you can see from the results above, the internal properties of the forwarded object can be modified. S and stu of updateName refer to the same object, which holds a reference to that object. It is useless to try to swap references inside a function. Because only the references held by the variables inside the function are exchanged, there is no exchange effect.

For a type such as int, changes in other functions simply point to a new value and do not affect the previous value, so it behaves as if it were passed by value.

Stackoverflow discussion on this

  • Swapping function in Dart, not swapping the original values

  • What is the true meaning of pass-by-reference in modern languages like Dart?

Interceptors

One of the most comfortable aspects of the Dio library is its interceptor design, which literally means interceptor.

Through it, the request can be intercepted before it is sent, the request parameters can be modified or the process of the request can be changed to directly enter the response.

You can also intercept data when it is returned to determine whether the returned data is abnormal.

For example, the following defines the API return type to uniformly handle API exception data.

class ApiResponseInterceptor extends InterceptorsWrapper {
  @override
  Future onResponse(Response response) async {
    // If the data is from the cache, return it directly
    if ((response.headers.value(DIO_CACHE_HEADER_KEY_DATA_SOURCE) ?? ' ') = ='from_cache') {
      return response;
    }
    var resp = ApiResponse.fromJson(response.data);
    if(resp.code ! =200) {
      throw DioError(
        request: response.request,
        response: response,
        error: resp,
      );
    }
    response.data = resp.data ?? [];
    returnresponse; }}Copy the code

Later, errors can be intercepted, similar to a catch effect.

It can be said that interceptor makes the Dio library very useful.

The dio-HTTP-cache I use in my code is an interceptor for caching requests.

With that in mind, how does dio-HTTP-cache handle response data

Response _buildResponse(
    CacheObj obj, int statusCode, RequestOptions options) {
    Headers headers;
    if (null! = obj.headers) { headers = Headers.fromMap((Map<String.List<dynamic>>.from(
            jsonDecode(utf8.decode(obj.headers))))
        .map((k, v) => MapEntry(k, List<String>.from(v))));
    }
    if (null == headers) {
    headers = Headers();
    options.headers.forEach((k, v) => headers.add(k, v ?? ""));
    }
    // add flag
    headers.add(DIO_CACHE_HEADER_KEY_DATA_SOURCE, "from_cache");
    dynamic data = obj.content;
    if(options.responseType ! = ResponseType.bytes) { data = jsonDecode(utf8.decode(data)); }returnResponse( data: data, headers: headers, extra: options.extra.. remove(DIO_CACHE_KEY_TRY_CACHE), statusCode: statusCode ??200);
}
Copy the code

It’s easy to see that the isRedirect property is not handled when the Response is returned, so it automatically becomes null.

The last

After this digging, I became a little more aware of NULL in DART and realized the serious consequences of not reading documents carefully. More importantly, I found the benefit of reading other people’s code, which is to better understand how it is implemented and better avoid pitfalls.