在Flutter中使用Dart的介绍

926 阅读9分钟

当涉及到跨平台开发时,Flutter是快速增长的技术之一,而制作Flutter应用程序背后的秘诀是Dart语言。

即使您不熟悉Dart,也可以开始开发Flutter应用程序,本教程将涵盖您需要知道的基本语法和信息,以便您能够自如地构建Flutter应用程序。

什么是Dart?

Dart是一种多功能和客户端优化的语言,用于在任何网络或移动平台上快速开发应用程序,并可用于桌面和嵌入式设备。Dart语言的核心目标是为您提供一套工具和编程语言,使您的工作效率提高,并随着开发人员的要求和需要的增加而不断发展。

Dart是一种全面的语言,提供优秀的语言能力,如Future、Stream、Sound Null Safety等。

Dart的设计对大多数具有不同编程背景的开发者来说都是熟悉的。因此,无论你是否有JavaScript和TypeScript的背景,或者你一直是一个面向对象的程序员,你都会发现使用Dart很熟悉。

而且,由于Dart的架构,杀手锏、热重载和声明式编程都可以在Flutter中实现。

更重要的是,Dart还带有许多内置库,如dart:ync、dart:convert、dart:html、dart:io等,以及一个奇妙的生态系统和出色的软件包管理器pub.dev

无论你是否想使用Flutter,Dart都将是你学习和在你的下一个应用程序中使用的最佳选择。

如果你想快速尝试一下,你可以在线使用dartpad.dev

如何使用Dart

在您开始创建Flutter应用程序之前,您应该了解一些Dart的概念。

1.Main()函数。

每个应用程序的入口是main() 函数。即使你想在控制台打印一些东西,你也必须有一个main() 的部分。

void main() {
  var list = ['apples', 'bananas', 'oranges'];
  list.forEach((item) {
    print('${list.indexOf(item)}: $item');
  });
}

在Flutter中,你将从PROJECT_ROOT/lib/main.dart 中的main() 函数开始你的应用程序,在那里你将你的主部件传递给runApp() ,它将把它附加到屏幕上。这就是第一个主入口点。

void main() {
  runApp(MyApp());
}

2.: (分号)。

在Dart中你需要; (分号),在上面的例子中你可以看到。

runApp(MyApp());

3.类型和空安全

Dart是一种类型安全的语言。它使用静态类型检查和运行时检查。当你学会了这个语法,你就能更快地理解Flutter代码。下面是一个简单变量的解剖图。

[修改器] [类型] [变量_名称] = [值]。

// e.g: 
final String name = "Majid Hajian";

虽然类型是强制性的,但由于类型推理的原因,类型注释是可选的。所以,你可能会遇到这种情况。

var name = "Majid Hajian"; // from now on `name` is a String;

Dart中最常见的初始化变量修饰符是var,final,const, 和late ,但请记住,当你在变量名前使用类型时,你可以使用所有的修饰符,除了var

var name = "Majid Hajian";
String name = "Majid Hajian";
final String name = "Majid Hajian";
const String name = "Majid Hajian";

使用var 或不使用修饰符可以创建一个灵活的变量,这意味着你可以随时改变其值。如果你从不打算修改这个变量,你应该使用final ,它只设置一次变量,或者使用const ,它形成一个编译时常量。

但也有更复杂的情况。让我们来看看MapList 的类型定义。

// Type of a List (Array): List<TYPE_OF_MEMBER>
// e.g: 
List<String> names = ['Majid', 'Hajian'];

// Type of a Map (Key-Values): Map<Key_TYPE, VALUE_TYPE>
// e.g: 
Map<String, number> ages = {'sara': 35, 'susan: 20};

在很多情况下,你可能没有给Dart分析器提供足够的信息,你可能会面临一个类型转换错误。让我们看一个例子。

var names = [];

变量类型推断List<dynamic>dynamic 可能是任何类型,因为我们在初始化变量时没有提供数组的可能类型。因此,Dart将类型投给了List<dynamic> ,在这里它可以是任何类型。通过在初始化或启动变量时给值添加注解,我们可以防止这种类型的错误。

final names = <String>[];
// Or
final List<String> names = [];

从Dart 2.12开始,Dart是一种健全的null安全语言,这意味着你的代码中的类型默认是不可置空的,这表明一个变量不能包含null ,除非你说它们可以。

final String name = null;
// or
final String name; 

注意,上面的变量已经无效了,因为我们用一个null 的值来初始化一个变量。因为我们还没有指定,所以运行时的空引用错误变成了编辑时的分析错误。

这时,? 就派上用场了。null 为了给变量赋值,我们可以在其类型声明中使用?

final String? name = null;

你会在Flutter 2.0.0+和Dart 2.12.0+中经常看到这种语法。

最后,在Dart中最常见的内置类型,你会在Flutter应用程序中发现以下几种。

  • 数字 (int, double)
  • 字符串 (String)
  • 布尔(bool)
  • 列表(List,也叫数组)
  • 集合 (Set)
  • 地图 (Map)
  • 符号 (Symbol)
  • 值null (空)

4.类

Dart是一种面向对象的语言,具有类和混合基的继承性。这意味着你可以创建abstract 类型,class ,使用implement ,和extends 。在你想使用mix-in的地方,你也可能看到with

在Dart类中,constructor 的名称与className 相同,像这样。

class MyApp {
  MyApp(); // constructor
}

如果你不需要初始化实例变量或创建一个对象,你就不需要有构造函数。如果你需要的话,你应该通过构造函数参数来传递它们。

class MyApp {
MyApp(this.title);

final String? title;
}

在Dart 2中,你也不需要使用new 关键字来实例化一个类。

final myapp = MyApp('Majid Hajian');
print(myapp.title);

Flutter中的所有widget都是StatelessWidgetStatefulWidget 的扩展类。因此,您可以创建您的widget(类)。

class MyApp extends StatelessWidget {

}

StatelessWidgetState 对象对应的StatefulWidget 都有build() 方法来建立您的屏幕。所以,为了实现你的widget的构建,你必须@重写build()方法。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

惯例是以大写字母开始className

5.Dart中的参数

无论是在类中还是在函数中,学习如何定义参数是很有必要的,因为它是Flutter开发的关键部分之一。

  • 需要

在Dart中,如果你想定义一个必需的参数,你可以把它们传递给构造函数或函数。

String getUrl(String prefix, String host) {
  return '';
}
// OR 
class MyApp {
  MyApp(this.title);

  final String? title;
}

在这两种情况下,你都需要将参数正确传递到预期的位置。这也是我们对位置性参数的命名。

  • 可选的

在很多情况下,你会发现你想把一个参数变成可选的。例如,要改变上面的代码,我们可以这样编码。

String getUrl({String? prefix, String? host}) {
  return '';
}
// OR
class MyApp {
  MyApp({this.title});

  final String? title;
}

我们使用{} 来定义我们的可选参数,这些参数的命名与我们描述的一样。现在,要使用被命名的参数,使用参数的名称并赋值。

getUrl( host: '', prefix: '');
//Or 
MyApp(title: 'Majid');

正如你所看到的,使用这种方法的主要优点是,你不需要将值传递到参数的准确位置。

更重要的是,你的函数和类的参数是自我记录的。换句话说,你可以简单地定义参数的名称是什么,并传递值。当你想为一个方法或类指定许多参数时,定义命名参数很有帮助。

在Flutter中你会经常遇到命名参数。下面是Flutter中Text widget的一个例子。

Text(
    this.data, {
    Key key,
    this.style,
    this.strutStyle,
    this.textAlign,
    this.textDirection,
    this.locale,
    this.softWrap,
    this.overflow,
    this.textScaleFactor,
    this.maxLines,
    this.semanticsLabel,
    this.textWidthBasis,
    this.textHeightBehavior,
})

this.data 是定位的,意味着第一个参数是必须传入的,但其余的参数是可选的,因为它们是在{} 中定义的。

你可能不会问,一个命名的参数怎么会是必须的而不是可选的。在Dart 2.12+中,你现在有了required 这个关键字,使一个参数成为必须传入的。让我们来看看上面的例子。

class MyApp {
  MyApp({this.title}); // optional named parameter
  final String? title;
}

但是如果你在参数前使用required 关键字,我们将使它成为强制传入的参数,尽管它是一个命名参数。

class MyApp {
  MyApp({required this.title});

  final String? title;
}

如果你现在实例化MyApp() 类,你也必须传入title ;否则,编译器会抛出一个错误。

print(MyApp(title: 'Majid'));

6.Dart格式化

Dart自带的格式化工具可以帮助你的代码更容易阅读。这里有一个提示,可以帮助你更好地格式化你的代码,特别是在Flutter中,你会有很多嵌套的小部件。尽可能地使用,!

 Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: <Widget>[
    Text('You have pushed the button this many times:'),
    Text('$_counter', style: Theme.of(context).textTheme.headline4),
  ],
),

这里有一个Column widget,它有两个Text 子项。没有一个子程序在传递参数时使用, 。文字很拥挤,不容易读懂,但如果你在每个Text widget的最后一个参数的末尾使用, ,它就会被格式化,更加友好。

Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: <Widget>[
    Text(
      'You have pushed the button this many times:',
    ),
    Text(
      '$_counter',
      style: Theme.of(context).textTheme.headline4,
    ),
  ],
),

你可以通过命令行中的格式化工具或你选择的编辑器与Dart插件一起免费获得开箱即用的格式化。

7.函数

你可以在一个类中定义一个函数--即方法--或在顶层定义。创建一个函数就像下面的语法一样简单。

// top-level
getMyname() {
// logic
}

// OR 

class MyClass() {

  getMyName() { 
  }
}

7.异步/等待

Dart通过Future或Stream提供异步编程。要定义一个Future ,你可以使用async 关键字。

Future<String> getUrl({String? prefix, String? host}) async {
  return 'd';
}

而要等到值被解决,你可以使用await 关键字。

main() async {  
  final url = await getUrl(prefix: '', host: '');
}

你应该使用await 关键字包裹在一个带有async 关键字的函数/方法中。

要创建一个Stream ,你将使用async* 关键字。现在,你可以订阅这个流,并在每次发出的时候获得这个值,直到你取消这个订阅。

 getUrl(prefix: '', host: '').listen(
    (String value) {
      print(value);
    },
  );

请注意,listen() 函数接受一个函数,即回调,因为在Dart中所有的东西都是一个对象,所以即使在函数中也可以传递它们。这在Flutter中常用于事件发生时,如onPressed

TextButton(
      onPressed: () {
        // pressed
        // logic
      },
      child: Text('Submit'),
)

了解Flutter部件树

现在,您应该能够阅读、理解和编写Flutter代码了。为了证明这一点,让我们举个例子。

class MyCustomWidget extends StatelessWidget {
  MyCustomWidget({this.counter});

  final String? counter;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text(
            'You have pushed the button this many times:',
          ),
          Text(
            '$counter',
            style: Theme.of(context).textTheme.headline4,
          ),
        ],
      ),
    );
  }
}

首先,您将创建您的自定义widget,其中使用extendsbuild() 然后,你将@override 方法。您将返回Center ,一个Flutter预定义的widget,有几个名称参数,包括您指定的childColumn

Column 有几个名称参数,其中你只使用mainAxisAlignmentchildren 。你会有两个Text widget,在这里他们既有位置参数又有名称参数,以此类推。

你现在会看到你是多么容易理解这段代码,你现在甚至可以写出你的代码了

总结

Flutter是一项神奇的技术,可以帮助你创建一个跨平台的应用程序,而Dart是它的基础。只要你知道从哪里开始,先学什么,Dart就很容易学。

在这篇文章中,我们回顾了Flutter中最广泛使用的基础知识,这样您就可以打开一个Flutter应用程序,不仅了解最初的语法,而且也可以编写您的Dart代码。