Says flutter---02Dart语法

133 阅读5分钟

01-Hello World

  • dart语言的入口函数是main()函数,并且必须显示定义;该函数没有返回值。
  • 传递给main的命令行参数是通过List(可选参数)。
  • 每行语句必须用分号结尾。

02-定义变量

明确声明

  • 变量类型 变量名称=赋值
    String name = 'shk'
    int age = 18
    name = 18 //错误写法
    /** 定义的变量可以修改值,但是不可以赋值其他类型 **/
    

类型推导

  • var使用
    var name = 'dsfjh' //name类型为string
    name = 18 //错误写法
    /** 其中 数据.runtimeType 用于获取变量当前的类型**/
    
  • dynamic使用(可以存储任何类型)
    1.在开发中,不建议使用dynamic类型变量会带来风险
      dynamic name = 'ddf';  //name.runtimeType为String
      name = 19 //name.runtimeType为int
    2.Object也可以定义变量,与dynamic的区别是:
      Object调用方法时,编译时会报错;(由于Object声明的对象是Object类型,所以只能使用Object类的变量和方法,不能使用其当前字类的方法,比如String.length等)
      dynamic调用方法时编译不报错,但是运行时会存在安全隐患。(可能由于类型不一样导致错误调用)
    
  • final和const使用
    相同点:都是定义常量,不可改变。
    不同点:const赋值,在编译期间就已经确定;      // const num = 3.14
           final赋值可以动态获取,比如赋值一个函数。(一旦赋值就会有确定的结果,不会再次赋值)// const num = getNumber()
    案例demo:
    class Person{
      const Person();  
    }
    main(List<String> args) {
      final a = const Person();  //const放在赋值语句的右边,可以共享对象,提高性能(见类的常量构造函数)
      final b = const Person();
      print(identical(a, b)); //true
    
      final m = Person();
      final n = Person();
      print(identical(m, n)); //false
      }
    

03-数据类型

数字类型

1.整数用int,浮点数用double2.intdouble可表示的范围取决于运行dart的平台。
3.字符串转数字:int/double.parse('123')  //int 123
  数字转字符串:123.toString()/toStringAsFixed //'123'

布尔类型

/** dart中不能判断非零即真,或者非空即真 **/
var str = 'fff'
// 错误的写法
if(str){
  print('str')
}

字符串类型

1.单引号和双引号皆可。
2.可以用3个单引号或者双引号表示多行字符串
  var str = '''djf
  jopdfop
  siufoidsf'''
3.字符串与其他变量或者表达式拼接:${} ,如果表达式是一个标识符,{}可以省略
  'my name is ${name}, age is $age'

集合类型

  • 定义
    • List(元素有序,且不一定不重复)
      1.使用类型推导定义
        var list = ['a','s','d'];
      2.明确指明类型
        List<int> list = [1,5,3];
      
    • Set(元素无序,且不重复)---可用于去重
      1.使用类型推导定义
        var set = {'s','d'};
      2.明确指明类型
        Set<int> = {4,5,2};
      
    • Map
      1.使用类型推导定义
        var map = {'heghf':1.66, 'sddfdff':'dddd'};
      2.明确指明类型
        Map<String,Object> map = {'heghf':1.66, 'sddfdff':'dddd'};
      
  • 常见操作
    • 获取长度属性 length
      list.length;
      set.length;
      map.length;
      
    • 添加/删除/包含操作
      list.add(5);
      list.remove(1);
      list.contains(2);
      list.removeAt(2);  //List根据index删除指定位置元素           
      
    • 有关Map操作
      1.根据key获取value  map['heghf']
      2.获取所有的entries map.entries //一对一对获取?
      3.获取所有的keys    map.keys
      4.获取所有的values  map.values
      5.判断是否包含某个key或者value   map.containsKey('heghf')
      6.根据key删除元素   map.remove('heghf')
      

04-函数

定义

  • 基本定义
    返回值类型 函数名称(参数列表){
      函数体
      return 返回值
    }
    //返回值类型可忽略不写
    
  • 箭头语法定义(适用于函数中只有一个表达式,不是一个语句)
    sum(num1,num2) => num1+num2;
    

参数问题

  • 可选参数
    • 命名可选参数
      {param1, param2, ...}
      fn(String name,{int age}){
        print('$name') 
      }
      fn('name:www') //name=www,age=null
       fn('name:www',age: 19) //name=www,age=19
      
    • 位置可选参数
      [param1, param2, ...]
      fn(String name,[int age, double height}){
        print('$name') 
      }
      fn('www',17) //name=www,age=17,height=null
      
  • 参数默认值
    可选参数才有默认值,必选参数不能有默认值;
    不传入的情况下,使用默认值。
    

函数是一等公民

可以将函数赋值给一个变量,也可以把函数作为另外一个函数的参数或者返回值

匿名函数的使用

main(List<String> args) {
  // 1.定义数组
  var movies = ['盗梦空间', '星际穿越', '少年派', '大话西游'];
  // 2.使用forEach遍历: 有名字的函数
  printElement(item) {
    print(item);
  }
  movies.forEach(printElement);
  // 3.使用forEach遍历: 匿名函数
  movies.forEach((item) {
    print(item);
  });
  movies.forEach((item) => print(item));
}

词法作用域

根据代码的结构({})来决定作用域范围,优先使用自己作用域的变量,没有再一层层向外寻找。

词法闭包

闭包可以访问其词法范围内的变量,即使函数在其他地方被使用,也可以正常的访问。

返回值问题

所有函数都返回一个值;如果没有返回值,则返回null。(其对应的类型是dynamic类型)

05-运算符

除法、整除、取模运算

??= 赋值操作

//针对于自己在使用过程中
当变量有值使用自己原来的值,当变量为null使用后面内容赋值。
main(List<String> args) {
  // var name2 = 'kobe';
  var name2 = null;
  name2 ??= 'james'; 
  print(name2); // 当name2初始化为kobe时,结果为kobe,当初始化为null时,赋值了james
}

ex1??ex2 条件运算符

//针对于赋值给别的变量
如果ex1是null,则返回ex2的结果;如果ex1不是null,直接使用ex1的结果。
// var temp = 'why';
var temp = null;
var name = temp ?? 'kobe';
print(name);// 当temp初始化为why时,name结果为why,当初始化为null时,name结果kobe

级联语法

对一个对象进行连续的操作
class Person {
  String name;
  void run() {
    print("${name} is running");
  }
}
main(List<String> args) {
  final p1 = Person();
  // p1.name = 'why';
  // p1.run();
  final p2 = Person()
              ..name = "why"
              ..run();
}

06-流程控制

if和else

不支持非空即真或者非0即真,必须有明确的bool类型

循环操作

switch-case

每一个case语句,默认情况下必须以一个break结尾。
main(List<String> args) {
  var direction = 'east';
  switch (direction) {
    case 'east':
      print('东面');
      break;
    case 'south':
      print('南面');
      break;
    default:
      print('其他方向');
  }
}

07-类和对象

类的定义

class 类名 {
  类型 成员名;
  返回值类型 方法名(参数列表) {
    方法体
  }
}
举例:创建一个对象(new 关键字可以省略)
main(List<String> args) {
  var p = new Person(); // 直接使用Person()也可以创建
  p.name = 'why';
  p.eat();
}
class Person {
  String name;
  eat() {
    print('$name在吃东西');
  }
}

构造方法(构造方法不能有返回值,但是工厂构造方法可以有返回值)

  • 普通构造方法
    当类中没有明确指定构造方法时,将默认拥有一个无参的构造方法(上述demo就是默认构造方法);
    也可以根据自己需求定义构造方法,此时默认的构造方法会失效。(且dart不支持函数重载)   
    class Person {
    String name;
    int age;
    Person(String name, int age) {
      this.name = name;
      this.age = age;
    }
    //构造方法的语法糖格式
    Person(this.name, this.age)
    }
    
  • 命名构造方法
    应用场景:实际开发中希望实现更多的构造方法不能创建相同名称的构造方法,即使用命名构造方法。
    class Person {
      String name;
      int age;
    
      Person() {
        name = '';
        age = 0;
      }
       // 命名构造方法
      Person.withArgments(String name, int age) {
        this.name = name;
        this.age = age;
      }
      // 重写自带的方法
      @override
      String toString() {
        return 'name=$name age=$age';
      }
    }
      // 创建对象
    var p1 = new Person();
    print(p1);
    var p2 = new Person.withArgments('why', 18);   //注意区分类函数  
    print(p2);
    
  • 初始化列表
    1.常用于设置final变量的值;
    2.在构造方法体执行之前执行。
    class Person{
      final String name;
      final int age;
      Person(this.name): this.age = age ?? 10 {   //给age设置默认值
      }
    }
    
  • 重定向构造方法(在一个构造方法中调用另外一个构造方法:是在冒号后面使用this调用)
    class Person {
      String name;
      int age;
      Person(this.name, this.age);
      Person.fromName(String name) : this(name, 0);
    }
    
  • 常量构造方法(传入相同值,返回同一个对象) //一般只写一个常量构造函数
    1.默认情况下,创建对象时传入相同的参数创建出来的不是同一个对象;在构造方法前加 const 修饰,可以实现。
    main(List<String> args) {
      var p1 = const Person('why');
      var p2 = const Person('why');
      print(identical(p1, p2)); // true
    }
    class Person {
      final String name;
      const Person(this.name);
    }
    2.注意
      ** 拥有常量构造方法的类中,所有的成员变量必须是 final 修饰的.
      **  为了可以通过常量构造方法,创建出相同的对象,不再使用new关键字,而是使用 const 关键字。
          如果是将结果赋值给const修饰的标识符时,const可以省略.
    
  • 工厂构造方法(多个变量传入,返回同一个对象)
    1.定义:使用factory关键字,用于通过工厂获取对象 
    2.使用:需要手动返回一个对象
     	main(List<String> args) {
     		var p1 = Person('why');
     		var p2 = Person('why');
             print(identical(p1, p2)); // true
     	}
     	class Person {
     		String name;
     		String color;
     		static final Map<String, Person> _namecache = {};
     		static final Map<String, Person> _colorcache = {};
     		factory Person(String name) {
    				if (_namecache.containsKey(name)) {
       			return _cache[name];
     			} else {
       			final p = Person._internal(name,'default');
      			_namecache[name] = p;
       			return p;
     			}
     		}
      		Person._internal(this.name,this.color);
     	}   
    

setter和getter(监控类属性被访问的过程)

main(List<String> args) {
  final d = Dog("黄色");
  d.setColor = "黑色";
  print(d.getColor);
}
class Dog {
  String color;
  String get getColor {
    return color;
  }
  set setColor(String color) {
    this.color = color;
  }
  Dog(this.color);
}

类的继承

1.使用 extends 关键字,字类中使用super来访问父类;
2.父类中的所有成员变量和方法都会被继承,,但是构造方法除外;
  main(List<String> args) {
    var p = new Person();
    p.age = 18;
    p.run();
    print(p.age);
  }
  class Animal {
    int age;
    run() {
      print('在奔跑ing');
    }
  }
  class Person extends Animal {
  }
3.子类可以拥有自己的成员变量, 并且可以对父类的方法进行重写;
  class Person extends Animal {
    String name;
    @override
    run() {
      print('$name在奔跑ing');
    }
  }
4.子类中可以调用父类的构造方法,对某些属性进行初始化;(方式:子类构造方法 :super(父类构造方法参数))
  class Animal {
    int age;
    Animal(this.age);
    run() {
      print('在奔跑ing');
    }
  }
  class Person extends Animal {
    String name;
    Person(String name, int age) : super(age);  //子类的构造函数要去调用父类唯一的构造函数使其初始化。
    @override
    run() {
      print('$name在奔跑ing');
    }
  }

抽象类(定义类)

  没有具体实现的方法(没有方法体),就是抽象方法.
  1.抽象方法,必须存在于抽象类中;
  2.抽象类是使用abstract声明的类;
  3.抽象类不能通过普通构造函数实例化,但是可以通过工厂构造函数实例化;
  4.抽象类中的抽象方法必须被子类实现, 抽象类中的已经被实现方法, 可以不被子类重写
    abstract class Shape {
    getArea();
  }
  class Circle extends Shape {
    double r;
    Circle(this.r);
    @override
    getArea() {
      return r * r * 3.14;
    }
  }
  class Reactangle extends Shape {
    double w;
    double h;
    Reactangle(this.w, this.h);
    @override
    getArea() {
    return w * h;
    }
  }
  5.external关键字可以使得方法的声明和实现分开,使用@patch注解实现。(可以针对不同平台不同实现)

隐式接口

1.应用场景:Dart中没有一个专门的关键字来声明接口,默认情况下,定义的每个类都相当于默认也声明了一个接口,可以由其他的类来实现(因为Dart不支持多继承),在开发中,我们通常将用于给别人实现的类声明为抽象类;
2.注意事项:通过implements实现某个类时,类中所有的方法都必须 被重新实现(无论这个类原来是否已经实现过该方法)。
3.使用: abstract class Runner {
          run();
        }
        abstract class Flyer {
          fly();
        }
       class SuperMan implements Runner, Flyer {
          @override
          run() {
            print('超人在奔跑');
          }
          @override
          fly() {
            print('超人在飞');
          }
        }

Mixin混入(定义类)

1.出现场景:使用implements必须重新实现所有方法,但是某些情况下,某个类希望能直接复用之前类的原有实现方法(不能使用继承,只支持单继承,即复用一个类),即使用mixin混入方式。
2.使用:通过mixin定义一个类,用于被其他类混入使用,通过with关键字进行混入。
      main(List<String> args) {
        var superMan = SuperMain();
        superMan.run();
  		superMan.fly();
	  }
	 mixin Runner {
  		run() {
    	print('在奔跑');
  	    }
	 }
	mixin Flyer {
  		fly() {
    	print('在飞翔');
  		}
	}
	class SuperMain with Runner, Flyer {
	} 

类成员和方法

1.使用:使用static关键字定义:
	main(List<String> args) {
  		var stu = Student();
  		stu.name = 'why';
  		stu.sno = 110;
  		stu.study();
  		Student.time = '早上8点'; // stu.time = '早上9点'; 错误
 ‍‍‍ 	   Student.attendClass();    // stu.attendClass(); 错误
	}
	class Student {
  		String name;
  		int sno;
  		static String time;
  		study() {
    		print('$name在学习');
  		}
  		static attendClass() {
    		print('去上课');
  		}
	}
2.注意:
     实例对象不能访问类成员和类方法

枚举类型(定义类)

  • 定义(表示固定数量的常量值)//通常分用于表示一个变量有几种可能的取值
    使用enum关键字
    main(List<String> args) {
    	print(Colors.red);
    }
    enum Colors {
    	red,
    	green,
    	blue
    }
    
  • 属性
    1.index:表示每个枚举常量的索引,从0开始;
    2.values:包含每个枚举值的list。
    3.使用:
      	main(List<String> args) {
    		  print(Colors.red.index);
    		  print(Colors.green.index);
    		  print(Colors.blue.index);
    		  print(Colors.values);
      	}
      	enum Colors {
    		  red,
    		  green,
    		  blue
      	}
    4.注意:不能子类化、混合或实现枚举;
           不能显式实例化一个枚举。
    

08-泛型

  • List和Map的写法
    List<String> names3 = ['why', 'kobe', 'james', 111]; // 最后一个报错
    Map<String, String> infos2 = {'name': 'why', 'age': 18}; // 18不能放在value中 
    
  • 泛型类定义
    main(List<String> args) {
      Location l2 = Location<int>(10, 20);
      print(l2.x.runtimeType); // int 
      Location l3 = Location<String>('aaa', 'bbb');
      print(l3.x.runtimeType); // String
    }
    class Location<T> {   // 假如希望类型只是num类型:class Location<T extends num>
      T x;
      T y;
      Location(this.x, this.y);
    }
    
  • 泛型方法定义
    main(List<String> args) {
      var names = ['why', 'kobe'];
      var first = getFirst(names);
      print('$first ${first.runtimeType}'); // why String
    }
    T getFirst<T>(List<T> ts) {
      return ts[0];
    }
    

09-库的使用

  • 导入
    1.语法: import '库所在的url'
    2.关于库url形式:
      1)dart标准库:dart:io   dart:html   dart:math
      2)相对路径导入即自己项目中定义的其他dart文件
      3)Pub包管理工具管理的库(我理解的类似于npm):  import 'packsge:flutter/material.dart'
    3.库文件部分内容
      1show:显示某个成员(屏蔽其他)
      import 'lib/student/student.dart' show Student, Person;
      2hide:隐藏某个成员(显示其他)
      import 'lib/student/student.dart' hide Person;
    4.库文件内容与当前文件名字冲突,使用as
      import 'lib/student/student.dart' as Stu;
      Stu.Student s = new Stu.Student();
    
  • 定义
    • library关键字
      library math;
      
    • part关键字(库文件太大,拆分库) //官方不推荐使用
      part "mathUtils.dart";
      part "dateUtils.dart";
      
    • export关键字
      utils.dart文件
      library utils;
      export "mathUtils.dart";
      export "dateUtils.dart";
      
      test_libary.dart文件
      import "lib/utils.dart";
      main(List<String> args) {
      	print(sum(10, 20));
      	print(dateFormat(DateTime.now()));
      }