我喜欢和不喜欢Flutter的东西

Let’s start with some background information on how I got here. I already made apps for native Android in Kotlin, apps for Android and iOS with React Native, and shortly decided to use Flutter for a small app for a small company. It was very clear what the upsides and downsides were with regards to working with Flutter as opposed to React Native. I wanted to share my findings of what I like and don’t like about Flutter in this post.

让我们从一些有关如何到达这里的背景信息开始。 我已经在Kotlin中为本地Android开发了应用程序,并通过React Native为Android和iOS开发了应用程序,不久就决定将Flutter用于小型公司的小型应用程序。 很明显,与Flutter而不是React Native相比,在Flutter上工作有什么好处和坏处。 我想在这篇文章中分享我对Flutter的喜欢和不喜欢的发现。

✔颤动 (✔ Running Flutter)

Running on multiple devices is very straightforward, no more running specific commands for each device you want to use, whether it is Android or iOS. Just execute flutter run. If you have multiple emulators or simulators running Flutter will notify you on which device you would like to run your application. You can run it on all of them with flutter run -d all.

在多个设备上运行非常简单,无需为要使用的每个设备(无论是Android还是iOS)运行特定的命令。 只需执行flutter run 。 如果您有多个运行模拟器的软件,Flutter会通知您您要在哪个设备上运行应用程序。 您可以使用flutter run -d all在所有它们上运行它。

✔更少的样板 (✔ Less Boilerplate)

Flutter is really clean to start with, it only has a few needed files. No Redux-Saga boilerplate, no initializing of barrel files, etc. There’s only a main.dart in the lib folder. It’s very easy to start.

Flutter一开始确实很干净,只有几个需要的文件。 没有终极版-佐贺样板,没有初始化的桶文件等只起了main.dartlib文件夹中。 开始非常容易。

standard main.dart
标准main.dart

✔一切都是小部件 (✔ Everything Is A Widget)

As you’d already probably noticed from the .dart files if you are familiar with Flutter, (most) components are considered a parent for one or multiple child components. It’s really easy to build your application. The most basic one is the Container component, it can have a fixed height and width. Commonly used components are the Row and Column, they specify how you want to group the children, horizontally or vertically. Another useful component is the Card, it has an ‘elevation’ parameter so you can easily create an elevated effect. I found it very convenient to build an app with the motto of ‘everything is a widget’. The documentation about Flutter layouts is very comprehensive.

正如您可能已经从.dart文件中注意到的那样,如果您熟悉Flutter,(大多数)组件将被视为一个或多个子组件的父组件。 构建您的应用程序真的很容易。 最基本的是Container组件,它可以具有固定的高度和宽度。 常用的组件是RowColumn ,它们指定了如何水平或垂直分组子级。 另一个有用的组件是Card ,它具有一个“ elevation”参数,因此您可以轻松地创建一个增强的效果。 我发现以“一切都是小部件”的座右铭构建应用程序非常方便。 关于Flutter布局的文档非常全面。

✔启动器图标 (✔ Launcher Icons)

Launcher icons can be cumbersome to implement. Android and iOS both have different ways to integrate them. No more fiddling with xxhdpi images or opening XCode, there’s a package called flutter_launcher_icons that updates your icons based on only one png. You place your icon in the assest/icon folder and run the following command.

启动器图标的实现可能很麻烦。 Android和iOS都有不同的集成方式。 不再需要xxhdpi图像或打开XCode了,有一个名为flutter_launcher_icons的软件包可以仅基于一个png更新图标。 您将图标放在assest/icon文件夹中,然后运行以下命令。

flutter pub run flutter_launcher_icons:main

Before you can run this, you’ll also need to add some information on where to find the main icon in your pubspec.yaml file.

在运行它之前,您还需要添加一些有关在pubspec.yaml文件中找到主图标的pubspec.yaml

flutter_icons:
android: "launcher_icon"
ios: true
image_path_android: "assets/icon/icon_round.png"
image_path_ios: "assets/icon/icon.png"

✔哨兵整合 (✔ Sentry Integration)

Sentry is used for monitoring exceptions thrown from your app. It was very easy to set up and required almost no code editing. The only thing you need to do is change your main.dart to incorporate exception handling.

Sentry用于监视从您的应用程序引发的异常。 设置非常容易,几乎不需要任何代码编辑。 您唯一需要做的就是更改main.dart以合并异常处理。

Sentry integration main.dart
Sentry集成main.dart

✔Future-和StreamBuilder (✔ Future- And StreamBuilder)

Futures are a very powerful mechanism to execute asynchronous tasks, such as fetching data from an API. It’s not always ideal to integrate this into your application because you will have to do error handling, insert a loading spinner and also show your content whenever it is ready. Flutter designed a very useful component for this, the FutureBuilder.

期货是执行异步任务(例如从API提取数据)的一种非常强大的机制。 将其集成到您的应用程序中并不总是理想的,因为您将必须执行错误处理,插入加载微调器并在准备就绪时显示内容。 Flutter为此设计了一个非常有用的组件FutureBuilder

FutureBuilder
未来建设者

It will trigger the calculation, show the loading spinner, and depending on whether an error has been thrown or not it will show either the error message or the content the Future returned!

它将触发计算,显示正在加载的微调器,并根据是否抛出错误来显示错误消息或Future返回的内容!

There’s a downside to this, you can’t manually retrigger the FutureBuilder, this is why we will have to turn our face to the StreamBuilder. With the StreamBuilder we can manually clear the stream and trigger the Future again by adding null to the StreamController _accountController.add(null).

这有一个缺点,您不能手动重新触发FutureBuilder,这就是为什么我们必须将脸转向StreamBuilder 。 使用StreamBuilder,我们可以通过向StreamController添加null来手动清除流并再次触发Future。 _accountController.add(null)

StreamBuilder
流构建器

The FutureBuilder and StreamBuilder are very useful widgets to implement when you are working with asynchronous API calls. It’s very clean and easy to use and will automatically handle loading and error handling for you!

当您使用异步API调用时,FutureBuilder和StreamBuilder是非常有用的小部件。 它非常干净且易于使用,将自动为您处理加载和错误处理!

✔测试 (✔ Testing)

Mobile testing can take quite some effort to get it right. For React Native I used Jest and Enzyme, although it works quite well it was not easy to mock quite some libraries. Integration tests were done with Detox and again, this was not very straight forward to get all the boilerplate in order.

移动测试可能需要花费很多精力才能正确完成。 对于React Native,我使用了JestEnzyme ,尽管它工作得很好,但是要模拟很多库并不容易。 集成测试是使用Detox ,同样,要使所有样板井然有序不是很简单。

With Flutter, testing has been made easy. Integration tests require setting up 2 files, app.dart and app_test.dart in a folder called test_driver. You can execute the integration test by running the Flutter drive test command.

使用Flutter, 测试变得很容易。 集成测试需要建立2个文件, app.dartapp_test.dart在一个文件夹,名为test_driver 。 您可以通过运行Flutter驱动器测试命令来执行集成测试。

flutter drive --target=test_driver/app.dart
app.dart integration test
app.dart集成测试
app_test.dart integration test
app_test.dart集成测试

As you can see, this code snippet also contains a part of code you need to take automated screenshots. This is explained in the next section on how to do so. Unit tests only require 1 file, test.dart that is placed in the test folder.

如您所见,此代码段还包含自动截屏所需的一部分代码。 下一节将对此进行说明。 单元测试仅需要1个文件test.dart放在test文件夹中。

test.dart unit test
test.dart单元测试

In order to execute unit tests, you’ll need the following Flutter test command.

为了执行单元测试,您需要以下Flutter测试命令。

flutter run test

✔屏幕截图 (✔ Screenshots)

Creating automated screenshots with Flutter is a breeze. The screenshots package also provides Fastlane support so you don’t have to manually move screenshots after they are taken. The only exception to this is if you use older Android tablets as I did. See the issue I created.

用Flutter创建自动屏幕截图非常容易。 screenshots程序包还提供Fastlane支持,因此您无需在捕获屏幕快照后手动移动屏幕快照。 唯一的例外是,如果您像我一样使用较旧的Android平板电脑。 请参阅我创建的问题

Screenshot taking executes automatically when running integration tests with the screenhots command. You only need to create a screenshots.yaml file in which you specify certain things like the test file you would like to run. In this case, it will run the app.dart file that was created when running integration tests in the previous section.

使用screenhots命令运行集成测试时,截屏将自动执行。 您只需要创建一个screenshots.yaml文件,即可在其中指定某些内容,例如要运行的测试文件。 在这种情况下,它将运行上一节中运行集成测试时创建的app.dart文件。

After creating the file you only need to insert following statements into it. These are also already included in the section about integration tests, it could not be easier. Automated screenshots in React Native was a pain, I eventually decided to work with Detox, which in turn worked well but was a lot (with strong emphasis) of work to set up correctly. With screenshots it’s nothing more than adding a few lines to your already excisting test files.

创建文件后,您只需要在其中插入以下语句即可。 这些也已经包含在有关集成测试的部分中,这再简单不过了。 React Native中的自动屏幕截图很痛苦,我最终决定使用Detox ,后者反过来效果很好,但是要正确设置还需要大量工作(重点很强)。 有了screenshots ,无非就是在已经存在的测试文件中添加几行。

  • import 'package:screenshots/screenshots.dart';

    import 'package:screenshots/screenshots.dart';

An import has to be added of course to find the screenshots method.

当然,必须添加导入才能找到屏幕截图方法。

  • final config = Config();

    final config = Config();

A final config has to be added at the beginning of each test file.

必须在每个测试文件的开头添加最终配置。

  • await screenshot(driver, config, 'myscreenshot1');

    await screenshot(driver, config, 'myscreenshot1');

You can insert an await screenshot in every test you want in order to take a snapshot of that current test. After filling in this information in your test files, you can trigger automated screenshots with the screenshots command.

您可以在每个所需的测试中插入一个await screenshot ,以便对该当前测试进行快照。 在将这些信息填写到测试文件中之后,您可以使用screenshots命令来触发自动截图。

✔生物特征认证 (✔ Biometric Authentication)

I found it very easy to set up biometric authentication within Flutter. The local_auth package is very easy to use and will catch all needed interactions with the underlying OS regarding biometrics. It’s possible to create an authentication.dart file, the only thing left is calling it where you want it.

我发现在Flutter中设置生物特征认证非常容易。 local_auth软件包非常易于使用,它将捕获与底层操作系统有关生物识别的所有必要交互。 可以创建一个authentication.dart文件,剩下的唯一事情就是在需要的地方调用它。

if (await Authentication.authenticate(context)) {
...
} else {
...
}
authentication.dart
身份验证

I also did this with a library in React Native, it was a lot more configuration and tweaking to get things working and in the end, it didn’t quite do everything as expected. For example, if you enter your fingerprint wrongly 3 times, it will lock your biometrics. In Flutter it displays a message “Biometric authentication is disabled. Please lock and unlock your sreen to enable it.” without any extra configuration. With React Native I would have to catch each exception and manually show messages based on which exception had been thrown. With the local_auth package I didn’t have to do anything special to get this all working as I was expecting to.

我还使用React Native中的一个来完成此操作,它需要进行大量配置和调整才能使工作正常进行,最后,它并没有像预期的那样做所有事情。 例如,如果您输入了3次错误的指纹,它将锁定您的生物识别信息。 在Flutter中,它显示一条消息“ 生物特征认证已禁用。 请锁定并解锁您的屏幕以启用它。 ”,无需进行任何额外配置。 使用React Native,我必须捕获每个异常并根据抛出的异常手动显示消息。 使用local_auth程序包,我不需要做任何特殊的事情就可以像我期望的那样使它们全部正常工作。

✘(✔)共享状态管理 (✘(✔) Shared State Management)

EDIT: Support for shared state management out of the box is not that great, but there are very good packages to handle shared state. After some useful remarks, I tried out Provider with MobX and I must say this will resolve the shared state management issues. If you are already familiar with MobX this shouldn’t be hard to get you going with shared state management in Flutter. Definitely try it out!

编辑 :开箱即用的共享状态管理支持不是很好,但是有很好的程序包可以处理共享状态。 经过一些有用的评论之后,我尝试了MobX的 Provider ,我必须说这将解决共享状态管理问题。 如果您已经熟悉MobX,那么使用Flutter中的共享状态管理就不难了。 绝对可以尝试一下!

I stumbled across a very peculiar problem, shared state handling. In Flutter you have StatefulWidgets, that hold state. Now how do you access state from another component? The answer was not so easy. I found some answers on StackOverflow and Medium. It looks like to me that there isn’t one, clear and preferred way to be able to access state from other StatefulWidgets. Not sure how this will hold with larger apps that have a lot of StatefulWidgets.

我偶然发现了一个非常特殊的问题,即共享状态处理。 在Flutter中,您拥有保存状态的StatefulWidgets。 现在,您如何从另一个组件访问状态? 答案不是那么容易。 我在StackOverflowMedium中找到了一些答案。 在我看来,没有一种,清晰且首选的方式可以从其他StatefulWidgets访问状态。 不确定具有大量StatefulWidgets的大型应用程序如何处理。

I implemented the answer where you don’t make your State private by renaming it and letting it start with a capital letter and not an underscore (if it starts with an underscore, it’s considered private within Flutter). Let’s say we have a parent component Parent and a child component Balance that is created by the parent component.

我实现了答案,即您不通过重命名并让其以大写字母开头而不是下划线来使您的国家不私有(如果以下划线开头,则在Flutter中被视为私有)。 假设我们有一个父组件Parent和一个由父组件创建的子组件Balance

Instead of creating the following StatefulWidget with a private State,

除了使用私有状态创建以下StatefulWidget之外,

balance.dart private state
balance.dart私人国家

we create one with a public State that can be obtained in the parent.

我们创建了一个可以在父级获得的公共国家。

balance.dart public state
balance.dart公共状态

This way when we create the StatefulWidget, we can create it with a key for which we hold a reference in the parent component. Balance(key: _balance) where _balance is obtained by final GlobalKey<BalanceState> _balance = GlobalKey();. This enables us to call a public method that lives in the child component by calling the currentState on that GlobalKey reference _balance.currentState.somePublicMethod().

这样,当我们创建StatefulWidget时,可以使用在组件中保存引用的键来创建它。 Balance(key: _balance)其中_balancefinal GlobalKey<BalanceState> _balance = GlobalKey(); 。 这使我们能够通过在GlobalKey引用_balance.currentState.somePublicMethod()上调用currentState来调用位于子组件中的公共方法。

There are probably better ways to resolve this, but for my application, this was a clean and least invasive way to get things done without refactoring code.

可能有更好的方法来解决此问题,但是对于我的应用程序来说,这是一种无需重构代码即可完成工作的干净且侵入性最小的方法。

✘Android版本 (✘ Android Release)

You obviously want to deploy your app after development right, this should be easy to do. And it is for iOS, but you will have to do some changes to get it right for Android. This was already done right with React Native, so I peeked at the build.gradle of the React Native project and stole some lines.

您显然想在开发权之后部署您的应用程序,这应该很容易做到。 它适用于iOS,但您必须做一些更改才能使其正确适用于Android。 这已经在React Native上完成了,所以我偷看了React Native项目的build.gradle并偷了一些代码。

I was having difficulties with the setup of signed Android apps and multiple building flavors. After some searching through GitHub issues and some debugging, I ended up adding the following things to the build.gradle in the app folder. Note that the following file only contains what I have added.

我在设置已签名的Android应用程序和多种构建风格方面遇到困难。 在搜索了GitHub问题并进行了调试之后,我最终在app文件夹中的build.gradle中添加了以下内容。 请注意,以下文件仅包含我添加的内容

added method to build.gradle
添加了build.gradle方法

You will also need to create a key.properties in the android folder that contains the following filled fields. This will enable Flutter to build signed Android apps that are ready to be uploaded to the Play Store.

您还需要在包含以下填充字段的android文件夹中创建key.properties 。 这将使Flutter能够构建已签名的Android应用程序,这些应用程序可以立即上传到Play商店。

storePassword=
keyPassword=
keyAlias=
storeFile=release.keystore

TL; DR (TL;DR)

That’s all! Flutter really makes it easy to start working on, and test your Android and iOS app. It is very clean with the everything is a widget philosophy and automated screenshots are very practical with the screenshots package. Biometric authentication is also very easy to implement. Testing also has never been easier, as well as getting your launcher icons right with the flutter_launcher_icons package. Shared state management is not so convenient as it should be and some Android build.gradle knowledge is needed to create production builds.

就这样! Flutter确实使您可以轻松地开始进行工作以及测试您的Android和iOS应用。 一切都是小部件原理,这非常干净,而截图包中的自动截图非常实用。 生物特征认证也非常容易实现。 通过flutter_launcher_icons软件包正确设置启动器图标, 测试也从未如此简单。 共享状态管理并不像它应该的那样方便,并且有些Android build.gradle 创建生产版本需要知识

I would really recommend Flutter if the app you are building is small or medium in size. Flutter makes a lot of things much easier and makes your apps more structured and readable. In my opinion, it’s not quite ready to tackle the behemoths of apps that require massive amounts of state, but Flutter is heavily worked on so it will surely get there someday.

如果您正在构建的应用程序是中小型应用程序,我真的会推荐Flutter。 Flutter使许多事情变得更加容易,并使您的应用程序更加结构化和可读性。 在我看来,它还没有准备好应对需要大量状态的应用程序的庞然大物,但是Flutter进行了大量工作,因此肯定有一天会实现。

翻译自: https://medium.com/axons/what-i-like-and-dont-like-about-flutter-41ab84d4b2f2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值