Flutter-Dart语法一

1.Dart语言的优势

1)Dart 有两种运行方式,静态编译(AOT Ahead Of Time)和动态解释(JIT Just In Time)。
AOT:静态编译的程序在执行前全部被翻译为机器码。在Flutter 的Release模式下使用,闭了所有断言,尽可能多地去掉了调试信息,关闭了所有调试工具。为快速启动,快速执行,包大小做了优化。禁止了所有调试辅助手段,服务扩展。

JIT:动态解释,就是边翻译边运行,每次改都不需要再编译成字节码,节省了大量时间,缩短产品的开发周期。对应Flutter的Debug模式,此模式下打开了断言,包括所有的调试信息,服务扩展和Observatory等调试辅助,支持广受欢迎的亚秒级有状态的hot reload。

2)易于移植,Dart可编译成ARM和X86代码,这样Dart移动应用程序可以在iOS、Android和其他地方运行。

3)Dart可以在没有锁的情况下进行对象分配和垃圾回收。像JavaScript一样,Dart避免了抢占式调度和共享内存,大多数支持并发执行线程的计算机语言(Java、Kotlin、Swift)都使用抢占式来切换线程,抢占就会产生竞态条件,竞态条件很有可能导致严重错误,如应用程序崩溃导致数据丢失等等,一般解决竞态条件使用锁来保护,但是锁本身可能导致卡顿,也有可能产生死锁,而Dart针对这个问题,采取了isolate方法来解决,Dart中的线程称为isolate,不共享内存。那么Dart是单线程的,意味根本不允许抢占。单线程有助于开发者确保关键功能(动画和转场)完成而无需抢占。 Flutter应用程序被编译为本地代码,因此它们不需要再领域之间建立缓慢的桥梁,所以启动速度快得多。

2.Dart语言的特性

1)在Dart中,一切都是对象,每个对象都是一个类的实例,所有对象都继承自Object。

2)没有赋初值的变量都会有默认值null。

2)Dart不具备关键字public、protected、private。如果一个标识符以下划线_开始,那么它和它的库都是私有的。

3)Dart支持泛型类型,如List<int>(整数列表)或List<dynamic>(任何类型的对象列表)。

4)Dart是强类型语言,但可以用var或者dynamic来声明一个变量,Dart会自动推断其数据类型,dynamic类似C#。

5)Dart中的类和接口都是统一的,类即是接口,你可以继承一个类,也可以实现一个类,自然也包含了良好的面向对象和并发编程的支持。

6)Dart支持顶级方法,如main方法,同时还支持在类中定义函数(静态函数和实例函数),还可以在方法中定义方法,Dart支持顶层变量,也支持类变量或对象变量。

3.Dart 语法

1)注释


// 这是注释符号,还可以用/*...*/这是很多语言的注释符号

2)数据类型

  • num:包含两种子类型,int和double类型。使用num声明的变量,可以在这两种子类型之间随意的转换,但使用int或者double明确的声明,那就不能转换了。
//整形,其取值通常位于-2的53次方到2的53之间。
  num x = 777;
  //浮点数 64位
  x = 777.7;

  int y = 777;
  y = 777.7;       //这一行编译器会报错,因为将int型的数据转为double型

  double n = 77,7;
  d = 77;          //这个地方会报错,因为将double型的数据转为int型

  • String:字符串是UTF-16编码的字符序列,可以使用单引号或者双引号来创建字符串。单引号和双引号之间可以相互嵌套。
字符串嵌套:

  String m_str1 = '单引号中的"双引号"字符串';
  String m_str2 = "双引号中的'单引号'字符串";

  print(m_str1);        //输出:单引号中的"双引号"字符串
  print(m_str2);        //输出:双引号中的'单引号'字符串

字符串拼接:

  // 使用空格拼接,多个空格也是可以地
  String m_str1 = '单引号字符串' '拼接'     '---';
  print(m_str1);       //输出:单引号字符串拼接---

  // $可以获取字符串的内容,用${表达式}可以将表达式的值放入字符串中,使用${表达式}也可以使用字符串拼接

  bool flag = true;
  String m_str1 = "字符串";
  print("看看这个值:${m_str1} ""看看这个值flag:${flag}"); 
  //输出:看看这个值:字符串 看看这个值flag:true

  //使用$+字符串
  String name = "knight";
  print("$name" + "CTO");     //输出:knightCTO;

  • bool:布尔值只有 true 和 false,默认值是null。
当Dart需要一个布尔值,只有true对象才被认为是true,所有其他值都是false。
String name ="knight";
  //报错 因为name不是bool类型
  if(name){
    print(name);

  }

  • enum:枚举
枚举类型是一种特殊的类,通常用来表示相同类型的一组常量。使用关键字enum定义枚举。
枚举类型不能定义在类中,枚举的每一个值都有一个index属性,index从0开始计数。枚举不能被继承,不能创建实例。

enum Animal {
  cat,
  dog,
  bird
}

  • list:在Dart中数组就是List对象。
list 创建:

//创建一个int类型的list 并赋值为0,1,2,3,4
  List list =  [0,1,2,3,4];

  //使用构建的方式创建list
  List list1 = new List();

  //创建一个常量的List,不可以改变的List
  List list2 = const[0,1,2,3];

  //增加泛型
  List list3 = new List<String>();

  //创建固定的长度的数组列表,不能移除或者增加
  List list4 = new List(5);

  //创建包含所有以下元素的可改变的长度列表
  List list5 = new List.from([0,1,2,3]);

list 操作:

 //在列表中存放不同类型的对象
  List list = [1,2,3,false,"Kinght"];
  print(list);          //输出:[1, 2, 3, false, Kinght]

  //在列表中添加元素
  list.add(7);
  print(list);          //输出:[1, 2, 3, false, Kinght, 7]

  //修改列表下标为1的值
  list[1] = "paul";
  print(list);          //输出:[1, paul, 3, false, Kinght, 7]

  //移除列表的指定值得的元素
  list.remove("paul");
  print(list);          //输出:[1, 3, false, Kinght, 7]

  //移除列表指定下标下的元素
  list.removeAt(0);
  print(list);          //输出:[3, false, Kinght, 7]

  //获取列表的长度
  print(list.length);   //输出:4

  //向列表中的指定位置添加元素 在第0的位置上插入Android
  list.insert(0, "Android");
  print(list);          //输出:[Android, 3, false, Kinght, 7]

  //判断数组中是否有某元素
  print(list.indexOf("Android")); //这里存在,输出对应的下标,如果没有则输出-1

  //排序
  List list1 = [3,1,2,6,7];
  // 根据语法提示: List.sort([(int, int) → int compare]) → void
  list1.sort((a,b) => a.compareTo(b));
  print(list1);           //输出:[1, 2, 3, 6, 7]

  • Map:一个键值对相关的对象,键和值可以是任何类型的对象。当Map的Key没有指定类型时,Key类型不一致也不会报错。但每个键只出现一次,而一个值则可以出现多次,而且value可以为空字符串或者为null。
Map 创建:

//1.通过构建器来创建Map
  Map map1 = new Map();
  //添加值 赋值
  map1["one"] = 'Android';
  map1["two"] = 'IOS';
  map1["three"] = 'Flutter';
  print(map1);              //输出:{one: Android, two: IOS, three: Flutter}

  //2.通过复制的形式
  Map map2 = Map.of(map1);
  print(map2);              //输出:{one: Android, two: IOS, three: Flutter}

  //3.跟上面形式一样  Object.fromEntries() 函数传入一个键值对的列表,并返回一个带有这些键值对的新对象。
  // 这个迭代参数应该是一个能够实现@iterator方法的的对象,返回一个迭代器对象。它
  // 生成一个具有两个元素的类似数组的对象,第一个元素是将用作属性键的值,第二个元素是与该属性键关联的值。
  Map map3 = Map.fromEntries(map1.entries);
  print(map3);

  //4.直接声明,直接赋值key为String类型的map
  Map map4 = {'one':'Android',
    'two':'IOS',
    'three':'Flutter'};
  print(map4);              //输出:{one: Android, two: IOS, three: Flutter}

  //5.创建一个空的Map
  Map map5 = Map.identity();
  print(map5);              //输出:{}


  //6.创建不可变的Map
  Map map6 = const {'one':'Android','two':'IOS','three':'flutter'};
  print(map6);              //输出:{one: Android, two: IOS, three: flutter}

  //7.在目标的map6创建(复制)新的不可修改map7
  Map map7 = Map.unmodifiable(map6);
  print(map7);              //输出:{one: Android, two: IOS, three: flutter}

  //8.创建key为int值得map
  Map map8 = {1:'Android',
    2:'IOS',
    3:'Flutter'};
  print(map8);              //输出:{1: Android, 2: IOS, 3: Flutter}

  //9.根据list所提供的key value来创建map
  List<String> keys = ['one','two'];
  List<String> values = ['Android','IOS'];
  Map map9 = Map.fromIterables(keys, values);
  print(map9);               //输出:{one: Android, two: IOS}
  
   //通过构建器来创建Map
   Map map10 = new Map();
   //添加值 赋值 赋值不同类型的Map
   map10["one"] = 'Android';
   map10["two"] = 'IOS';
   map10["three"] = 'Flutter';
   map10[4] = 'RN';
   print(map10);              //输出:{one: Android, two: IOS, three: Flutter, 4: RN}


Map 操作:

 //创建Map key是int类型,value是String类型
   var  map1 = new Map<int,String>();

   //对Map第一个位置赋值,中括号是key
   map1[0] = 'Android';
   //对Map第二个位置赋值
   map1[1] = 'IOS';
   //对Map第三个值赋值
   map1[2] = 'flutter';
   //对Map赋空值
   map1[3] = null;
   //因为Map中的键值是唯一的,当第二次输入的key如果存在,Value会覆盖之前
   map1[2] = 'RN';
   print(map1);                //{0: Android, 1: IOS, 2: RN, 3: null}

   //获取Map的长度
   print(map1.length);         //输出:4

   //判断Map是否为空
   print(map1.isNotEmpty);     //输出结果:true

   //判断Map是否不为空
   print(map1.isEmpty);        //输出结果:false

   //检索Map是否含有某个Key
   print(map1.containsKey(1)); //输出:true

   //检索Map是否包含某个Value
   print(map1.containsValue('Android'));  //输出:true

   //删除某个键值对
   map1.remove(0);
   print(map1);                //输出:{1: IOS, 2: RN, 3: null}

   //获取所有的key
   print(map1.keys);           //输出:(1, 2, 3)

   //获取所有的values
   print(map1.values);         //输出:(IOS, RN, null)

   //循环打印
   /*
     key:1, value:IOS
     key:2, value:RN
     key:3, value:null

    */
     map1.forEach((key,value) {
     print("key:${key}, value:${value}");
   });

  • var:它仅仅只是一个语法, var本身并不是一种类型。var声明的变量在赋值的那一刻,就已经决定了它是什么类型。
// 编译报错

var a = 1;

a = "Test";

  • object :能够表示任意类型。因为所有的类型都派生自object。
object a = 1;

a = "Test";

  • dynamic:也可以表示任意类型。但确定具体类型是在运行时。
// 下面代码能够通过编译,但是会在运行时报错。
dynamic a = "test";

a++;

  • final:修饰的变量,必须在定义时将其初始化,其值在初始化后不可改变。无法在编译时(运行之前)知道这个变量的值。
final name = 'Bob';   

name = 'job'; 
//运行出错,因为final修饰的变量不能调用其setter方法,即:不能设值

final baz =  [1];
 // baz=[1,2,3,4]; //出错 此调用修改了变量的实例 即:[1] 和[1,2,3,4]是不同的对象
baz[0]=2;     //正常执行,只修改了变量引用对象的成员变量的值
print(baz); 


  • const:用来定义常量,只能被设一次值,在声明处赋值,且值必须为编译时常量。与final的区别是const所修饰的是编译时常量,我们在编译时就已经知道了它的值。
 // 定义常量值
const bar = 1000000;      
// bar =13;  
// 出现异常,const修饰的变量不能调用setter方法,即:不能设值,只能在声明处设值

const atm = 1.01325 * bar; 
// 值的表达式中的变量必须是编译时常量(bar);


// 修饰常量值

 var foo = const [];  
    foo = [1,2,1];  
    /*此部分代码的重点在于var foo , 一个正常变量可以随意赋值或更改,重点不在const [],
      所以不要纠结const []是不可变的。[]和[1,2,1]是不同的对象*/
    print(foo); 

3)运算操作符
运算符:+、-、*、/、~/、%
常用属性:isNaN、isEven、isOdd
常用方法:abs()、round()、floor()、ceil()、toInt()、toDouble()
级联操作符:..

  int i =7;
  double d = 10.1;

  print(i / d);               //0.6930693069306931
  print(i ~/ d);              //0   这个操作是取整 就是得出商

  print(i.isOdd);             // 判断是奇数
  print(i.isEven);            // 判断是偶数

  //求绝对值
  var x5 = (-7).abs();
  print(x5 == 7);

  //四舍五入1
  var x6 = (7.7).round();
  print(x6);                   //输出8
  
   //求小于它的最大整数
  var x8 = (7.7).floor();
  print(x8);                   //输出7

  //求大于它的最小整数
  var x9 = (7.7).ceil();
  print(x9);                   //输出8


级联操作符:(..)可以在同一对象上连续调用多个函数以及访问成员变量。使用级联操作符可以避免创建临时变量,并
且写出来的代码看起来更加流畅。

querySelector('#button') // Get an object.
  ..text = 'Confirm'   // Use its members.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'));

相当于:
var button = querySelector('#button');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));

注意:下面代码是不合法
var sb = new StringBuffer();
sb.write('foo')..write('bar');

sb.write函数返回一个void,无法再void上使用级联操作符。注意级联语法不是操作符,只是语法!
4.流程控制
1) if else
if (isRaining()) {//条件语句
  you.bringRainCoat();//内容体
} else if (isSnowing()) {//条件语句
  you.wearJacket();//内容体
} else {
  car.putTopDown();//内容体
}

2)for循环

var message = new StringBuffer("Dart is fun");
for (var i = 0; i < 5; i++) {
  message.write('!');
}

//使用foreach循环 list 和 Set都可以用这种方式
List numbers = [1,2,3,4,5,6,7,8,9,10];
numbers.foreach((number)=> print(number));

//使用for in循环,一般List和Set都是用这种方式
List numbers = [1,2,3,4,5,6,7,8,9,10];
for(var number in numbers){
     print(number);
}

3)While and do-while

//判断条件
while (!isDone()) {
  //内容
  doSomething();
}


do {
  printLine();//内容体
} while (!atEndOfPage());//条件判断

4)Switch and case

var command = 'OPEN';
switch (command) {
  case 'CLOSED':
    executeClosed();
    break;
  case 'PENDING':
    executePending();
    break;
  case 'OPEN':
    executeOpen();
    break;
  default:
    executeUnknown();

5)用break来终止循环,使用continue来开始下一次循环。

5.function 函数

Dart是一个真正的面向对象语言,方法也是对象并且具有一种类型,Function。这意味着,方法可以赋值给变量,也可以当做其他方法的参数。所有的函数都返回一个值。如果没有指定返回值,则默认把语句return null;作为函数的最后一个语句执行。


1.定义一个方法 判断列表对应下标是否为null

bool isNoble(int atomicNumber) {
  return list[atomicNumber] != null;
}

2.不指定返回值类型的函数
  我们可以不指定返回值类型,这样的函数返回值默认为Object,也就是说你可以返回任意类型

isNoble(int atomicNumber) {
  return list[atomicNumber] != null;
}

3.缩略写法
 对于只有一个表达式的方法,可以选择使用缩写语法来定义

bool isNoble(int atomicNumber) => list[atomicNumber] != null;
=> expr是语法{return expr;}形式的缩写。=>形式也称为胖箭头语法。注意:在箭头(=>)和冒号(;)之间只能使用一个表达式,不能使用语句。

4.必须参数和可选参数
可选参数,主要被{}或者[]指定,{}是命名参数,[]是位置参数,二者不能同时使用。
在定义方法的时候,可以使用=来定义可选参数的默认值。默认值只能是编译时常量。如果没有提供默认值,则默认值为null。

可选命名参数方法:
void enableFlags({bool bold = false,bool hidden = false}){

}

//调用方法 没有传hidden的值,那默认值就是false
enableFlags(bold:true);

可选位置参数方法:
//定义一个方法 这个方法位置可选位置参数device的默认参数是carrier pigon
//也就是当调用这个方法,没有传这个参数时,这个参数会取默认值
String say(String from, String msg,
    [String device = 'carrier pigeon', String mood]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  if (mood != null) {
    result = '$result (in a $mood mood)';
  }
  return result;
}

//调用上面的方法
assert(say('Bob', 'Howdy') ==
'Bob says Howdy with a carrier pigeon');

List和Map 作参数

//List和Map都取了默认值
void doStuff(
    {List<int> list = const [1, 2, 3],
      Map<String, String> gifts = const {
        'first': 'paper',
        'second': 'cotton',
        'third': 'leather'
      }}) {
  print('list:  $list');
  print('gifts: $gifts');
}


5.匿名函数
    
函数格式:
([[Type] param1[, …]]) { 
  codeBlock; 
};


// 定义了一个参数为i(该参数没有指定类型)的匿名函数。 list中的每个元素都会调用这个函数打印出来。
var list = ['张无忌', '风清扬', '张三丰', '独孤求败', '萧峰'];
list.forEach((i) {
    print(list.indexOf(i).toString() + ': ' + i);
  });

6.闭包

个闭包是一个方法对象,不管该对象在何处被调用,该对象都可以访问其作用域内的变量,方法可以封闭定义到其作用域内的变量。

Function makeAdder(num addBy) {
  return (num i) => addBy + i;
}

main() {
  //相当于 add2 = (num i) => 2 + i;
  var add2 = makeAdder(2);

  // 相当于 add4 = (num i) => 4 + i;
  var add4 = makeAdder(4);

  assert(add2(3) == 5);
  assert(add4(3) == 7);
}

6.Assert(断言)

如果条件表达式结果不满足需要,则可以使用assert语句来打断代码的执行。

/ 确保变量是非空值 
assert(text != null);
// 确保值是小于100
assert(number < 100);
// 确保这是一个 https 地址
assert(urlString.startsWith('https'));

assert方法参数可以为任何布尔值的表达式或者方法。如果返回的值为true,断言执行通过,执行结束。如果返回值为false,断言执行失败,会抛出异常,断言只有在debug模式运行有效,如果生产模式运行,则断言不会执行。

7.Exceptions(异常)
1)throw

thow new FormatException('Expected at least 1 section');

// 可以抛出任意对象
throw 'Out of llamas!';

2)try catch finally

可以使用on或者catch来声明捕获语句,也可以同时使用。使用on来指定异常类型,使用catch来捕获异常对象。函数catch()可以带有一个或者两个参数,第一个参数为抛出的异常对象,第二个为堆栈信息。
  ...
} on Exception catch (e) {
  print('Exception details:\n $e');
} catch (e, s) {
  print('Exception details:\n $e');
  print('Stack trace:\n $s');
}

finally :无论是否抛出异常,要确保某些代码都要执行,可以使用finally语句来实现。

try {
  breedMoreLlamas();
} catch(e) {
  print('Error: $e');  // 优先处理异常
} finally {
  cleanLlamaStalls();  // 然后再执行
}

如果没有catch语句来捕获异常,则在执行完finally语句后,异常被抛出了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,302评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,563评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,433评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,628评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,467评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,354评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,777评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,419评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,725评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,768评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,543评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,387评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,794评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,032评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,305评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,741评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,946评论 2 336