Flutter学习之基础语法

最近由于要启动mac跟Windows的项目开发,考虑到mac跟Windows写UI页面的便秘程度,决定调研一下同时支持多平台的flutter在当前项目是否可行,顺便记录一下学习的东西,以便后续翻阅;

  • var、final、const

声明可以有两种方式,一种是不指定类型,即使用var关键字;

/// 声明变量
// 不指定类型,即使用var关键字
var name = 'Bob';
// 当变量未赋初始值的时候,是个dynamic类型,可以随意赋值不同联系
var a;
a = 5;
a = 'ff';
// 当变量已经赋初始值的时候,再赋其他类型值就会报错,提示你要修改
var a = 666;
a = 'f' as int;

// 另一种是明确指定类型(Optional types)

String name = 'Bob';

// 在变量类型并不明确的情况下,可以使用dynamic关键字

dynamic name = 'Bob';

未初始化的变量默认值是 null。即使变量是数字类型默认值也是 null,因为在 Dart 中一切都是对象,数字类型也不例外。

使用过程中从来不会被修改的变量, 可以使用 final 或 const,而不是 var 或者其他类型,Final 变量的值只能被设置一次;

final name = 'Bob';
final name;
name = 'Bob';

const name = 'Bob';
const name; // error 
name = 'Bob';

final跟const常量的区别在于,const修饰的常量,在声明的时候就得赋值;

num类型,取整除法运算符

算术运算符都是类似的大部分都是类似的,除了符号为~/ 做除法运算时对结果进行取整;

int a = 8;
print(a.isEven);// 偶数
print(a.isOdd); // 奇数
// double类型不能用isEven、isOdd

double b = 1;
b = 1.5;
int c = 2;
c = 1.5; // error
// double类型可以赋值整型,int类型不能赋值小数

// 取整运算符
var nums = [1,2,3,4,5];
nums.forEach((element) { 
  print(element~/2);
});

输出就是0 1 1 2 2

String

String str = 'chris';
print(str[0]);
print(str[1]);
print(str+'666');
print(str*2);

// 打印结果
flutter: c
flutter: h
flutter: chris666
flutter: chrischris

// format拼接,用$符号取值
int a = 10;
int b = 20;
int c = b - a;
print('a + b = ${a+b}');
print('a - b = $c');

// 转义字符,前面加个r即可
String str = 'chris\n666';
String str1 = r'chris\n666';
print(str);
print(str1);

// '''可以表示多行
String str = '''xxx
    yyy
    ddd
  ''';
  print(str);

// 打印结果
flutter: xxx
    yyy
    ddd

list、map

list

可以存放不同类型的数据

  var list1 = [1, 2, 'chris'];
  list1.add("666");
  list1.insert(1, 'value');
  list1.remove('chris');
  list1.sort(); // 排序
  list1.sublist(1,2);

  var list2 = [1, 2, 3];
  var list3 = const [1, 2, 'chris']; // 不可变数组
  list2.add(4);
  list2.add("value"); // error list1初始化都是int类型,所以只能添加int类型 
  list3.add('666');// error

map

var gifts = {
  // Key:    Value
  'first': 'partridge',
  'second': 'turtledoves',
  'fifth': 'golden rings'
};

var nobleGases = {
  2: 'helium',
  10: 'neon',
  18: 'argon',
};

print(gifts.length);
print(gifts.keys);
print(gifts.values);

以上 Map 对象也可以使用 Map 构造函数创建:

var gifts = Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';

var nobleGases = Map();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';

// 创建 Map 类型运行时常量,要在 Map 字面量前加上关键字 const。
final constantMap = const {
  2: 'helium',
  10: 'neon',
  18: 'argon',
};

// constantMap[2] = 'Helium'; // error

// list转map
var list = ['one','two','three'];
list.asMap;

赋值运算符??=

当被赋值的对象为null时候才进行赋值,否则保持原值不变,例如:

var b = 10;
b ??= 100;
print(b);

条件运算符??

Dart有以下两个运算符,有时可用来替换 if-else 表达式, 让表达式更简洁:

***condition*** **?** ***expr1*** **:** ***expr2***

如果条件为 true, 执行 expr1 (并返回它的值): 否则, 执行并返回 expr2 的值。

***expr1*** **??** ***expr2***

如果 expr1 是 non-null, 返回 expr1 的值; 否则, 执行并返回 expr2 的值(类似kotlin的?:)。

如果赋值是根据布尔值, 考虑使用 ?:。

var visibility = isPublic ? 'public' : 'private';

如果赋值是基于判定是否为 null, 考虑使用 ??。

 String playerName(String name) => name ?? 'Guest';

Dart方法中的箭头函数

Dart中的方法也是一个对象

返回值和参数可以省略,

当方法的执行语句只有一句的时候,可以使用箭头函数 => 表示;

String playerName(String name) {
    return name ?? 'Guest';
}

// 等价于
String playerName(String name) => name ?? 'Guest';

方法中的可选参数

// 可选参数,传递参数必须带上形参的名字 ,不然程序不知道你要给哪一个参数赋值;

这跟扩展参数不一样,扩展参数是逗号后面可以一直写;

使用[]包裹的就是按照顺序来的可选型,不用传参数名

void function() {
    print(sum1(1,c:3,b:4));
}

sum1(int a,{b, c}) {
    b ??= 0;
    c ??= 0;
    return a + b + c;
}

void function() {
    print(sum1(1,c:3,b:4));
}

void function2() {
    print(sum1(1,2));
}

// 按照顺序来传参的可选型
sum2(int a,[int b = 0,int c = 0]) {
    return a + b + c;
}

上面是没有指定类型的可选参数,如果不设置默认参数或者可空,就会报错

// nullability
sum1(int a,{int? b, int c = 0}) {
    b ??= 0;
    c ??= 0;
    return a + b + c;
}

方法作为参数传递

作为参数保存或者传递给其他方法调用

void main() {
  var hello = printHello;
  hello();
}

void printHello() => print('Hello');

void main() {
 var list = [1,2,3,4];
 list.forEach(print);
}

匿名函数

void main() {
 var func = () {
    print('匿名方法');
 };
 func();

 ((){
    print('立即执行方法');
 })();
}

匿名方法也可以作为参数进行传递,这就有点像我们OC、Swift当中的闭包了,但是匿名方法跟闭包还是有一丢丢的区别的;

void main() {
 var list = [1,2,3,4];

 int b = 0;
 forEachDemo(list, (var a) {
    b++;
    print('元素$b=$a');
 });
};

void forEachDemo(List list, void func(var element)) {
    for(var e in list) func(e);
}

闭包

首先闭包的概念是:定义在函数里面的函数就是闭包,闭包也是一个对象;

闭包的作用:可以访问外部函数的局部变量;

在iOS中,使用闭包最怕的就是闭包跟外部函数互相持有,造成循环引用;

下面一个例子,这段代码打印结果是0,1,2,3;

void main() => closureDemo();

void closureDemo() {
    var func = funA(); // 此时func就是一个闭包
    func();
    func();
    func();
    func();
}

funA() {
    int count = 0; 
    // 返回一个匿名函数给外部使用,这个匿名函数其实就是一个闭包
    return () => print(count++); 
}

这个就是因为闭包形成一个闭环,在栈上持续存在,没有被释放,所以它可以不断的访问外部函数的局部变量;如果我们在print里面传进去的是一个常量,此时也就不会存在闭环这一说;

Dart中带下划线的代表私有的东西,比如_age就是私有属性,_run代表私有方法,只有类内部才能访问,外部不能直接访问;

Dart中构造函数前面的的 new 关键字是可选的,可写也可不写;

void main() {
 Person p = Person(); 
 // p._age = 18; // error
 p.height = 180;
 // p._run(); // error
 p.printP();
}

class Person {
  final String name = "Chris";
  int? _age;
  int? height;

  void _run() {
    print("name:$name age:$age");
  }

  void printP() {
    _run();
  }
}

构造函数

void main() {
  Person p1 = Person("Chris",18);
  Person p2 = Person.withName("Chris",180);
  p1.changeName(20);
  p1.printP();
  p2.printP();
}

class Person {
  String? _name;
  int? age;
  int? height;

  Person(String name, int age) {
    this._name = name; // 也可写成_name = name;
    this.age = age; // 如果写成age = age,就会赋值不进去
  }
  // 上面构造方法跟下面这个等价
  Person(this._name, this.age);

  // 命名构造函数
  Person.withName(this._name, this.height);

  changeName(int age) {
    this.age = age;
  }

  _run() {
     print("name:$_name,age:$age,height:$height");
  }

  printP() {
    _run();
  }
}

当一个对象所有的成员属性都被final修饰的时候,那么这个对象就可以被创建为常量 const,提高运行速度;

class Person() {
 final String? _name;
 final int? age;
 const Person(this._name);
}

工厂构造、单例对象、初始化列表

再实际开发中经常会用到单例设计模式,比如我定义一个工厂类,需要它是一个单例,可以这么写;

class FactoryClass() {
    // 单例,用全局static修饰符修饰
    static FactoryClass? _instance;
    factory FactoryClass() {
        if(_instance == null) {
            // 此处由于不能调用自身构造函数,所以可以写一个私有的_init命名构造函数;
            _instance = FactoryClass._init();
        }
        // 构造函数不能带return,所以如果要加return,需要factory修饰构造方法
        return _instance!; // return不能返回空,所以用!强制解包
    }

    // 上面方法可以简化为下面的写法,利用箭头函数,这就是一个最简单的单例方法了
    factory FactoryClass() => _instance ??= FactoryClass._init();

    // 私有的命名构造函数,可以创建一个新的对象
    FactoryClass._init();
}

初始化列表

在开发期间, 可以使用 assert 来验证输入的初始化列表的数据正确性。

class Person {
    String name;
    int age;
    final height;

    Person(this.name,this.age,int h) : height = h,
                                       assert(h >= 0), 
                                       assert(age >= 0) {
        print("name:$name, age:$age, height:$height");
    }
}

类方法和对象操作符

void staticDemo() {
    // 调用静态属性
    StaticClass.count = 10;
    // 调用静态方法
    print(StaticClass.sum(20));
}

class StaticClass() {
    // 静态属性
    static int count = 1;
    // 实例属性
    int current = 2;
    // 静态方法
    static int sum(int a) {
        return a + count;
    }
    // 实例方法即可以访问对象属性,也可以访问静态属性
    int sum2(int a) {
        return a + count + current;
    }
}

对象操作符

as、is、.. 操作符

void staticDemo() {
    var s1 = Object();
    s1 = StaticClass();
    // as 强转
    (s1 as StaticClass).sum2();
    // is 判断
    if(s1 is StaticClass) {
        s1.sum2();
        // ..操作符,链式编程,每次都是返回对象本身,所以此处打印的是对象
        print(s1..current = 15..sum2(20));
    }
}

class StaticClass() { 
    int current;
    int sum2(int a) {
        return a + current;
    }
}

继承

Dart中使用extends继承一个类,它是单继承的形式;

子类会继承除了构造方法以外的属性方法;

void extendsDemo() {
    Student st = Student();
    st.run();
    st.study;
    st.name = "Chris";
    st.age = 18;

    print(st.isFree);
}

// 我们所写的所有的类,不写的情况下默认都是继承Object,所以class Person等价于下面
class Person extends Object {
    String? name;
    int? age;
    int? _height;
    bool get isFree => height! <= 110;

    run() {
        print("Person run...");
    }
}

class Student extends Person {
    study() {
        print("good good study");
    }
    // override复写父类的东西
    @override
    bool isFree => age! < 18;
    @override
    run() {
        print("Student run...");
    }
}

有一个小细节需要注意的就是,在父类中有显性的实现构造方法,那么子类中至少得实现其中一个才行;

class Person {
    String? name;
    int? age;
    int? _height;
    Person(this.age); 
    Person.init();
    Person.withName(this.name);
}

class Student extends Person {
    final String subName;
    // Student(String? name) : super(name); 
    // Student.init() : super.init();

    Student.withName(String? name) : subName = name!, super.withName(name);

    // 当然我们也可以自己写构造方法,只要实现其中一个就可以
    // 这个super实现,跟初始化列表一样,我们也可以在前面写我们要初始化的东西,当然他们只能写在        super之前
    Student() : subName = "Chris", super.init();
}

抽象类和接口

抽象类其实就有点类似于iOS中的协议,即只有声明方法,没有实现方法;

抽象类是不能被实例化的类,用abstract修饰

AbstractDemo() {
    AbstractClass as = Student();
    print(as.sum);
}

abstract class AbstractClass {
    // 这就是抽象方法
    int sum(int a, int b);
}

// 继承抽象类就必须实现抽象方法
class Student extends AbstractClass {
    @override
    int sum(int a, int b) {
        return a + b;
    }
}

在iOS中可以遵循多个协议,那么在Dart中是否也可以呢?

我们可以用implements来实现多个抽象类的实现,需要重写抽象类里的所有方法

AbstractDemo() {
    AbstractClass as = Student();
    print(as.sum);
}

abstract class AbstractClass1 { 
    int sum1(int a, int b);
}
abstract class AbstractClass2 { 
    int sum2(int a, int b);
}
abstract class AbstractClass3 { 
    int sum3(int a, int b);
}

class Student implements AbstractClass1, AbstractClass2, AbstractClass3 {
    @override
    int sum1(int a, int b) {
        return a + b;
    }
    @override
    int sum2(int a, int b) {
        return a + b;
    }
    @override
    int sum3(int a, int b) {
        return a + b;
    }
}

Mixins混入

混入,说白了就是一个多继承的形式

mixinDemo() {
    D d = D();
    d.a(); // 打印c,以最后一个加载的为主
}

class A {
    a() => print("a.....");
}

class B {
    a() => print("b.....");
    b() => print("b.....");
}

class TestClass {}

// 参与混入的类,也不能继承于非Object类,就怕继承的类有构造方法
class C extends TestClass {
    // C(); // 参与混入的类,不能有构造方法
    a() => print("c.....");
    c() => print("c.....");
}

// class D extends A with B, C {}
class D = A with B, C;

operator操作符

我们可以对操作符,大于、小于、等于等操作符进行重载,使用operator修饰符内部重新实现操作符的比较;

operatorDemo() {
    OperatorClass opt1 = OperatorClass(10);
    OperatorClass opt2 = OperatorClass(20);
    print(opt1 > opt2);
}

class OperatorClass {
    int age;
    OperatorClass(this.age);
    bool operator >(OperatorClass other) => this.age > other.age;
}

异步支持

方式1:使用async+await关键字

// 定义一个异步函数
Future requestSomething() async {
  String result = await getSomething(); // 线程在此阻塞,等待getSomething执行完成
  return result; // 不需要使用FutureApi,Dart会自动创建Future对象
}

String getSomething() {
  return "my name is Dart";
}

方式2:使用Future提供的api

Future someOperations() {
  return Future.value("my name is Dart");
}

// 操作完成后执行then回调
someOperations().then((value) => {
  print(value)
})
// 操作执行过程中出错执行onError回调
.onError((error, stackTrace) => {
  print("$error, $stackTrace")
});
  • 处理Stream(异步事件序列):

方式1:使用async+await for关键字

例如需要连续执行100次sendAudioStream耗时方法,但是又要求这些任务按顺序依次执行

// 需要按顺序执行多次的耗时任务
Stream sendAudioStream(ByteData audioStream) {
  print(audioStream.lengthInBytes);
  return Stream.value(true);
}

// 对耗时任务结果的处理
Future handleStream(Stream sendAudioStream) async {
  await for(var stream in sendAudioStream) {
    print("sendAudioStream result is $stream");
    await Future.delayed(Duration(milliseconds: 180));
  }
  return Future.value();
}

Iterable fromOneTo(int n) sync* {
  int k = 1;
  while(k < n) {
    yield k;
    k++;
  }
}

void main() {
  for (var i in fromOneTo(100)) { 
    // 将耗时任务加入队列中
    handleStream(sendAudioStream(ByteData(1024)));
  }
}

方式2:使用Stream提供的api

将上例中的handleStream方法改动为下方所示即可:

Future handleStream(Stream sendAudioStream) async {
    sendAudioStream.listen((event) {
        print("result is $event");
        await Future.delayed(Duration(milliseconds: 180));
    }, 
    onError: (){

    }, 
    onDone: (){

    });
    return Future.value();
}

你可能感兴趣的:(Flutter学习之基础语法)