在开始之前,先打一个预防针。 这篇文章演示用到的app相对简单,几乎没有业务逻辑。 示例很基础,但这是分享将原生Android应用移植到Flutter的最好例子。该示例没有任何架构,就是最纯粹的原生调用。

一年前,我在Play Store上架了第一款Android应用。 该应用的架构和编码都非常简单; 这是我的第一个大型开源项目,这个app见证了我的Android学习道路。之后在一家代理公司工作,接触到了不同的技术和架构,包括Kotlin,Dagger,RxJava,MVP,MVVM,VIPER等,这些对我的Android开发确实有帮助。

然而,在过去的几个月里,很想吐槽一下Android开发,特别是关于兼容性。关于每次调试的构建时间更是无力吐槽……(强烈推荐这篇文章,它会深入探讨更多细节),Kotlin和Databinding的出现让问题有所改善,但仍然是杯水车薪。 Flutter可以说出现的很及时。

几个月前我开始使用Flutter,那时还是beta版。通过官方文档和示例开始了Flutter的学习旅程(文档写的特别棒)。 很快,我开始理解Flutter背后的设计思想,并决定自己尝试一下,看看能否将Flutter投入使用。 一开始我在想用什么项目来练手,考虑后决定移植我的第一款Android应用到Flutter。 这似乎是一个合适的选择,因为它可以让我以入门的姿态比较两种框架的优劣,同时不会过分关注应用程序架构。

我首先创建了网络请求,解析JSON,并习惯了Dart的单线程并发模型(这可单独作为一个主题来讲)。 在接收到网络请求响应后,开始创建列表布局和列表元素。 Flutter创建布局的只需要扩展各种Widgets并重载几个方法。 接下来我会比较Flutter和Android在构建这些功能时的差异。 让我们从在Android中构建此列表所需的步骤开始:

  1. 用XML创建list-item布局文件
  2. 创建一个适配器来绑定视图并设置数据
  3. 为列表创建布局(可能在Activity或Fragment中)
  4. 填充Fragment/Activity中的列表布局
  5. 在Fragment / Activity中创建适配器,布局管理器等的实例
  6. 在后台线程上从网络下载电影数据
  7. 回到主线程设置适配器中的项目
  8. 现在需要考虑保存和恢复列表状态等细节……

当然,这很繁琐。 构建这些功能其实是相当普通的任务,这是一个很寻常的用例, 你可能很想知道:**是否有更好的方式来实现?**一种不太容易出错的方式,**能否只涉及较少的样板代码,提高开发速度?**下面该Flutter入场了。

Flutter吸收了移动开发领域多年来在应用程序开发,状态管理,应用程序架构等方面积累的经验,这也是为什么会与React.js如此相似的原因。用Flutter的方式来构建应用时正确的开始。下面看看如何在Flutter中实现上面的例子:

为电影项目创建一个无状态的Widget(无状态,因为包含静态属性),接收一个movie(例如Dart类)作为构造函数参数,并以声明方式描述布局,同时绑定电影的值(名称 ,发布日期等)到Widget

为列表创建一个Widget。

@override
Widget build(BuildContext context) {
return new FutureBuilder(
future: widget.provider.loadMedia(widget.category),
builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
return !snapshot.hasData
? new Container(
child: new CircularProgressIndicator(),
)
: new ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) =>
new MovieListItem(snapshot.data[index]),
);
}
);
}

让我们看看这里发生了什么。 最重要的是,我们使用了FutureBuilder(Flutter SDK的一部分),它需要我们指定一个Future(回调)和一个构建器函数。 构建器函数为我们提供了一个BuildContext和要返回的项目的索引。 使用这个,我们可以检索一个电影,给定Future的结果列表,快照,并创建一个MovieListItem-Widget(在步骤1中创建),并将该电影作为构造函数参数。

然后,当第一次调用构建方法时,开始等待Future回调的返回结果。 一旦得到返回结果,构建器会再次被调用,我们可以用返回结果来构建我们的UI。

这两个类与API调用结合起来会有以下结果:

这貌似太简单了……现在有没有感觉到用Flutter创建列表很容易,继续探索吧。

下一步我们尝试稍微复杂的布局。 该应用的电影详情有相当复杂的布局,包括约束布局应用程序栏。 这样的布局展示能获得用户的青睐,如果Flutter想要在Android里有立足之地,那么需要能够提供更复杂的布局方式。 下面看看我是如何构建的:

该布局由SliverAppBar组成,其中包含电影图像的堆叠布局,渐变,气泡和文本图层。 能够以模块化的方式表达布局使得创建这种相当复杂的布局变得非常简单。 下面是实现:

@override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: primary,
body: new CustomScrollView(
slivers: [
_buildAppBar(widget._mediaItem),
_buildContentSection(widget._mediaItem),
],
)
);
}

在构建布局时,我将布局的各个部分模块化为变量,方法或其他小部件。 例如,图像顶部的文字气泡只是另一个小部件,它将文本和背景颜色作为参数。 创建一个自定义视图就像这样简单:

import ‘package:flutter/material.dart’;

class TextBubble extends StatelessWidget {
final String text;
final Color backgroundColor;
final Color textColor;

TextBubble(this.text,
{this.backgroundColor = const Color(0xFF424242),
this.textColor = Colors.white});

@override
Widget build(BuildContext context) {
return new Container(
decoration: new BoxDecoration(
color: backgroundColor,
shape: BoxShape.rectangle,
borderRadius: new BorderRadius.circular(12.0)),
child: new Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 6.0),
child: new Text(
text,
style: new TextStyle(color: textColor, fontSize: 12.0),
),
),
);
}
}

想象一下,在Android中构建一个像这样的自定义视图有多困难。不过,在Flutter,这分分钟解决。能够将用户界面的一部分抽取到像Widget这样的自包含单元中,可以轻松地在应用程序中甚至跨不同应用程序重复使用这些小部件。这个应用中,布局的很多部分都在不同界面上重复使用,并让我告诉你:这真的很简单。前面太容易了,我决定扩展应用程序,合并电视节目。几个小
时后也顺利完成了。该应用程序包含了电影和电视节目,并且开发过程中没有遇到任何困难。我通过构建用于加载和显示数据的泛型类来实现,这使得我可以重复使用电影和演出的每个布局。如果用Android实现相同的事情,我必须为电影和演出分别使用不同的Activity。可以想象这让维护工作瞬间变得复杂,并且Android对于布局的共享处理方式不太灵活。

在Flutter体验结束时,我得出了一个非常直接和令人信服的结论:

我编写了更易维护的跨平台代码。 同时花费了更少的时间写了更少的代码。

现在不用再像Fragment一样去管理状态,这很繁琐也很容易出错。 不用再为一点点修改而重新构建应用,浪费时间。 不再有XML布局, 也不再有findViewById。 不再有多余的样板代码 。

既然两个app的功能几乎一样,我就比较好奇两种不同语言实现的代码量。 那么应该如何进行对比?(免责声明:Flutter版本中还没有实现持久化,原生代码写的也很乱)。 我们使用Cloc来进行代码的比较,为了简单起见,我们来看Android上的Java和XML文件,以及Flutter版本的Dart文件。

#####原生Android中的Java:

Meta-Data for the native Android app

http://cloc.sourceforge.net v 1.60 T=0.42 s (431.4 files/s, 37607.1 lines/s)

Language files blank comment code

Java 83 2405 512 8599
XML 96 478 28 3577
Bourne Again Shell 1 19 20 121
DOS Batch 1 24 2 64
IDL 1 2 0 15

SUM: 182 2928 562 12376

#####Flutter:

Meta-Date for the Flutter app

http://cloc.sourceforge.net v 1.60 T=0.16 s (247.5 files/s, 14905.1 lines/s)
12376

#####Flutter:

Meta-Date for the Flutter app

http://cloc.sourceforge.net v 1.60 T=0.16 s (247.5 files/s, 14905.1 lines/s)

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐