我要告诉你一个秘密:多年来我一直讨厌移动开发。我曾经想喜欢它——移动是未来!它很酷!它功耗低!它是一种与首次接触计算机的用户建立联系的方式,这些用户并非来自传统的桌面平台!然而……对我来说,开发是一种缓慢而令人沮丧的体验。相反,我把自己隔离在完全没有问题的 Web 开发领域,并为 HTML blink
标签的消失而哀悼(开玩笑的)。
然后,我发现了 Flutter,这是一个由 Google 开发的开源移动应用 SDK,使开发人员可以使用相同的代码库为 iOS 和 Android 创建移动应用。
一旦我发现了 Flutter,我发现移动开发可以变得令人愉快。
是的,令人愉快。
你怎么问?为了向您展示我的意思,让我们一起编写一个非常简单的 Flutter 应用程序,该应用程序查询 Stack Overflow。作为有自尊心的开源开发人员,我相信您希望随时了解人们在 Stack Overflow 上提出的关于您的软件的问题。这个应用程序允许您搜索关于特定主题的问题。
闪电般快速的开发周期
传统的编译器很麻烦。您知道它是怎么回事:您点击“编译”,然后您意识到自己已经深入浏览了 10 个选项卡的可爱小猫照片——而且已经午餐时间了。幸运的是,当我在 Web 上工作时,解释器和即时 (JIT) 编译器使我摆脱了对小猫的痴迷,使“编辑、保存、刷新”成为游戏规则。
Flutter 将这种快速开发理念更进一步:“编辑、保存”。虽然它不是 Web 技术,但得益于 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
的 future 中可用。即使我们正在往返服务器,我们也不需要传入回调;我们可以只使用 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 进行开发,这有可能降低类型安全性(因此在运行时会有更多不必要的意外),并且代码是解释或 JIT 编译的。使用 Xamarin,您可以通过 C# 获得强大的类型安全保证,并且根据目标平台,代码被编译为本机代码、JIT 编译或在虚拟机上运行。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 将在 7 月 16 日至 19 日在俄勒冈州波特兰举行的第 20 届年度 OSCON 活动上展示 从零开始实时编码一个美观、高性能的移动应用程序。
5 条评论