序
这一切的开始,都是由于电饭锅预约功能的缺失,而激发了自己写一个世界上最简单的 app 的想法——大米粥计时器。
用过电饭锅的人都知道,自带一个定时预约功能,比如,今天晚上9点26分把大米水都按一定比例准备好放到电饭锅里,计划明天早上7点13分做熟能吃。那么,按照现在电饭锅的预约功能,需要自己算剩余时间为:9小时47分,然后输入给电饭锅。
但是问题来了,每天这么算,头疼,只要算错了,提前了还好,晚的话,就吃不上大米粥。之前小米出过一款电饭锅,直接输入07:13即可自动算时间,但是几年后的今天,小米把这个功能去掉了。没办法,只能自己想办法。纠结了好久 uniapp,react native,flutter,最终选择了我认为最有未来的 flutter,因为他有自己的渲染引擎,一处开发所有端的样式都能保持一致,同时又保证了性能的丝滑。
语法对比
dart 语法与 ts 语法简单是太像了,像到我感觉是不是谷歌曾经想通过dart 来取代 js 了?
定义变量+赋值+定义函数
- ts
let num: number = 10;
function add(x: number, y: number): number {
return x + y;
}
- dart
void main(){
int num = 10;
num = 100;
num = "100";❌//报错,因为类型错误
num = 100.0;❌//报错,因为双精度不能赋值给整型
double c = 1.0;
c = 10;✅//整型可以赋值给双精度
var d = 1;
d = 2;
d = "2";❌//报错,因为类型错误
const f = 1;
f = 2;//报错,不能再赋值
var a = 1;
const x = 1; // 声明一个编译时常量 x,并赋值为 1,const 可以提高程序性能
const y = x + 2; // 因为 x 是常量,所以加2后依然是常量没问题
const z = y + a;❌//报错,因为a不是一个固定的值,所以不能用 const 定义
final w = y + a;✅//通过 final 来定义
final m = 1; // 虽然这样没问题,但是推荐使用 const 定义
final n = m + 2; // 虽然这样没问题,但是推荐使用 const 定义
}
int add(int x, int y) {
return x + y;
}
箭头函数 + 模板字符串
- ts
const sum = (x: number): number => x * 2;
const sum = (x: number): number => {
const y = 10;
return x + y;
};
function greet(name: string): string {
return `Hello, ${name}`;//ts 里叫模板字符串
}
- dart
int sum(int x) => x * 2;//箭头函数只接受单独一条语句,不支持花括号,与 ts 不同。
//下面是错误示范
int sum(int x) => {❌//多条语句不支持,需要使用普通方式定义函数
int y = 10;
return x + y;
}
int sum(int x) {✅//其实比 ts更方便,因为 ts 普通函数需要 function定义
int y = 10;
return x + y;
}
String greet(String name) {//dart 里,这个叫 「字符串插值」
return 'Hello, $name';
}
String greet(String a1) {//dart里模板字符串,通过三个单引号或三个双引号来实现
return '''Hello, $a1
Morning
ok
''';
可选参数
- ts
function printSomething(x: number, y?: number) {
if (y) {
console.log(`${x} ${y}`);
} else {
console.log(x);
}
}
printName(1); // 输出 1
printName(1, 2); // 输出 1 2
- dart 里可选参数分为
- 命名可选参数,使用{ }包裹参数
- 位置可选参数,使用[ ]包裹参数
//下面是可选命名参数 有默认值
int printSomething(int x, {int y = 0}) {
if (y > 0) {
print('$x $y');
} else {
print(x);
}
return 1;
}
//下面是可选位置参数 有默认值
int printSomethingTwo(int x, [int y = 0]) {
if (y > 0) {
print('$x $y');
} else {
print(x);
}
return 1;
}
//下面是可选命名参数 无默认值
int printSomethingThree(int x, {int? y}) {
if (y != null && y > 0) {
print('$x $y');
} else {
print(x);
}
return 1;
}
//下面是可选位置参数 无默认值
int printSomethingFour(int x, [int? y]) {
if (y != null && y > 0) {
print('$x $y');
} else {
print(x);
}
return 1;
}
void main() {
printSomething(1); // 输出 1
printSomething(1, y: 2); // 输出 1 2
printSomethingTwo(1); //输出 1
printSomethingTwo(1, 2); //输出 1 2
printSomethingThree(1); //输出 1
printSomethingThree(1, y: 2); //输出 1 2
printSomethingFour(1); //输出 1
printSomethingFour(1, 2); //输出 1 2
}
- 结论:
- 只要可选参数,必须有默认值,如果不想给默认值,类型后面要加问号,同时逻辑上要进行非null 判断
- 只要是用 {} 包裹的参数,在函数调用时,必须用 参数名:参数值 的方式进行传值
泛型
- ts
function identity<T>(arg: T): T {
return arg;
}
const arr: number[] = [1, 2, 3];//这里的 number[]可能省略,能推荐出来
const result = identity<number[]>(arr); // result 的类型是 number[]
- dart
T identity<T>(T arg) {
return arg;
}
List<int> arr = [1, 2, 3];
var result = identity<List<int>>(arr); // result 的类型是 List<int>
面向对象,继承
- ts
class Animal {
private name: string;
constructor(name: string) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
constructor(name: string) {
super(name);
}
speak() {
console.log(`${this.name} barks.`);
}
}
const dog = new Dog();
dog.speak();
- dart
class Animal {
String name;
Animal(this.name);
void speak() {
print('$name makes a noise.');
}
}
class Dog extends Animal {
Dog(String name) : super(name);
@override
void speak() {
super.speak(); //不是必须的
print('$name barks.');
}
}
void main() {
Dog dog = Dog("John");//创建实例不需要 new 关键字
dog.speak();
}
因为多态,实现接口等操作,平时写业务时,用的不多,这里就没再列出。对于有需要的时候,直接看文档就好了。这里只是放上最快的入门对比
如果时间允许,下一个文章写一下《大米粥计时器的代码讲解》