Dart语言是使用flutter框架开发时候必备的语言,flutter是一个跨平台的框架,一套代码就可以完美实现安卓和ios两个平台,适配也很不错,Dart语言很友好,和java很类似,学习成本不高,推荐学习。
接下来我会通过两篇文章,带大家一起进入Dart之旅。
目录(谁知道简书md自动生成目录方法留言教我,谢谢)
概念
- 万物皆对象,所有的对象都是类的实例,如 数字、函数、null
等也都是对象,所有的对象都继承自 Object 类。 - 在 Dart 1.x 中,强类型 strong mode 是可选的,但在 Dart 2 中,默认就是 strong mode。
- Dart 支持顶级函数,也支持类或对象的静态和实例方法。也可以在函数内部嵌套函数或本地函数;同样支持顶级变量,也支持类或对象的静态变量和实例变量(也被称作字段或属性)
- Dart没有 public、protected、private等关键字,如果一个标识符以 _开头则表示私有
- 没有初始化的变量都会被赋予默认值null
类型:
numbers、strings、bool、list、map、runes、symbol
numbers : 数据类型
- int 取值范围:-2^53 to 2^53
- double
void _numbersDemo() {
int intNum = 12;
double doubleNum = 12.12;
//发现 + 必须都是string类型的才可以,所以使用.toString()
print("int = " + intNum.toString() + " doubule=" + doubleNum.toString());
}
运行结果:int = 12 doubule=12.12
strings : 字符串类型
- Dart 的String 是 UTF-16 编码的一个队列
- '...'、"..." 表示字符串
- '''...'''、"""..."""表示多行字符串
- r'...'、r"..."表示“raw”字符串
void _stringsDemo(){
String string1 = 'rocker1';
String string3 = '''rocker3_1
rocker3_2
rocker3_3''';
String string5Origin = 'rocker56 \nrocker56';
String string5 = r'rocker5 \n rocker5';
print(string1);
print(string3);
print("********");
print(string5Origin);
print(string5);
}
运行结果:
rocker1
rocker3_1
rocker3_2
rocker3_3
********
rocker56
rocker56
rocker5 \n rocker5
bool : 布尔类型
与java类似,Dart 是强 bool 类型检查,只有 bool 类型的值是true 才被认为是 true
List : 列表、数组
具有一系列相同类型的数据被称为 List 对象。
list 对象的第一个元素的位置是0,最后个元素的索引是list.lenght - 1。
void _listDemo(){
var names = new List();
//或者简单的用list来赋值
//List<String> names =["jim","rocker"]
//添加元素
names.add("rocker");
//添加多个元素
names.addAll(["jim","tom","abc","wangxiaojian"]);
//打印数组
print("before names="+names.toString());
//获取list长度
print("length="+names.length.toString());
//删除指定元素,成功返回true,失败返回false
names.remove("jim");
//删除最后一个元素,返回删除的元素 names.removeLast();
//删除指定范围元素,含头不含尾,成功返回null names.removeRange(start,end);
// 删除指定条件的元素,成功返回null
names.removeWhere((item) => item.length >6);
//sort()对元素进行排序,传入一个函数作为参数,return <0表示由小到大, >0表示由大到小
names.sort((a, b) => a.compareTo(b));
print("after names="+names.toString());
}
运行结果:
before names=[rocker, jim, tom, abc, wangxiaojian]
length=5
after names=[abc, rocker, tom]
map:字典
Map 类型将keys 和 values 关联在一起。
keys 和 values 可以是任意类型的对象。
像其它支持Map 的编程语言一样,Map 的 key 必须是唯一的。
void _mapDemo() {
// Map的声明
var person = {
'wangxiaojian': ['15', "pingpang", 'other'],
'xiaoming': ['16', 'basketball'],
'aihua': ['15', 'football']
};
var person1 = new Map();
//指定键值对的参数类型
var dog = new Map<int, String>();
//Map的赋值,中括号中是Key,这里可不是数组
dog[54] = 'wangcai';
print("before dog[54]=" + dog[54] );
//Map中的键值对是唯一的,同Set不同,第二次输入的Key如果存在,Value会覆盖之前的数据
dog[54] = 'tiedan';
print("after dog[54]=" + dog[54] );
//检索Map是否含有某Key
print("does it include dog[54]=" + dog.containsKey(54).toString() );
//删除某个键值对
dog.remove(54);
}
运行结果:
before dog[54]=wangcai
after dog[54]=tiedan
does it include dog[54]=true
runes 类型
runes 是UTF-32字符集的string 对象。
codeUnitAt 和 codeUnit 用来获取UTF-16字符集的字符。
使用runes 来获取UTF-32字符集的字符。
void _testRunes(){
var string = '\u{1f44f}';
print(string);
print(string.codeUnits);
print(string.runes.toList());
Runes input = new Runes(
'\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}');
print(input.toList());
}
运行结果:
👏
[55357, 56399]
[128079]
[9829, 32, 32, 128517, 32, 32, 128526, 32, 32, 128123, 32, 32, 128406, 32, 32, 128077]
Symbol
symbol字面量是编译时常量,在标识符前面加#。如果是动态确定,则使用Symbol构造函数,通过new来实例化。
一般程序中不会使用Symbol类型。
流程控制语句
- if...else
- for
- while do-whild
- break continue
- switch...case
- assert(仅在checked模式有效)
函数(Functions)
函数都有返回值,如果没有指定函数返回值,则默认返回值是null,没有返回值的函数,系统会在最后添加隐式的return 语句。
函数可以有两种类型的参数:
- 必须的:必须参数放在参数列表前面。
- 可选的:可选参数放在必须参数后面。
可选参数
可以通过名字或位置指定可选参数,但是两个不能同时存在。
- 命名可选参数使用{},默认值使用冒号:,调用时需指明参数名,与顺序无关。
void _testOptionParams(String name,{age:0,interest:null}){
print("name="+name+" age="+age.toString()+" interest="+interest.toString());
}
void main() {
_testOptionParams("wangxiaojian",age: 30,interest:"pingpang");
}
运行结果:
name=wangxiaojian age=30 interest=pingpang
- 位置可选参数使用[],默认值使用等号=,调用时参数按顺序赋值。
void _testOptionParams(String name,[age=0,interest=null]){
print("name="+name+" age="+age.toString()+" interest="+interest.toString());
}
void main() {
_testOptionParams("wangxiaojian","pingpang");
}
运行结果:
name=wangxiaojian age=pingpang interest=null
操作符
is is!操作符判断对象是否为指定类型,如numbers、string等。
as用来类型断言。
异常
throw
- 抛出固定类型的异常:
throw new FormatException('Expected at least 1 section');
- 抛出任意类型的异常:
throw 'Out of llamas!';
- 因为抛出异常属于表达式,可以将throw语句放在=>语句中,或者其它可以出现表达式的地方:
distanceTo(Point other) =>
throw new UnimplementedError();
catch
将可能出现异常的代码放置到try语句中,使用catch来处理异常。
可以向catch()传递1个或2个参数。第一个参数表示:捕获的异常的具体信息,第二个参数表示:异常的堆栈跟踪(stack trace)。
void _testException({demo:100}){
try{
var i = 2/0;
print(i.toString()+" "+demo);
}catch(e,s){
print('Exception details:\n $e');
print('Stack trace:\n $s');
}
}
void main() {
_testException();
}
运行结果:
Exception details:
type 'int' is not a subtype of type 'String'
Stack trace:
#0 _testException (file:///Users/wangxiaojian/Documents/androidCode/flutter_rong_app/test/widget_test.dart:99:28)
#1 main (file:///Users/wangxiaojian/Documents/androidCode/flutter_rong_app/test/widget_test.dart:13:3)
#2 _rootRun (dart:async/zone.dart:1126:13)
#3 _CustomZone.run (dart:async/zone.dart:1023:19)
#4 runZoned (dart:async/zone.dart:1501:17)
#5 Declarer.declare (package:test/src/backend/declarer.dart:121:22)
#6 RemoteListener.start.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test/src/runner/remote_listener.dart:118:26)
......
rethrow、finally
rethrow语句用来处理一个异常,同时希望这个异常能够被其它调用的部分使用。
finally用来执行那些无论异常是否发生都执行的操作。
void _testFinally({demo:100}) {
try {
print(" "+demo);
} catch (e) {
print('2');
rethrow;
}
}
void main() {
try {
_testFinally();
} catch (e) {
print('3');
} finally {
print('4'); // 即使没有rethrow最终都会执行到
}
}
运行结果:
2
3
4
类
Dart 是一种面向对象的语言,并且支持基于mixin的继承方式:一个类可以继承自多个父类。
使用new语句来构造一个类。构造函数的名字可能是ClassName,也可以是ClassName.identifier。例如:
class Person{
var _name;
Person(){}
Person.initName(this._name);
}
void main() {
//使用new初始化对象
var person = new Person();
//使用ClassName.identifier初始化对象
var person1 = Person.initName("xiaojian");
}
- 使用.来调用实例的变量或者方法。
- 使用 ?. 来避免左边操作数为null引发异常。
- 使用const替代new来创建编译时的常量构造函数。
- 两个使用const构建的同一个构造函数,实例相等。
- 获取对象的运行时类型使用:o.runtimeType。
实例化变量
在类定义中,所有没有初始化的变量都会被初始化为null。
所有实例变量会生成一个隐式的getter方法,不是final或const的实例变量也会生成一个隐式的setter方法。
构造函数
构造函数不能被继承,如果不显式提供子类的构造函数,系统就提供默认的构造函数。
class Base{
//父类构造函数
Base(){
print("base init");
}
}
class Person extends Base{
var _name;
var _interest;
//默认构造函数
Person(){}
//命名构造函数
Person.initNameAndAge(this._name,this._interest);
//重定向构造函数
Person.initName(name):this.initNameAndAge(name,"无");
}
class RockerAddress {
final num lat;
final num lng;
//常量构造函数
const RockerAddress(this.lat, this.lng);
static final RockerAddress address =
const RockerAddress(39.19, 141.32);
}
class Logger {
final String name;
bool isDebug = false;
// 命名构造函数
Logger._internal(this.name);
// 缓存保存对象
static final Map<String, Logger> _cache = <String, Logger>{};
//工厂构造函数
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = new Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
void log(String msg) {
if (!isDebug) {
print(msg);
}
}
}
void main() {
//person demo
var person = new Person();
var person1 = Person.initNameAndAge("xiaojian","pingpang");
var person2 = Person.initName("jim");
//rockaddress demo
var rockaddress = RockerAddress.address;
print(rockaddress.toString());
//logger demo
var p1 = new Logger("1");
p1.log("2");
var p2 = new Logger('22');
p2.log('3');
// 相同对象直接访问缓存
var p3 = new Logger('1');
print(p1==p3);
}
运行结果:
base init
base init
base init
Instance of 'RockerAddress'
2
3
true
- 默认构造函数
没有声明构造函数,dart会提供默认构造函数。默认构造函数没有参数,并且调用父类的无参数构造函数。
- 命名构造函数
使用命名构造函数可以实现一个类多个构造函数。构造函数不能被继承,父类中的命名构造函数不能被子类继承。如果想要子类也拥有一个父类一样名字的构造函数,必须在子类实现这个构造函数。
- 重定向构造函数
有时候构造函数的目的只是重定向到该类的另一个构造函数。重定向构造函数没有函数体,使用冒号:分隔。
- 常量构造函数
如果类的对象不会发生变化,可以构造一个编译时的常量构造函数。定义格式如下:
定义所有的实例变量是final。
使用const声明构造函数。
- 工厂构造函数
当实例化了一个构造函数后,不想每次都创建该类的一个新的实例的时候使用factory关键字,定义工厂构造函数,从缓存中返回一个实例,或返回一个子类型的实例。
工厂构造函数不能访问this
- 初始化列表
除了调用父类的构造函数,也可以通过初始化列表 在子类的构造函数体前(大括号前)来初始化实例的变量值,使用逗号,分隔。如下所示:
一个初始化器的右边不能访问this。
class Point {
num x;
num y;
Point(this.x, this.y);
// 在构造函数体前 初始化列表 设置实例变量
Point.fromJson(Map jsonMap)
: x = jsonMap['x'],
y = jsonMap['y'] {
print('In Point.fromJson(): ($x, $y)');
}
}
- 调用父类的非默认构造函数
默认情况下,子类调用父类的无参数的非命名构造函数。父类的构造函数会在子类的构造函数体之前(大括号前)调用。如果也使用了初始化列表 ,则会先执行初始化列表 中的内容,即下面的顺序:
初始化列表
父类无参数构造函数
主类无参数构造函数
如果父类不显式提供无参的非命名构造函数,在子类中必须手动调用父类的一个构造函数。在子类构造函数名后,大括号{前,使用super.调用父类的构造函数,中间使用:分割。
父类构造函数参数不能访问this,例如,参数可以调用静态方法但不能调用实例方法。
class Person {
String firstName;
Person.fromJson(Map data) {
print('in Person');
}
}
class Employee extends Person {
// 父类没有无参数的非命名构造函数,必须手动调用一个构造函数 super.fromJson(data)
Employee.fromJson(Map data) : super.fromJson(data) {
print('in Employee');
}
}
main() {
var emp = new Employee.fromJson({});
// Prints:
// in Person
// in Employee
if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}
(emp as Person).firstName = 'Bob';
}
抽象类
使用abstract关键字定义一个抽象类,抽象类不能实例化,抽象类通常用来定义接口,抽象类通常会包含一些抽象的方法。
//定义抽象类
abstract class Base{
//定义抽象方法
void doSomething();
}
方法
- 实例方法
Getters and setters
get()和set()方法是Dart 语言提供的专门用来读取和写入对象的属性的方法。每一个类的实例变量都有一个隐式的getter和可能的setter(如果字段为final或const,只有getter)。
像++之类的操作符,无论是否明确定义了getter,都按预期的方式工作。为了避免意想不到的副作用,操作符调用getter一次,将其值保存在临时变量中。
class Rectangle {
num left;
num top;
num width;
num height;
Rectangle(this.left, this.top, this.width, this.height);
// Define two calculated properties: right and bottom.
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}
void main() {
var rect = new Rectangle(3, 4, 20, 15);
print(rect.left == 3);
rect.right = 12;
print(rect.left == -8);
}
运行结果:
true
true
- 抽象方法
抽象方法,只有函数声明,没有函数体,作为接口在其他类中实现。调用抽象方法会导致运行时错误。如果在不是抽象类中定义抽象方法会引发warning,但不会阻止实例化。
abstract class Base{
//定义抽象方法
void doSomething();
}
class Child extends Base{
@override
void doSomething() {
print("test");
}
}
- 静态变量和方法
使用static关键字来实现类范围内变量和方法。
静态方法不能被实例调用,不能访问this,只能被类名调用。
对于通用或广泛使用的工具和功能,应该使用顶级函数,而不是静态方法。
可以使用静态方法作为编译时常量。例如,可以给一个常量构造函数,传递一个静态方法作为参数。
class SecurityUtil{
//静态变量
static const key = "Encryption key is generally static";
//静态方法
static String md5(String){
}
}
隐式接口
每一个类都隐式的定义一个接口,这个接口包含了这个类的所有实例成员和它实现的所有接口。
一个类可以实现一个或多个(用,隔开)接口,通过implements关键字。
class Person{
final _name;
Person(this._name);
String greet(who) => 'hello,$who,i am $_name';
}
//implements关键字
class Imposter implements Person {
final _name = '';
String greet(who) => 'hi $who.do you know who i am.';
}
greetBob(Person p) => p.greet('jim');
void main() {
print(greetBob(new Person('wangxiaojian')));
print(greetBob(new Imposter()));
}
运行结果:
hello,jim,i am wangxiaojian
hi jim.do you know who i am.
继承
使用extends来创造子类,使用super来指向父类。
子类可以重载实例方法,使用@override注解重载方法。
class Person {
final _name;
Person(this._name);
void printName() {
print("base name = $_name");
}
}
class Imposter extends Person {
Imposter(name) : super(name);
@override
void printName() {
print("child name = $_name");
}
}
void main() {
new Person("wangxiaojian").printName();
new Imposter("wangxiaojian").printName();
}
运行结果:
base name = wangxiaojian
child name = wangxiaojian
枚举类型
- 枚举类型是一种特殊的类,通常用来表示一组固定数字的常量值。
使用enum关键字声明枚举类型。 - 每个枚举类型都有一个index,返回以0开始的位置索引,每次加1
- 使用values关键字获取枚举的所有值,返回一个列表。
- 不能继承,mixin,或实现一个枚举;不能显式的实例化一个枚举。
enum Week {
Monday,
Tuesday,
wednesday,
Thursday,
Friday,
Saturday,
Sunday
}
void main() {
print(Week.Monday.index ==0);
print(Week.Tuesday.index ==1);
print(Week.wednesday.index ==4);
}
运行结果:
true
true
false
[Week.Monday, Week.Tuesday, Week.wednesday, Week.Thursday, Week.Friday, Week.Saturday, Week.Sunday]
minxins
mixins是一种在多个类层次结构中,重用一个类的代码的方法。使用with关键字后跟多个mixin名。
class Speak{
void speak(){
print(this.runtimeType.toString()+" speak");
}
}
class Walk{
void walk(){
print(this.runtimeType.toString()+ " walk");
}
}
class Base{
}
class Person extends Base with Speak,Walk{
}
class Animal extends Base with Speak,Walk{
}
void main() {
new Person().speak();
new Animal().walk();
}
运行结果:
Person speak
Animal walk