我要告诉你一个秘密:多年来我一直讨厌移动开发。我想喜欢它——移动是未来!它很酷!它低功耗!它是一种与那些第一次接触计算机不是来自传统桌面平台的用户建立联系的方式!然而……对我来说,开发是一种缓慢而令人沮丧的体验。相反,我把自己隔离在网络开发的*完全没有问题*的领域,并哀悼 HTML blink
标签的消失(开玩笑的)。
然后,我发现了 Flutter,这是一个由 Google 开发的开源移动应用 SDK,使开发人员可以使用相同的代码库来创建 iOS 和 Android 的移动应用。
一旦我发现了 Flutter,我发现移动开发可以是*令人愉快的*。
是的,令人愉快的。
你怎么做到的?为了向你展示我的意思,让我们一起编写一个非常简单的 Flutter 应用,该应用查询 Stack Overflow。作为你这个有自尊心的开源开发人员,我相信你想要随时了解人们在 Stack Overflow 上提出的关于你的软件的问题。这个应用允许你搜索关于特定主题的问题。
闪电般快速的开发周期
传统的编译器很麻烦。你知道它是怎么回事:你点击“编译”,然后你意识到自己已经深入浏览了 10 个可爱的小猫照片标签——而且已经是午餐时间了。幸运的是,当我在网络上工作时,解释器和即时 (JIT) 编译器将我从对小猫的痴迷中拯救出来,使“编辑、保存、刷新”成为游戏的名称。
Flutter 将这种快速开发理念更进一步:“编辑,保存”。即使它不是一种网络技术,由于 Flutter 的热重载,你也可以在*不到一秒*的时间内在移动设备的屏幕上看到你的更改。
通常,你可以通过使用花哨的、动态类型的脚本语言来获得这种快速的开发周期,但缺点是将错误推迟到运行时,而不是在编译时预先捕获它们。第二个常见的缺点是它们的性能不如编译语言那样快。通过使用 Dart 作为其首选编程语言,Flutter 可以避开这两个问题。 Dart 具有强大的、健全的类型系统,允许你在演示测试覆盖率低于理想状态的代码库之前捕获问题。
其次,Dart 有两种模式
- 在 Dart 虚拟机上以“解释”模式运行,这允许令人愉快的热重载体验,以及;
- 编译模式,它在准备发布应用时将你的应用编译为本机机器代码。
鉴于这些特性,Dart 独特地适合为开发人员提供出色的 Flutter 开发和发布体验。
最后,Dart 被设计成易于学习,因此如果你使用过任何 C 风格的语言(如 Java、C++ 或 JavaScript),你会觉得它很熟悉。
很酷的特性,如 Streams 和 Futures
是时候开始编码了!我们的应用将使用 Stack Overflow API 来查找关于 Flutter 的需要回复的问题,以便你,作为无畏的开源项目所有者,可以通过保持社区的知情来帮助你的社区。在 Dart 中获取该信息的最简单方法是使用异步请求
final url = 'https://api.stackexchange.com/2.2/questions?
order=desc&sort=activity&tagged=flutter&site=stackoverflow';
var result = await http.get(url);
print(result.body);
结果会打印出一些 JSON,看起来像这样
{
"items": [
{
"tags": [
"android",
"ios",
"flutter"
],
"owner": {
"reputation": 1,
...
},
is_answered: false,
"view_count": 1337,
"title": "How to make a pretty Flutter app?"
...
}
在此代码片段中,http.get
返回一个 Future<Request>
,这意味着结果将在未来的 (Http)Request
类型中可用。即使我们正在进行服务器的往返,我们也不需要传入回调;我们可以只使用 await
关键字来等待响应。 Flutter 具有 FutureBuilder
和 StreamBuilder
小部件,可以根据 Future
或 Stream
的结果构建相应的 UI 组件。
Stream
就像 Future
一样,只不过它可以异步地多次提供结果,而不仅仅是一次。在我们的应用中,我们将创建一个 Stream,我们可以在其中监听关于我们感兴趣的 Stack Overflow 问题的更新。由于 Stack Overflow API 没有提供开箱即用的推送通知,我们使用 StreamController
构建自己的流,并在我们获得更新的 Stack Overflow 信息时添加它们
final controller = StreamController<List<String>>();
void refreshQuestions() async {
var result = await http.get(url);
Map decoded = json.decode(result.body);
List items = decoded['items'];
controller.add(items
.where((item) => !item['is_answered'])
.map<String>((item) => item['title'])
.toList());
}
在 refreshQuestions
中,我们对 Stack Overflow API 进行新的调用,然后过滤结果,以便我们只查看尚未回答的问题。从这些结果中,我们提取问题的标题以在我们的应用中显示它们。
Flutter 方便地提供了一个 StreamBuilder
小部件,它可以根据流的内容自动更新用户在应用中看到的内容。在这种情况下,我们提供输入流源 (controller.stream
) 并显示不同的结果,具体取决于我们是否成功接收到数据(在本例中构建非常令人兴奋的 Text
小部件)。 StreamBuilder
还方便地负责从流中取消订阅并进行清理。
StreamBuilder(
stream: controller.stream,
builder: (BuildContext context, AsyncSnapshot<List<String>> snapshot) {
if (snapshot.hasError)
return Text('Error ${snapshot.error}');
else if (snapshot.connectionState == ConnectionState.waiting) {
return Text('Receiving questions...');
}
return Expanded(
child: ListView(
children: snapshot.data
.map<Widget>((info) => Text(info))
.toList()));
});

上面代码的结果。(我不明白为什么没有设计学校会接受我。)
适用于 iOS 和 Android 的一种技术
“不要重复自己”是一种常见的软件工程信条,但移动开发世界似乎否认这一点。通常,公司会建立独立的 iOS 和 Android 应用团队,每个团队都需要两次解决相同的问题。使用 Flutter,你可以用 Dart 编写代码并以原生方式部署到 iOS 和 Android。滚动行为、系统字体和其他基本交互组件会自动默认为你正在使用的平台。在更高的层面上,Flutter 提供了 Cupertino 和 Material Design 小部件库,以获得用户期望在其选择的平台上的外观和感觉。
在我们的 Stack Overflow 应用中,我们想要有一个“获取新结果”按钮来查看是否有需要我们注意的新问题。我们将编写一个 PlatformAdaptiveButton
,其行为取决于我们正在运行的平台
class PlatformAdaptiveButton extends StatelessWidget {
final Widget child;
final Widget icon;
final VoidCallback onPressed;
PlatformAdaptiveButton({Key key, this.child, this.icon, this.onPressed})
: super(key: key);
@override
Widget build(BuildContext context) {
if (Theme.of(context).platform == TargetPlatform.iOS) {
return CupertinoButton(
child: child,
onPressed: onPressed,
);
} else {
return FloatingActionButton(
icon: icon,
onPressed: onPressed,
);
}
}
}
然后,在我们的 Flutter 应用中,我们可以简单地构造
return PlatformAdaptiveButton(
child: const Text('Refresh'),
icon: const Icon(Icons.refresh),
onPressed: refreshQuestions);
当按下时,它会从 Stack Overflow API 请求更新。 Flutter 的路线图要求在你的代码中提供更多内置的平台自适应组件的方式,所以请继续关注。
一些其他的应用开发系统也提供了跨平台功能:React Native、Xamarin 和 Ionic 等。使用 React Native 和 Ionic,你可以用 JavaScript 进行开发,这可能会导致更少的类型安全(因此在运行时会有更多不必要的意外),并且代码会被解释或 JITed。使用 Xamarin,你可以使用 C# 获得强大的类型安全保证,并且根据目标平台,代码会被编译为原生代码、JITed 或在虚拟机上运行。 Flutter 在 iOS 和 Android 上都编译为本机机器代码,从而提供可预测的、快速的性能。
定制
“但是 Emily,”你说。“我在一家机构工作,我根本不能让我创建的所有应用看起来都一样!我需要它们看起来与众不同并添加那些时尚的触感,就像我的签名,雅致地使用 Comic Sans!” 不要害怕,我的审美家朋友。 Flutter 在这方面确实表现出色。
因为 Flutter 正在将每个像素绘制到屏幕上,所以*一切*都是可定制的。不喜欢内置的 CupertinoButton
的行为方式?创建一个子类并自己设计它。认为纯色应用栏已经过时了?编写你自己的小部件。在我们的 Stack Overflow 应用中,我编写了一个自定义应用栏,它具有自定义字体和渐变颜色方案,以将其与其他所有无聊的应用栏区分开来——而且代码也不多
@override
Widget build(BuildContext context) {
final double statusBarHeight = MediaQuery.of(context).padding.top;
return Container(
padding: EdgeInsets.only(top: statusBarHeight),
height: statusBarHeight * 4,
child: Center(
child: Text(
title,
style: const TextStyle(
color: Colors.white, fontFamily: 'Kranky', fontSize: 36.0),
),
),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.deepOrange,
Colors.orangeAccent,
],
),
),
);
}
你可以在下面看到最终结果。
![]() |
![]() |
我为这篇文章编写的所有代码都可以在 GitHub 上找到:Stack Overflow Viewer。
感兴趣吗?
我们可以做的事情还有很多!但是,进一步的定制留给读者作为练习……或者作为吸引你来 OSCON 的诱饵,在那里我们将从头开始编写一个完全不同、更有用且更美观的应用,供开源爱好者使用。
Emily Fortuna 和 Matt Sullivan 将在第 20 届年度 OSCON 活动中展示 从头开始实时编码一个美观、高性能的移动应用,该活动将于 7 月 16 日至 19 日在俄勒冈州波特兰举行。
5 条评论