当涉及到跨平台开发时,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 ,它形成一个编译时常量。
但也有更复杂的情况。让我们来看看Map 和List 的类型定义。
// 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都是StatelessWidget 或StatefulWidget 的扩展类。因此,您可以创建您的widget(类)。
class MyApp extends StatelessWidget {
}
StatelessWidget 和State 对象对应的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,其中使用extends 。build() 然后,你将@override 方法。您将返回Center ,一个Flutter预定义的widget,有几个名称参数,包括您指定的child ,Column 。
Column 有几个名称参数,其中你只使用mainAxisAlignment 和children 。你会有两个Text widget,在这里他们既有位置参数又有名称参数,以此类推。
你现在会看到你是多么容易理解这段代码,你现在甚至可以写出你的代码了
总结
Flutter是一项神奇的技术,可以帮助你创建一个跨平台的应用程序,而Dart是它的基础。只要你知道从哪里开始,先学什么,Dart就很容易学。
在这篇文章中,我们回顾了Flutter中最广泛使用的基础知识,这样您就可以打开一个Flutter应用程序,不仅了解最初的语法,而且也可以编写您的Dart代码。