
在收到Yura Luchaninov的报告后,我决定亲自尝试Flutter。 为了伸展大脑,厨房里的男人有些不寒而栗。 事情已经过去了。 我开始看,然后看,然后写。 一切似乎都可以解决,应用程序已启动,它们所解释的内容是可以理解的,一切都很简单。 但并非没有“但是”-并非所有人都解释。 而且由于平台,YP,方法甚至主题领域对我来说都是新的,所以这种烦恼是因为您“不开始”,甚至都不知道要搜索什么:Dart / Flutter / Window / Screen / Route / Widget?
当然,我不想重读Dart,Flutter及其小部件的所有文档,因为我没有太多时间,我只是想仔细看一下Flutter。 如果有一个简短的指南来描述您需要的所有内容,那将是非常不错的选择,但仅此而已,不再需要更多的内容,以便在Flutter上理解和编写不太复杂的应用程序!
关于指南
关于该主题的大多数文章都写得很好,并不复杂。 问题在于,其中大多数都需要被认为是基础知识的知识,但是,在其他描述基础知识的文章中并未提及这些知识。 在本系列文章中,我想纠正这种情况。 让我们从头开始,在不引起任何上述注意的情况下,我们将启动一个或几个应用程序。 在此过程中,我们将学习如何使用所有主要组件 ,如何创建唯一的界面 ,如何使用本机模块 ,以及为两个平台组装应用程序 。
我将从网络开发人员的角度进行撰写。 你们中的大多数人最有可能熟悉Web堆栈,并且与熟悉的平台进行类比比对房屋,动物,狗,Foo,Bar等进行类比更好。
我将简要概述一下,以免延误。 对于最好奇的事情,我将在所讨论的主题上留下有用的链接。
Flutter是一个年轻但非常有前途的平台,已经吸引了启动其应用程序的大公司的关注。 这个平台的有趣之处在于它的简单性可与Web应用程序的开发相提并论,并且其工作速度与本机应用程序相当。 多种技术可实现较高的应用程序性能和开发速度:
- 与当今许多著名的移动平台不同,Flutter 不使用任何形式的JavaScript 。 作为Flutter的编程语言,他们选择了Dart,它将Dart编译成二进制代码,从而达到了与Objective-C,Swift,Java或Kotlin相当的操作速度。
- 同样,Flutter 不使用任何形式的本机组件 ,因此您不必编写任何与它们进行通信的层。 相反,就像游戏引擎(并且您知道游戏具有非常动态的UI)一样,它自己绘制整个界面。 按钮,文本,媒体元素,背景-所有这些都在Flutter本身的图形引擎中绘制。 经过以上讨论,值得注意的是Flutter上的“ Hello World”应用程序占用的空间很小:iOS≈2.5Mb和Android≈4Mb。
- Flutter 使用一种基于ReactJS Web框架的声明方法 ,该框架基于小部件(在网络世界中称为组件)构建UI。 为了更大程度地提高界面速度,仅在需要更改窗口小部件时才重绘窗口小部件 (就像Virtual DOM在Web前端世界中一样)。
- 除此之外,该框架还具有内置的 Hot-reload ,它对Web非常熟悉,但在本机平台上仍然不可用。
关于这些因素的实际好处,我强烈建议阅读Android开发人员的文章,该文章将其应用程序从Java重写为Dart,并分享了他的印象。 在这里,我只是取出由他命名的文件/代码行的数目(用Java编写)之前-179/12176,然后(用Dart重写)之后-31/1735。 在文档中,您可以找到有关平台技术功能的详细说明。 如果您有兴趣查看其他可运行应用程序的示例 ,那么这里是另一个链接。
关于达特
Dart是一种编程语言,我们必须用它编写Flutter的应用程序。 这非常简单,如果您有Java或JavaScript的经验,您将很快学习它。
我试图写一篇有关Dart的评论文章,试图仅描述学习Flutter的最低要求。 但是这种语言有很多细微差别,尽管多次尝试撰写这样的文章,但我仍然无法使它完整而又简短。 另一方面,《 飞镖语言之旅》的作者在这方面做得很好。
关于培训
与Dart一样,该主题在官方指南中也有很好的描述。 我只能在这里复制它,但我不会这样做。
无需等待,我们转到安装指南页面,选择平台,然后按照以下步骤在系统上安装平台。 在我们的编辑器中,我们将连接插件。 在同一指南中,有设置VS Code和IntelliJ的说明 。 还为您的编辑器提供了Dart和Flutter插件(通常需要安装两个插件)。 我们启动该应用程序并检查其性能 。
给OSX用户的提示。 我为iOS模拟器中手机的画框所占用的空间感到抱歉,因此我关闭了它们并切换到iPhone 8(它不太“长”):
Hardware → Device → iOS # → iPhone 8
Window → Show Device Bezels
您可以不使用按钮,因为这里有热键: Shift + Cmd + H
这是家, Cmd + Right
-这是将手机翻转过来的功能,其余的都可以在“ Hardware
菜单中找到。 但我建议您打开屏幕键盘,因为了解在定期有一半屏幕被键盘挡住的情况下是否可以使用该应用程序很重要: Cmd + K
(当焦点位于某个输入字段时有效)。
带框架的iPhone 8和iPhone X

iPhone 8和iPhone X无边界

关于结构
我们将进入包含生成的应用程序的文件夹,并查看其中的内容。 不是所有的东西,而是正确的东西:
lib/
-根据pub (Dart的包管理器)的原则,所有代码都位于此子文件夹中;pubspec.yml
这是编写应用程序依赖项的位置,您需要安装该依赖项才能运行它,就像package.json
一样,但有一点细微差别,您不需要通过上述标准Dart实用程序而是通过Flutter命令来安装它们: flutter pub get <package_name>
;test/
--您知道那里是什么吗? 您可以通过调用flutter test
来运行它们;ios/
& android/
-包含每个平台设置的文件夹,它指示运行该应用程序需要哪些权限(访问位置,蓝牙),图标以及该平台特定的所有内容。
我们确定了结构,转到main.dart
文件正在等待我们的lib/
文件夹。 您可以猜测,这是我们必须在其中运行应用程序的文件。 它通过调用main()
函数像在C语言(甚至其他许多语言)中一样开始。
关于小部件(此处为Hello World)
在Flutter中,一切都建立在Widget的基础上: Widget中有视图,带有主题的样式和状态。 窗口小部件有两种主要类型:有状态和无状态,但目前还没有。 让我们轻松一点。
从main.dart中删除所有内容 。 粘贴以下代码,仔细阅读注释:
import 'package:flutter/widgets.dart';
runApp(…)
接受一个参数-一个小部件,它将成为整个项目的根。 顺便说一句,热重载无法将其拾取,因此您将需要重新启动应用程序。
Text(…)
-Flutter不能仅在屏幕上显示字符串。 要显示文本,必须指定Text
。 textDirection
。 而且这不是像text-align
那样的text-align
,与网络相比,它是Direction的类似物。 API的一部分,用于使应用程序国际化。 在知道方向之前, Text
行不通的,但是您不必在任何地方都指定文本-接下来,我们将分析如何为整个应用程序调整文本的方向。
已经启动了该应用程序? “你好,世界!” 出来! 好像是...是吗? 但是显然出了点问题。

该文本被系统信息阻止。 我们拥有所有可用的屏幕空间,并且从一开始就带出了小部件,其中显示了系统信息。 让我们尝试将文本移动到某个地方。
import 'package:flutter/widgets.dart'; main() => runApp( Center(
Center(…)
是一个小部件,可让您将在child
参数中传递的另一个小部件水平和垂直放置在中心。 在Flutter应用程序中,您经常会看到child
和children
,因为几乎所有小部件都使用这些名称来传达必须在调用的小部件内绘制的小部件。
在Flutter中使用小部件合成来呈现UI,更改外观甚至传输数据。 例如,“ Directionality(…)
小部件Directionality(…)
设置所有子小部件的文本方向:
import 'package:flutter/widgets.dart'; main() => runApp( Directionality( textDirection: TextDirection.ltr, child: Center( child: Text('Hello, World!'), ), ), );
让我们看一下另一个非常重要的小部件,同时变换我们应用程序的外观:
import 'package:flutter/widgets.dart'; main() => runApp( Directionality( textDirection: TextDirection.ltr, child: Container(

Color(…)
-颜色。 该文档说明了设置它的不同方法,但是主要的事情只是将数字传递给类构造函数。 在上面的示例中,我们向构造函数传递了一个以十六进制形式编写的数字,该数字与十六进制非常相似,只是在开始时我们又添加了两个字符来指示颜色的透明程度,其中0x00绝对透明,而0xFF根本不透明。
TextStyle(…)
-一个更加有趣的小部件,使用它可以设置颜色,大小,粗细,行距,添加下划线等。
Flutter应用程序编写完成! 在扩展坞中,您可以了解如何为Android和iOS组装它,并且在同一位置有链接可以让您知道如何将其发送到所需的商店。 对于谁来说这还不够,下面我对Flutter进行了更多的介绍,也许更多...
专业无状态小部件
如何使用小部件-我们已经弄清楚了,现在让我们弄清楚如何创建它们。 上面已经提到过,有些小部件具有状态,而没有状态。 到目前为止,我们仅使用了无状态小部件。 这并不意味着它们根本没有它,因为小部件只是类,并且它们的属性可以更改。 渲染小部件之后,更改其状态不会导致在UI中更新此小部件。 例如,如果我们需要更改屏幕上的文本,则需要生成另一个“ Text
小部件并指定要显示的新内容。 如果您知道我的意思,那么此类小部件可以称为常量。 而且它们很简单,所以让我们从它们开始。
要创建无状态小部件,您需要:
- 为新班级起一个漂亮的名字;
- 从
StatelessWidget
继承一个类; - 实现一个以
BuildContext
作为参数并返回某种Widget
的build()
方法。
import 'package:flutter/widgets.dart'; main() => runApp( Directionality( textDirection: TextDirection.ltr, child: Center( child: MyStatelessWidget() ), ), ); class MyStatelessWidget extends StatelessWidget {
具有一个参数的示例窗口小部件:
关于无状态,没有什么可添加的...
关于热装
请注意,更改小部件的内容时,该应用程序将自动重绘。 从main()
函数中删除小部件后,热重装开始为我们提供帮助。
同样重要的是要了解,由于启动了用于热插拔的模块,该应用程序的运行速度降低了一个数量级。
关于GestureDetector

在下一节中,我们将处理StatefulWidget
( StatefulWidget
改变时会更改的小部件)。 为了使它有趣,我们需要以某种方式更改此状态,同意吗? 我们将响应屏幕上的触摸来更改小部件的状态。 为此,我们将使用GestureDetector(…)
-一个小工具,它不会绘制任何内容,但会监视智能手机屏幕上的触摸并报告有关该智能手机屏幕的信息,从而调用已传递给它的功能。
在屏幕中央创建一个按钮,单击该按钮将在控制台中显示一条消息:
import 'package:flutter/widgets.dart'; main() => runApp( Directionality( textDirection: TextDirection.ltr, child: Container( color: Color(0xFFFFFFFF), child: App(), ), ), ); class App extends StatelessWidget { @override Widget build(BuildContext context) { return Center( child: GestureDetector(
单击蓝色按钮,然后在控制台中看到消息。 我们一次又一次地单击,我们在控制台中看到该消息。 再次...好吧,别再坚持了。
有状态的小部件
StatefulWidget
简单,甚至比StatelessWidget
更简单。 但是有一个细微差别:它们本身并不存在,为了进行工作,我们需要另一个类来存储此小部件的状态。 同时,它的可视部分(包括它的小部件)也变为其状态。
首先,请查看小部件类:
上面,我们创建了一个“空”小部件,该小部件实现了一个非常简单的createState()
方法。 表示和状态的这种分离使Flutter可以极大地优化应用程序。
状态对象是完全简单的。 而且,它几乎与上面我们写的StatelessWidget
相同。 它的主要区别是父类。

请注意,类名以下划线开头。 在Dart中,所有以下划线开头的名称都标识私有值。 而且Flutter中的小部件状态通常是私有的,尽管这不是必需的。
我们做了多么出色的应用程序! 这是一个很好的结果。 但是在完成本部分课程之前,让我们看几个更有趣的小部件。 仅这次,我们将编写更多代码,只是为了使其更加有趣。 大多数应用程序应该是您熟悉的,其余的您应该已经学会了:
import 'package:flutter/widgets.dart'; main() => runApp(App()); class App extends StatelessWidget { @override Widget build(BuildContext context) { return Directionality( textDirection: TextDirection.ltr, child: Container( padding: EdgeInsets.symmetric( vertical: 60.0, horizontal: 20.0, ), color: Color(0xFFFFFFFF), child: Content(), ), ); } } class Content extends StatelessWidget { @override Widget build(BuildContext context) { return Column( children: [ Counter('Manchester United'), Counter('Juventus'), ], ); } } class Counter extends StatefulWidget { final String _name; Counter(this._name); @override State<Counter> createState() => _CounterState(); } class _CounterState extends State<Counter> { int count = 0; @override Widget build(BuildContext context) { return Container( margin: EdgeInsets.only(bottom: 10.0), padding: EdgeInsets.all(4.0), decoration: BoxDecoration( border: Border.all(color: Color(0xFFFD6A02)), borderRadius: BorderRadius.circular(4.0), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [

我们有两个新的小部件: Column()
和Row()
。 尝试弄清楚他们在做什么。 在下一篇文章中,我们将更详细地讨论它们,并研究多个控件,使您可以将其他控件组合在一起,并使用Flutter库(称为Material)创建一个不错的应用程序。
关于作业
如果您想在闲暇时阅读其他内容,下面列出了一些有趣的链接: