flutter开发:sqlflite的使用

引入插件

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.0
  sqflite: ^1.3.2+1

封装公共方法

这里的两个公共类是封装的比较好的,只要有sqlflite数据库操作就可以拿过来直接用的

  1. 数据库初始化,获取数据库对象以及关闭数据库。在sqflite中的数据库操作,首先得获取到数据库对象,通过数据库对象才可以对相应的表进行增删查改。
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';

class SqlManager {
  static const _VERSION = 1;
  static const _NAME = "my.db";
  static Database _database;

  ///初始化
  static init() async {
    var databasesPath = await getDatabasesPath();
    String path = join(databasesPath, _NAME);
    _database = await openDatabase(path,
        version: _VERSION, onCreate: (Database db, int version) async {});
  }

  ///判断表是否存在
  static isTableExits(String tableName) async {
    await getCurrentDatabase();
    var res = await _database.rawQuery(
        "select * from Sqlite_master where type = 'table' and name = '$tableName'");
    return res != null && res.length > 0;
  }

  ///获取当前数据库对象
  static Future<Database> getCurrentDatabase() async {
    if (_database == null) {
      await init();
    }
    return _database;
  }

  ///关闭
  static close() {
    _database?.close();
    _database = null;
  }
}
  1. 在对某一张表进行操作时,先得判断数据库对象是否存在,不存在就得创建。再判断表是否在该数据库中存在,不存在就创建。
import 'package:meta/meta.dart';
import 'package:sqflite/sqflite.dart';

import 'SqlManager.dart';

abstract class BaseDbProvider {
  bool isTableExits = false;

  createTableString();

  tableName();

  ///创建表sql语句
  tableBaseString(String sql) {
    return sql;
  }

  Future<Database> getDataBase() async {
    return await open();
  }

  ///super 函数对父类进行初始化
  @mustCallSuper
  prepare(name, String createSql) async {
    isTableExits = await SqlManager.isTableExits(name);
    if (!isTableExits) {
      Database db = await SqlManager.getCurrentDatabase();
      return await db.execute(createSql);
    }
  }

  @mustCallSuper
  open() async {
    if (!isTableExits) {
      await prepare(tableName(), createTableString());
    }
    return await SqlManager.getCurrentDatabase();
  }
}

具体操作

  1. 新建bean类,这里除了基本的get和set方法之外还有User对象转map,map转User的两个方法。
class User {
  User();

  int _id;
  String _name;
  String _desc;

  int get id => _id;

  String get name => _name;

  String get desc => _desc;

  set desc(String value) {
    _desc = value;
  }

  set name(String value) {
    _name = value;
  }

  set id(int value) {
    _id = value;
  }

  Map<String, dynamic> toMap() {
    var map = Map<String, dynamic>();

    map['id'] = _id;
    map['name'] = _name;
    map['desc'] = _desc;

    return map;
  }

  User.fromMapObject(Map<String, dynamic> map) {
    this._id = map['id'];
    this._name = map['name'];
    this._desc = map['desc'];
  }
}
  1. User表的增删查改方法
import 'package:sqflite/sqlite_api.dart';

import 'BaseDbProvider.dart';
import 'User.dart';

class UserDbProvider extends BaseDbProvider {
  ///表名
  final String name = 'UserInfo';

  final String columnId = "id";
  final String columnName = "name";
  final String columnDesc = "desc";

  UserDbProvider();

  //获取表名称
  @override
  tableName() {
    return name;
  }

  //创建表操作
  @override
  createTableString() {
    return '''
        create table $name (
        $columnId integer primary key,$columnName text not null,
        $columnDesc text not null)
      ''';
  }

  ///查询数据
  Future selectUser(int id) async {
    Database db = await getDataBase();
    return await db.rawQuery("select * from $name where $columnId = $id");
  }

  //查询数据库所有
  Future<List<Map<String, dynamic>>> selectMapList() async {
    var db = await getDataBase();
    var result = await db.query(name);
    return result;
  }

  //获取数据库里所有user
  Future<List<User>> getAllUser() async {
    var userMapList = await selectMapList();
    var count = userMapList.length;
    List<User> userList = List<User>();

    for (int i = 0; i < count; i++) {
      userList.add(User.fromMapObject(userMapList[i]));
    }
    return userList;
  }

  //根据id查询user
  Future<User> getUser(int id) async {
    var noteMapList = await selectUser(id); // Get 'Map List' from database
    var user = User.fromMapObject(noteMapList[id]);
    return user;
  }

  //增加数据
  Future<int> insertUser(User user) async {
    var db = await getDataBase();
    var result = await db.insert(name, user.toMap());
    return result;
  }

  //更新数据
  Future<int> update(User user) async {
    var database = await getDataBase();
    var result = await database.rawUpdate(
        "update $name set $columnName = ?,$columnDesc = ? where $columnId= ?",
        [user.name, user.desc, user.id]);
    return result;
  }

  //删除数据
  Future<int> deleteUser(int id) async {
    var db = await getDataBase();
    var result = await db.rawDelete('DELETE FROM $name WHERE $columnId = $id');
    return result;
  }
}

外部使用

  1. 创建Provider对象。
UserDbProvider provider = UserDbProvider();

2.通过对象调用User的增删查改方法

 if (user.id == null) {
     user.id = new DateTime.now().millisecondsSinceEpoch;  //id 为当前时间戳
     result = await provider.insertUser(user);
   } else {
     result = await provider.update(user);
   }

完整demo

去github上查看demo

参考链接


flutter动态申请权限

https://pub.flutter-io.cn/packages/permission_handler 

https://www.jianshu.com/p/fa68876fbdfd

  Future requestPermission() async {
    // 申请权限
    Map<PermissionGroup, PermissionStatus> permissions =
        await PermissionHandler().requestPermissions([PermissionGroup.storage]);

    // 申请结果
    PermissionStatus permission = await PermissionHandler()
        .checkPermissionStatus(PermissionGroup.storage);

    if (permission == PermissionStatus.granted) {
      print("权限申请通过");
    } else {
      print("权限申请通过");
    }
  }
} 

参考链接


flutter 动态申请权限

Flutter 1.22最新的多国语支持配置和使用

Flutter 1.22现已发布

​ 最新版本中,随之发布了很多新的特性以及修复了一些BUG,现在我们说说其中的新的多国语插件。个人感觉新多国语操作,多了一些模板性的东西,但是也方便了很多东西。

​ 话不多说,现在我们进入正题,如何使用新插件呢?

配置项目的yaml文件

dependencies:
  flutter:
    sdk: flutter
  flutter_localizations: #配置多国语支持
    sdk: flutter
  intl: ^0.16.1   #多国语插件,要求高于0.16.1版本
  
  
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  generate: true  #新增配置,自动多国语代码生成

继续阅读Flutter 1.22最新的多国语支持配置和使用

AAPT: error: resource android:attr/fontVariationSettings not found

最近在编译`Flutter`项目的时候,报错如下:

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:processDebugResources'.
> Android resource linking failed
  Output:  /Users/xxxx/Source/developer_quest/build/app/intermediates/incremental/mergeDebugResources/merged.dir/values/values.xml:86: error: resource android:attr/fontVariationSettings not found.
  /Users/xxxx/Source/developer_quest/build/app/intermediates/incremental/mergeDebugResources/merged.dir/values/values.xml:87: error: resource android:attr/ttcIndex not found.
  error: failed linking references.

  Command: /Users/xxxx/.gradle/caches/transforms-1/files-1.1/aapt2-3.2.1-4818971-osx.jar/ace5a2b7765511437e6add51febae423/aapt2-3.2.1-4818971-osx/aapt2 link -I\
          /Users/xxxx/Library/Android/sdk/platforms/android-27/android.jar\
          --manifest\
          /Users/xxxx/Source/developer_quest/build/app/intermediates/merged_manifests/debug/processDebugManifest/merged/AndroidManifest.xml\
          -o\
          /Users/xxxx/Source/developer_quest/build/app/intermediates/processed_res/debug/processDebugResources/out/resources-debug.ap_\
          -R\
          @/Users/xxxx/Source/developer_quest/build/app/intermediates/incremental/processDebugResources/resources-list-for-resources-debug.ap_.txt\
          --auto-add-overlay\
          --java\
          /Users/xxxx/Source/developer_quest/build/app/generated/not_namespaced_r_class_sources/debug/processDebugResources/r\
          --custom-package\
          dev.flutter.devrpg\
          -0\
          apk\
          --output-text-symbols\
          /Users/xxxx/Source/developer_quest/build/app/intermediates/symbols/debug/R.txt\
          --no-version-vectors
  Daemon:  AAPT2 aapt2-3.2.1-4818971-osx Daemon #0
  Output:  /Users/xxxx/.gradle/caches/transforms-1/files-1.1/core-1.1.0.aar/becc1f5b1538f3fe61f1f959fb2bd672/res/values/values.xml:174:5-210:25: AAPT: error: resource android:attr/fontVariationSettings not found.

  /Users/xxxx/.gradle/caches/transforms-1/files-1.1/core-1.1.0.aar/becc1f5b1538f3fe61f1f959fb2bd672/res/values/values.xml:174:5-210:25: AAPT: error: resource android:attr/ttcIndex not found.

  error: failed linking references.
  Command: /Users/xxxx/.gradle/caches/transforms-1/files-1.1/aapt2-3.2.1-4818971-osx.jar/ace5a2b7765511437e6add51febae423/aapt2-3.2.1-4818971-osx/aapt2 link -I\
          /Users/xxxx/Library/Android/sdk/platforms/android-27/android.jar\
          --manifest\
          /Users/xxxx/Source/developer_quest/build/app/intermediates/merged_manifests/debug/processDebugManifest/merged/AndroidManifest.xml\
          -o\
          /Users/xxxx/Source/developer_quest/build/app/intermediates/processed_res/debug/processDebugResources/out/resources-debug.ap_\
          -R\
          @/Users/xxxx/Source/developer_quest/build/app/intermediates/incremental/processDebugResources/resources-list-for-resources-debug.ap_.txt\
          --auto-add-overlay\
          --java\
          /Users/xxxx/Source/developer_quest/build/app/generated/not_namespaced_r_class_sources/debug/processDebugResources/r\
          --custom-package\
          dev.flutter.devrpg\
          -0\
          apk\
          --output-text-symbols\
          /Users/xxxx/Source/developer_quest/build/app/intermediates/symbols/debug/R.txt\
          --no-version-vectors
  Daemon:  AAPT2 aapt2-3.2.1-4818971-osx Daemon #0

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 13s
Exception: Gradle task assembleDebug failed with exit code 1

解决方案:

try to change the compileSdkVersion above 27:

compileSdkVersion 28

参考链接


resource android:attr/fontVariationSettings not found

30分钟掌握Dart语言

  • 在Dart中,一切都是对象,一切对象都是class的实例,哪怕是数字类型、方法甚至null都是对象,所有的对象都是继承自Object
  • 虽然Dart是强类型语言,但变量类型是可选的因为Dart可以自动推断变量类型
  • Dart支持范型,List<int>表示一个整型的数据列表,List<dynamic>则是一个对象的列表,其中可以装任意对象
  • Dart支持顶层方法(如main方法),也支持类方法或对象方法,同时你也可以在方法内部创建方法
  • Dart支持顶层变量,也支持类变量或对象变量
  • 跟Java不同的是,Dart没有public protected private等关键字,如果某个变量以下划线(_)开头,代表这个变量在库中是私有的,具体可以看这里
  • Dart中变量可以以字母或下划线开头,后面跟着任意组合的字符或数字

变量

变量定义

以下代码是Dart中定义变量的方法:

main() {
  var a = 1;
  int b = 10;
  String s = "hello";
  dynamic c = 0.5;
}

你可以明确指定某个变量的类型,如int bool String,也可以用var或 dynamic来声明一个变量,Dart会自动推断其数据类型。

变量的默认值

注意:没有赋初值的变量都会有默认值null

final和const

如果你绝不想改变一个变量,使用final或const,不要使用var或其他类型,一个被final修饰的变量只能被赋值一次,一个被const修饰的变量是一个编译时常量(const常量毫无疑问也是final常量)。可以这么理解:final修饰的变量是不可改变的,而const修饰的表示一个常量。

注意:实例变量可以是final的但不能是const的

下面用代码说明:

var count = 10; 
final Num = count;  // final 只能赋值一次
const Num1 = 10; // const赋值必须是编译时常量

final和const的区别:

区别一:final 要求变量只能初始化一次,并不要求赋的值一定是编译时常量,可以是常量也可以不是。而 const 要求在声明时初始化,并且赋值必需为编译时常量。

区别二:final 是惰性初始化,即在运行时第一次使用前才初始化。而 const 是在编译时就确定值了。

内建数据类型

Dart有如下几种内建的数据类型:

  • numbers
  • strings
  • booleans
  • lists(或者是arrays)
  • maps
  • runes(UTF-32字符集的字符)
  • symbols
    下面用一段代码来演示以上各类数据类型:
main() {
  // numbers
  var a = 0;
  int b = 1;
  double c = 0.1;

  // strings
  var s1 = 'hello';
  String s2 = "world";

  // booleans
  var real = true;
  bool isReal = false;

  // lists
  var arr = [1, 2, 3, 4, 5];
  List<String> arr2 = ['hello', 'world', "123", "456"];
  List<dynamic> arr3 = [1, true, 'haha', 1.0];

  // maps
  var map = new Map();
  map['name'] = 'zhangsan';
  map['age'] = 10;
  Map m = new Map();
  m['a'] = 'a';

  //runes,Dart 中 使用runes 来获取UTF-32字符集的字符。String的 codeUnitAt and codeUnit属性可以获取UTF-16字符集的字符
  var clapping = '\u{1f44f}';
  print(clapping); // 打印的是拍手emoji的表情

  // symbols
  print(#s == new Symbol("s")); // true
}

函数

函数的返回值

Dart是一个面向对象的编程语言,所以即使是函数也是一个对象,也有一种类型Function,这就意味着函数可以赋值给某个变量或者作为参数传给另外的函数。虽然Dart推荐你给函数加上返回值,但是不加返回值的函数同样可以正常工作,另外你还可以用=>代替return语句,比如下面的代码:

// 声明返回值
int add(int a, int b) {
  return a + b;
}
// 不声明返回值
add2(int a, int b) {
  return a + b;
}
// =>是return语句的简写
add3(a, b) => a + b; 

main() {
  print(add(1, 2)); // 3
  print(add2(2, 3)); // 5
  print(add3(1, 2)); // 3
}

命名参数、位置参数、参数默认值

命名参数

sayHello({String name}) {
  print("hello, my name is $name");
}
sayHello2({name: String}) {
  print("hello, my name is $name");
}
main() {
  // 打印 hello, my name is zhangsan
  sayHello(name: 'zhangsan');

  // 打印 hello, my name is wangwu
  sayHello2(name: 'wangwu');
}

可以看到,定义命名参数时,你可以以 {type paramName} 或者 {paramName: type} 两种方式声明参数,而调用命名参数时,需要以 funcName(paramName: paramValue) 的形式调用。

命名参数的参数并不是必须的,所以上面的代码中,如果调用sayHello()不带任何参数,也是可以的,只不过最后打印出来的结果是:hello, my name is null,在Flutter开发中,你可以使用@required注解来标识一个命名参数,这代表该参数是必须的,你不传则会报错,比如下面的代码:

const Scrollbar({Key key, @required Widget child})

位置参数

使用中括号[]括起来的参数是函数的位置参数,代表该参数可传可不传,位置参数只能放在函数的参数列表的最后面,如下代码所示:

sayHello(String name, int age, [String hobby]) { // 位置参数可以有多个,比如[String a, int b]
  StringBuffer sb = new StringBuffer();
  sb.write("hello, this is $name and I am $age years old");
  if (hobby != null) {
    sb.write(", my hobby is $hobby");
  }
  print(sb.toString());
}
main() {
  // hello, this is zhangsan and I am 20 years old
  sayHello("zhangsan", 20);
  // hello, this is zhangsan and I am 20 years old, my hobby is play football
  sayHello("zhangsan", 20, "play football");
}

参数默认值

你可以为命名参数或者位置参数设置默认值,如下代码所示:

// 命名参数的默认值
int add({int a, int b = 3}) { // 不能写成:int add({a: int, b: int = 3})
  return a + b;
}
// 位置参数的默认值
int sum(int a, int b, [int c = 3]) {
  return a + b + c;
}

main()函数

不论在Dart还是Flutter中,必须都需要一个顶层的main()函数,它是整个应用的入口函数,main()函数的返回值是void,还有一个可选的参数,参数类型是List<String>。

函数作为一类对象

你可以将一个函数作为参数传给另一个函数,比如下面的代码:

printNum(int a) {
  print("$a");
}
main() {
  //  依次打印:
  //  1
  //  2
  //  3
  var arr = [1, 2, 3];
  arr.forEach(printNum);
}

你也可以将一个函数赋值给某个变量,比如下面的代码:

printNum(int a) {
  print("$a");
}
main() {
  var f1 = printNum;
  Function f2 = printNum;
  var f3 = (int a) => print("a = $a");
  f1(1);
  f2(2);
  f3(6);
}

匿名函数

大多数函数都是有名称的,比如main() printName()等,但是你也可以写匿名函数,如果你对Java比较熟悉,那下面的Dart代码你肯定也不会陌生:

test(Function callback) {
  callback("hello");
}
main() {
  test((param) {
    // 打印hello
    print(param);
  });
}

匿名函数类似于Java中的接口,往往在某个函数的参数为函数时使用到。

函数返回值

所有的函数都有返回值,如果没有指定return语句,那么该函数的返回值为null。

运算符

Dart中的运算符与Java中的类似,比如++a a == b b ? a : b,但是也有一些与Java不太一样的运算符,下面用代码说明:

main() {
  // 与Java相同的运算符操作

  int a = 1;
  ++a;
  a++;
  var b = 1;
  print(a == b);  // false
  print(a * b); // 3
  bool real = false;
  real ? print('real') : print('not real'); // not real
  print(real && a == b); // false
  print(real || a == 3); // true
  print(a != 2); // true
  print(a <= b); // false
  var c = 9;
  c += 10;
  print("c = $c"); // c = 19
  print(1<<2); // 4

  // 与Java不太一样的运算符操作

  // is运算符用于判断一个变量是不是某个类型的数据
  // is!则是判断变量不是某个类型的数据
  var s = "hello";
  print(s is String); // true
  var num = 6;
  print(num is! String); // true

  // ~/才是取整运算符,如果使用/则是除法运算,不取整
  int k = 1;
  int j = 2;
  print(k / j); // 0.5
  print(k ~/ j); // 0

  // as运算符类似于Java中的cast操作,将一个对象强制类型转换
  (emp as Person).teach();

  // ??=运算符 如果 ??= 运算符前面的变量为null,则赋值,否则不赋值
  var param1 = "hello", param2 = null;
  param1 ??= "world";
  param2 ??= "world";
  print("param1 = $param1"); // param1 = hello
  print("param2 = $param2"); // param2 = world
  
  // ?.运算符
  var str1 = "hello world";
  var str2 = null;
  print(str1?.length); // 11
  print(str2?.length); // null 
  print(str2.length); // 报错
}

..运算符(级联操作)

如果你对Java中的建造者模式比较熟悉的话,Dart中的..运算符也很好理解,先看下面的代码:

class Person {
  eat() {
    print("I am eating...");
  }

  sleep() {
    print("I am sleeping...");
  }

  study() {
    print("I am studying...");
  }
}
main() {
  // 依次打印
  //  I am eating...
  //  I am sleeping...
  //  I am studying...
  new Person()..eat()
      ..sleep()
      ..study();
}

可以看到,使用..调用某个对象的方法(或者成员变量)时,返回值是这个对象本身,所以你可以接着使用..调用这个对象的其他方法,这不就类似于Java中的建造者模式,每次build某个属性时,都返回一个this对象吗。

控制流程

if / else switch for /while try / catch语句跟Java中都类似,try / catch语句可能稍有不同,下面用一段代码说明:

main() {
  // if else语句
  int score = 80;
  if (score < 60) {
    print("so bad!");
  } else if (score >= 60 && score < 80) {
    print("just so so!");
  } else if (score >= 80) {
    print("good job!");
  }

  // switch语句
  String a = "hello";
  // case语句中的数据类型必须是跟switch中的类型一致
  switch (a) {
    case "hello":
      print("haha");
      break;
    case "world":
      print("heihei");
      break;
    default:
      print("WTF");
  }

  // for语句
  List<String> list = ["a", "b", "c"];
  for (int i = 0; i < list.length; i++) {
    print(list[i]);
  }
  for (var i in list) {
    print(i);
  }
  // 这里的箭头函数参数必须用圆括号扩起来
  list.forEach((item) => print(item));

  // while语句
  int start = 1;
  int sum = 0;
  while (start <= 100) {
    sum += start;
    start++;
  }
  print(sum);

  // try catch语句
  try {
    print(1 ~/ 0);
  } catch (e) {
    // IntegerDivisionByZeroException
    print(e);
  }
  try {
    1 ~/ 0;
  } on IntegerDivisionByZeroException { // 捕获指定类型的异常
    print("error"); // 打印出error
  } finally {
    print("over"); // 打印出over
  }
}

类(Class)

类的定义与构造方法

Dart中的类没有访问控制,所以你不需要用private, protected, public等修饰成员变量或成员函数,一个简单的类如下代码所示:

class Person {
  String name;
  int age;
  String gender;
  Person(this.name, this.age, this.gender);
  sayHello() {
    print("hello, this is $name, I am $age years old, I am a $gender");
  }
}

上面的Person类中有3个成员变量,一个构造方法和一个成员方法,看起来比较奇怪的是Person的构造方法,里面传入的3个参数都是this.xxx,而且没有大括号{}包裹的方法体,这种语法是Dart比较独特而简洁的构造方法声明方式,它等同于下面的代码:

Person(String name, int age, String gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
}

要调用Person类的成员变量或成员方法,可以用下面的代码:

  var p = new Person("zhangsan", 20, "male");
  p.sayHello(); // hello, this is zhangsan, I am 20 years old, I am a male
  p.age = 50;
  p.gender = "female";
  p.sayHello(); // hello, this is zhangsan, I am 50 years old, I am a female

类除了有跟类名相同的构造方法外,还可以添加命名的构造方法,如下代码所示:

class Point {
  num x, y;
  Point(this.x, this.y);
  // 类的命名构造方法
  Point.origin() {
    x = 0;
    y = 0;
  }
}
main() {
  // 调用Point类的命名构造方法origin()
  var p = new Point.origin();
  var p2 = new Point(1, 2);
}

Dart中使用extends关键字做类的继承,如果一个类只有命名的构造方法,在继承时需要注意,如下代码:

class Human {
  String name;
  Human.fromJson(Map data) {
    print("Human's fromJson constructor");
  }
}
class Man extends Human {
  Man.fromJson(Map data) : super.fromJson(data) {
    print("Man's fromJson constructor");
  }
}

由于Human类没有默认构造方法,只有一个命名构造方法fromJson,所以在Man类继承Human类时,需要调用父类的fromJson方法做初始化,而且必须使用Man.fromJson(Map data) : super.fromJson(data)这种写法,而不是像Java那样将super写到花括号中。

有时候你仅仅只是在某个类的构造方法中,调用这个类的另一个构造方法,你可以这么写:

class Point {
  num x, y;
  Point(this.x, this.y);
  // 命名构造方法调用了默认的构造方法
  Point.alongXAxis(num x) : this(x, 0);
}

类的成员方法

一个类的成员方法是一个函数,为这个类提供某些行为。上面的代码中已经有了一些类的成员方法的定义,这些定义方式跟Java很类似,你可以为某个类的成员变量提供getter/setter方法,如下代码:

class Rectangle {
  num left, top, width, height;

  // 构造方法传入left, top, width, height几个参数
  Rectangle(this.left, this.top, this.width, this.height);

  // right, bottom两个成员变量提供getter/setter方法
  num get right => left + width;
  set right(num value) => left = value - width;
  num get bottom => top + height;
  set bottom(num value) => top = value - height;
}

抽象类和抽象方法

使用abstract修饰一个类,则这个类是抽象类,抽象类中可以有抽象方法和非抽象方法,抽象方法没有方法体,需要子类去实现,如下代码:

abstract class Doer {
  // 抽象方法,没有方法体,需要子类去实现
  void doSomething();
  // 普通的方法
  void greet() {
    print("hello world!");
  }
}
class EffectiveDoer extends Doer {
  // 实现了父类的抽象方法
  void doSomething() {
    print("I'm doing something...");
  }
}

运算符重载

Dart中有类似于C++中的运算符重载语法,比如下面的代码定义了一个向量类,重载了向量的+ -运算:

class Vector {
  num x, y;
  Vector(this.x, this.y);
  Vector operator +(Vector v) => new Vector(x + v.x, y + v.y);
  Vector operator -(Vector v) => new Vector(x - v.x, y - v.y);
  printVec() {
    print("x: $x, y: $y");
  }
}
main() {
  Vector v1 = new Vector(1, 2);
  Vector v2 = new Vector(3, 4);
  (v1 - v2).printVec(); // -2, -2
  (v1 + v2).printVec(); // 4, 6
}

枚举类

使用enum关键字定义一个枚举类,这个语法跟Java类似,如下代码:

enum Color { red, green, blue }

mixins

mixins是一个重复使用类中代码的方式,比如下面的代码:

class A {
  a() {
    print("A's a()");
  }
}
class B {
  b() {
    print("B's b()");
  }
}
// 使用with关键字,表示类C是由类A和类B混合而构成
class C = A with B;
main() {
  C c = new C();
  c.a(); // A's a()
  c.b(); // B's b()
}

静态成员变量和静态成员方法

// 类的静态成员变量和静态成员方法
class Cons {
  static const name = "zhangsan";
  static sayHello() {
    print("hello, this is ${Cons.name}");
  }
}
main() {
  Cons.sayHello(); // hello, this is zhangsan
  print(Cons.name); // zhangsan
}

泛型(Generics)

Java和C++语言都有泛型,Dart语言也不例外,使用泛型有很多好处,比如:

正确指定泛型类型会产生更好的生成代码。
泛型可以减小代码的复杂度

Dart内置的数据类型List就是一个泛型数据类型,你可以往List中塞任何你想的数据类型比如整型、字符串、布尔值等
关于Dart更多的泛型知识点,可以查看这里。

Dart库(Libraries)

Dart目前已经有很多的库提供给开发者,许多功能不需要开发者自己去实现,只需要导入对应的包即可,使用import语句来导入某个包,比如下面的代码:

import 'dart:html';

如果你想导入自己写的某个代码文件,使用相对路径即可,例如当前有一个demo.dart文件,跟该文件同级目录下有个util.dart文件,文件代码如下:

// util.dart文件内容
int add(int a, int b) {
  return a + b;
}

在demo.dart文件中如果要引用util.dart文件,使用下面的方式导入:

// demo.dart
import './util.dart';
main() {
  print(add(1, 2));
}

你可以使用as关键字为导入的某个包设置一个前缀,或者说别名,比如下面的代码:

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// Uses Element from lib1.
Element element1 = Element();
// Uses Element from lib2.
lib2.Element element2 = lib2.Element();

你也可以在导入包时使用show hide关键字来导入某个包中的部分功能,比如下面的代码:

// 只导入foo
import 'package:lib1/lib1.dart' show foo;
// 导入除了foo的所有其他部分
import 'package:lib2/lib2.dart' hide foo;

导入包时使用deferred as可以让这个包懒加载,懒加载的包只会在该包被使用时得到加载,而不是一开始就加载,比如下面的代码:

import 'package:greetings/hello.dart' deferred as hello;

异步

Dart提供了类似ES7中的async await等异步操作,这种异步操作在Flutter开发中会经常遇到,比如网络或其他IO操作,文件选择等都需要用到异步的知识。
async和await往往是成对出现的,如果一个方法中有耗时的操作,你需要将这个方法设置成async,并给其中的耗时操作加上await关键字,如果这个方法有返回值,你需要将返回值塞到Future中并返回,如下代码所示:

Future checkVersion() async {
  var version = await lookUpVersion();
  // Do something with version
}

下面的代码使用Dart从网络获取数据并打印出来:

import 'dart:async';
import 'package:http/http.dart' as http;

Future<String> getNetData() async{
  http.Response res = await http.get("http://www.baidu.com");
  return res.body;
}
main() {
  getNetData().then((str) {
    print(str);
  });
}

参考链接


30分钟掌握Dart语言

在macOS Catalina(10.15.5)上搭建Flutter开发环境

下载并安装目前最新的`Android Studio 4.0`,然后通过`Android Studio 4.0`安装`Android SDK`。

下载目前最新的`flutter`

$ cd ~

$ mkdir Android

$ cd Android

$ git clone -b stable https://github.com/flutter/flutter.git

配置环境变量

$ export PATH=/Users/`whoami`/Android/flutter/bin:$PATH

# android sdk目录,替换为你自己的即可,下面是Android Studio安装SDK的默认目录 
$ export ANDROID_HOME="/Users/`whoami`/Library/Android/sdk" 
  
$ export PATH=${PATH}:${ANDROID_HOME}/tools
  
$ export PATH=${PATH}:${ANDROID_HOME}/platform-tools
  
$ export PUB_HOSTED_URL=https://pub.flutter-io.cn
  
$ export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

上述环境变量,全部追加到 `.bashrc` 尾部

export PATH=/Users/`whoami`/Android/flutter/bin:$PATH
# android sdk目录,替换为你自己的即可,下面是Android Studio安装SDK的默认目录
export ANDROID_HOME="/Users/`whoami`/Library/Android/sdk"
export PATH=${PATH}:${ANDROID_HOME}/tools
export PATH=${PATH}:${ANDROID_HOME}/platform-tools
export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

如果不增加到`.bashrc` 尾部,则在 `Android Studio 4.0` 创建项目的时候,会非常慢,主要是网络问题。

必要的环境配置,依赖下载

#对于 macOS Catalina 10.15.5 来说,由于Shell被替换成了ZSH,因此环境变量需要重新配置一下

$ cp /etc/zprofile ~/.zprofile

$ chmod u+w ~/.zprofile

$ echo "source ~/.bashrc" >> .zprofile

$ flutter doctor

$ flutter doctor --android-licenses

$ sudo gem install cocoapods

# 杀掉全部的dart进程,否则可能导致长时间无法创建项目的问题
# 任何时候,flutter相关卡住,貌似都可以这样操作

$ killall -9 dart

继续阅读在macOS Catalina(10.15.5)上搭建Flutter开发环境