Flutter 疑难杂症(二)

1、空字符占位(\u3000 \u0020)使用

body: Container(
        width: double.infinity,
        height: double.infinity,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('a\u0020b\u0020cdefg'),
            Text('abcdefgh'),
            Text('三\u0020\u0020字\u0020\u0020字'),
            Text('四字字字'),
            Text('二\u3000\u3000字'),
            Row(
              children: [
                Text('三\u0020\u0020字\u0020\u0020字'),
                Expanded(
                  child: SizedBox(),
                )
              ],
            )
          ],
        ),
      ),

效果如下:


image.png

从结果可知,\u3000相当于占用一个中文字符,\u0020相当于占用1/4个中文字符,我们可以通过\u3000或\u0020来对齐表单左边文案,注意:字体样式要一致才有效;

2、数组、列表等自带查找匹配方法

iterator.dart

  /**
   * Returns the first element that satisfies the given predicate [test].
   *
   * Iterates through elements and returns the first to satisfy [test].
   *
   * If no element satisfies [test], the result of invoking the [orElse]
   * function is returned.
   * If [orElse] is omitted, it defaults to throwing a [StateError].
   */
  E firstWhere(bool test(E element), {E orElse()}) {
    for (E element in this) {
      if (test(element)) return element;
    }
    if (orElse != null) return orElse();
    throw IterableElementError.noElement();
  }

  /**
   * Returns the last element that satisfies the given predicate [test].
   *
   * An iterable that can access its elements directly may check its
   * elements in any order (for example a list starts by checking the
   * last element and then moves towards the start of the list).
   * The default implementation iterates elements in iteration order,
   * checks `test(element)` for each,
   * and finally returns that last one that matched.
   *
   * If no element satisfies [test], the result of invoking the [orElse]
   * function is returned.
   * If [orElse] is omitted, it defaults to throwing a [StateError].
   */
  E lastWhere(bool test(E element), {E orElse()}) {
    E result;
    bool foundMatching = false;
    for (E element in this) {
      if (test(element)) {
        result = element;
        foundMatching = true;
      }
    }
    if (foundMatching) return result;
    if (orElse != null) return orElse();
    throw IterableElementError.noElement();
  }

  /**
   * Returns the single element that satisfies [test].
   *
   * Checks elements to see if `test(element)` returns true.
   * If exactly one element satisfies [test], that element is returned.
   * If more than one matching element is found, throws [StateError].
   * If no matching element is found, returns the result of [orElse].
   * If [orElse] is omitted, it defaults to throwing a [StateError].
   */
  E singleWhere(bool test(E element), {E orElse()}) {
    E result;
    bool foundMatching = false;
    for (E element in this) {
      if (test(element)) {
        if (foundMatching) {
          throw IterableElementError.tooMany();
        }
        result = element;
        foundMatching = true;
      }
    }
    if (foundMatching) return result;
    if (orElse != null) return orElse();
    throw IterableElementError.noElement();
  }

方法说明:
firstWhere主要是用于筛选顺序第一个符合条件的元素,可能存在多个符合条件元素;
lastWhere主要是用于筛选顺序最后一个符合条件的元素,可能存在多个符合条件元素;
singleWhere主要是用于筛选顺序唯一一个符合条件的元素;

参数说明:
bool test(E element) :查找条件匹配方法,传子元素,匹配返回true;
E orElse():查找不到时,执行的方法,一般返回null;
返回值:返回匹配的子元素或orElse的返回值,一般通过自定义orElse中的返回值来判断是否查找到匹配条件的元素;

使用例子:

   var numbers = [0, 3, 1, 2, 7, 12, 2, 4];
   //注意: 如果没有找到,执行orElse代码块,可返回一个指定的默认值-1
   print(numbers.firstWhere((num) => num == 5, orElse: () => -1)); 
   //注意: 如果没有找到,执行orElse代码块,可返回一个指定的默认值-1
   print(numbers.lastWhere((num) => num == 2, orElse: () => -1)); 
   //注意: 如果没有找到,执行orElse代码块,可返回一个指定的默认值, 
   //前提是集合中只有一个符合条件的元素, 否则就会抛出异常
   print(numbers.singleWhere((num) => num == 4, orElse: () => -1));

使用坑:

  • E orElse()虽然是一个可选参数,但如果找不到匹配的,会直接抛出异常,所以orElse参数必须实现;

  • singleWhere慎用,因为singleWhere必须集合中只有一个符合条件的元素, 否则就会抛出异常;同时,在查找条件要求不高时,推荐使用firstWhere,因为firstWhere查找到就返回结果,而singleWhere需要所有元素都要查找才结束,firstWhere效率更高;

3、按条件引入库

语法:

import <文件> if(<条件>) <文件>  as <别名>

例子:

import '_network_image_io.dart'
  if (dart.library.html) '_network_image_web.dart' as network_image;

说明:
条件参数:一般为不同平台(android/ios/桌面/web)加载的不同的处理程序(我支持您编写此库);
使用场景:不同平台(android/ios/桌面/web)兼容开发;

4、factory工厂构造方法

以NetworkImage为例:

Image(
        image: NetworkImage(imgUrl), 

一般NetworkImage使用如上,看下NetworkImage源码,如下:
image_provider.dart

import '_network_image_io.dart'
  if (dart.library.html) '_network_image_web.dart' as network_image;

abstract class NetworkImage extends ImageProvider {
 
  const factory NetworkImage(String url, { double scale, Map headers }) = network_image.NetworkImage;
  ...
 
}

NetworkImage是一个抽象类,为什么可以实例化?我们注意到NetworkImage的构造方法是一个工厂构造方法,它其实实例化的是它的子类,如下:
_network_image_io.dart

import 'image_provider.dart' as image_provider;

class NetworkImage extends image_provider.ImageProvider implements image_provider.NetworkImage {
 
  const NetworkImage(this.url, { this.scale = 1.0, this.headers });
  ...

_network_image_web.dart

import 'image_provider.dart' as image_provider;
class NetworkImage
    extends image_provider.ImageProvider
    implements image_provider.NetworkImage {

  const NetworkImage(this.url, {this.scale = 1.0, this.headers});
  ...

上面两个分别是对应移动和web平台NetworkImage的实现类,也就是NetworkImage的子类,并且外部不可访问;
上面的代码比较绕,需要细心看,再理解下factory就很容易懂了

工厂构造方法:

  • 使用 factory 关键字来定义构造方法;
  • 构造方法返回对象;
  • 构造方法可能从缓存中获取一个实例并返回,或者 返回一个子类型的实例;

5、网络请求数据解析坑

class TestModel {
  double doubleValue;

  TestModel({this.doubleValue});

  TestModel.fromJson(Map json) {
    doubleValue = json['DoubleValue'];
  }

  Map toJson() {
    final Map data = new Map();
    data['DoubleValue'] = this.doubleValue;
    return data;
  }
}

一般我们用xx.fromJson的方法来解析网络请求数据,如果后台数据doubleValue返回0,就会报type 'int' is not a subtype of type ‘double'的错误

解决:
将doubleValue定义为dynamic类型,如下:

class TestModel {
  dynamic doubleValue;

  TestModel({this.doubleValue});

  TestModel.fromJson(Map json) {
    doubleValue = json['DoubleValue'];
  }

  Map toJson() {
    final Map data = new Map();
    data['DoubleValue'] = this.doubleValue;
    return data;
  }
}

注意:

  • 展示时用doubleValue.toString()
  • double类型的数据用toString()可能会出现小数点位数不符合要求的,需要将toString()换成toStringAsFixed(小数点位数)

你可能感兴趣的:(Flutter 疑难杂症(二))