推荐理由：相信大家已经在各种地方看过对不起有钱真的可以为所欲为的 gif 了，这个项目就是可以让大家自己填词的 gif 生成器。不仅可以为所欲为，元首和工作细胞的 gif 也同样支持，如果想要增加自己表情收藏的话完全可以试一试这个。
今日推荐英文原文：《Flutter: the good, the bad and the ugly》作者：Marco Bellinaso
推荐理由：在移动端 app 开发中作为新生势力的 Flutter 的优缺点
Flutter: the good, the bad and the ugly
This time last year I published an article with my impressions of React Native, which was all the rage in 2016 and 2017, with companies such as Facebook, Instagram, Tesla, Walmart, Airbnb, Skype and more investigating it, or even actually implementing apps (or subsections) with it.
But this year, React Native seems to be losing (or have already lost) much of its appeal, with some notable companies have announced they are abandoning it (check out this article by Airbnb’s Gabriel Peal, or this other one by Udacity’s Nate Ebel). Talking to developers and judging from what’s being published in articles, or from what recruiters look for on LinkedIn, it would appear that it’s just not that hot anymore. To be clear though, it’s not dead and the fact that it didn’t work so well for some companies doesn’t mean that it won’t work for others. Many of these companies used it on subsections of their apps rather than on all of it, which is one of the reasons it was so complex to use. However, one can’t deny that if big companies that contribute to the popularity of a technology (by writing about it and creating popular open-source libraries for it) eventually decide to leave it, it has an impact on the community. At the very minimum, teams that were previously on the fence about using it had an easier decision to make, using posts such as the above to back up their arguments.
But in 2017 something else happened. Google publicly released an early alpha version of its own cross-platform technology for mobile apps, Flutter, during the Google IO developers conference. Fast forward to this year’s conference and the brand announced that Flutter was ready for production and at the end of September it had a Release Preview 2. Read the full announcement on its blog — here are just some highlights:
- The theme for this last release is pixel-perfect iOS apps (while the focus previously had mostly been on Android’s Material Design).
- Note the app’s reduced package size, with a minimal Android app now just 4.7MB when built in release mode.
- Flutter, which has been open-source since the beginning, entered the list of the top 50 most active repos on GitHub.
- Some big companies are using it, such as as Alibaba (Android, iOS), Tencent (Android, iOS), and Google Ads (Android, iOS). There’s also a video on how Alibaba used Flutter to build its Xianyu app (Android, iOS), currently used by more than 50 million customers in China.
- This chart shows how Flutter is getting a lot of momentum on Stack Overflow:
So, this intrigued me enough to check it out, understand what it is all about, and create a real application with it (because, as always, just reading the documentation is not enough to get a real feeling of something new). It definitely helped that the last Friday of the month at ASOS is Tech Develops, where the entire tech department — almost a thousand people — are free to investigate any new tech we’re interested in.
You see some screens (for both Android and iOS) at the top of this page, and can download the Android version from the Play Store — feedback is appreciated!
What is Flutter and what does it do?
- It’s an open-source software development kit (SDK), developed by Google, to quickly build iOS and Android apps, sharing most of the code. It works in conjunction with the Android and iOS SDKs, which also means you still need a macOS machine to build for iOS (just like you do for React Native and Xamarin). The installation for Android was very smooth for me — I just followed the instructions on the website and used the ‘flutter doctor’ command, but I initially had some issues with the iOS setup. This article by Laxman Sahni helped set it up correctly.
- It uses the Dart programming language, also developed by Google. Yes, another language to learn but don’t worry, it’s super easy if you’re familiar with Java, JS, Kotlin, Swift or C#.
- The application is compiled ahead-of-time into native ARM code, not at runtime as in React Native. This gives better performance because there’s no JS bridge in the middle to parse and execute the code. However it also means there’s no over-the-air update option by downloading a new bundle of JS code at runtime.
- Rather than being a wrapper on top of the iOS/Android-specific native UI components (which is what React Native and Xamarin do), it really draws the UI from scratch on a ‘screen canvas’, through a fast C++ 2D graphics library called Skia (which also serves as the graphics engine for Google Chrome, Chrome OS, Android, Mozilla Firefox and Firefox OS, and many other products). The Skia project started back in 1996 and was acquired by Google in 2005, although it’s released under the BSD license and anyone can use it. This has huge consequences — I talk more about this in the pros and cons sections below.
- Similarly to React Native, Flutter is also based on a ‘unidirectional data flow’ architecture, or reactive programming, briefly but clearly explained here by Elizabeth Denhup. In even fewer words, the app reacts to user input by changing variables/properties (or more generically, the ‘state’ of the screen or view), and the UI is re-rendered according to the new state. Functions don’t change the UI (the colour of button, the text of a label, the content of a list, etc.), directly.
- Again, similarly to React Native, there is hot reloading (thank God!). You just change something in the code editor, save, and the UI refreshes on the Android emu or iOS sim. It’s so convenient and fast that it’s hard to go back once you try it and it makes up for the fact that the UI is created programmatically and therefore there’s no visual editor for it.
- Flutter is extensible with third-party plugins that add new custom-drawn UI components or wrap platform-specific features not already covered by the built-in classes (eg: for video/audio, monetisation, storage, camera, augmented reality, machine learning etc.). Here’s the best collection of plugins I’ve found. There are many, but not as many as you might need.
- Linked to the previous point, Flutter makes it relatively easy to write platform-specific code by either executing different code after checking Platform.isIOS and Platform.isAndroid (if there’s a difference in the UI widgets you want to instantiate, or logic in your .dart file), or by writing your own native plugins (if you really need to wrap platform-specific functionalities not provided by Flutter already).
- Also linked to point 7, performance should not be a problem for typical apps (at least in release mode — debug mode is significantly slower because it uses a virtual machine to run Dart code), as the UI is written by a fast low-level C++ lib, and other functionalities map to their native counterparts. However, you must do it right, by minimising the number of redraws and by redrawing only parts that actually depend on the changed state. Refer to this article by Andrea Bizzotto and this other one by Simon Lightfoot to find out more.
- You can use any text editor and the flutter command to write and build apps, but the recommended approach is to use one of the editors that support the Flutter plugin, namely Android Studio (my choice), VS Code or Intelli J. This gives you intellisense, autocompletion, some debugging tools and spares that you need to use the command-line to compile/run the apps.
To get a sense of the performance and the look and feel of a Flutter app in comparison to a Native app, just download the Flutter Gallery app from the Play Store (and maybe look at its source code on GitHub). Also, refer to the Widget Catalog page in the official docs.
- The fact that Flutter does its own UI drawing rather than being a wrapper around the platform-specific native components has both pros and cons. The pro is that if something is rendered in some way on your test iPhone with iOS 12, for example, it should be rendered in exactly the same way, not only on any other iOS version but also on any Android phone. With React Native or Xamarin, the UI components have a number of properties that are only supported in one platform or the other, or maybe they are supported but translated in slightly different ways to their native counterparts behind the scenes. This means that you either need to test on a lot of devices and OS versions (and potentially write platform specific code to fix some situations), or just know that it might look broken (or at least different) for some users. Your app might even crash if you use an attribute or a feature that’s not supported on a specific OS version. With Flutter you will be much safer (at least for the UI part of the app). You should still check the app on multiple devices, especially if you use third party plugins that do map to underlying platform-specific native components. This will be the case if you use things like audio/video, push notifications, in-app billing etc.). The negative side of this approach is covered in the next section of the article.
- Hot reloading is just too useful, it’s a developer’s dream come true: ⌘+S in the editor, and the app reloads in a sec on the sim! Goodbye to the endless build / wait / run / wait / test / start-over endless process. In reality you still need to rebuild when you change assets and plugins, change something in the navigation, state initialisation or logic, but most UI changes are applied immediately while the app is running. For apps that are UI-heavy, this is where you’d dedicate most of your time.
- I like the overall principle of small reusable components that react to a change in the ‘state’, which is also one of React’s and React Native’s core ideas. Reactive apps can also be developed in pure iOS and Android development of course, but it’s easier and more natural with Flutter (and RN). This is because it’s at the core of the technology, rather than something that’s provided by third-party libs and implemented in dozens of different ways.
- Flutter and Dart have built-in support for both unit testing for logic, and widget testing for UI/interactions. For example, you can send tap and scroll gestures, find child widgets in the widget tree, read text, and verify that the values of widget properties are correct. The official docs does a good job at clearly presenting what’s available. This article by Devon Carew shows how the Flutter plugin makes it all well integrated into your code editor.
- I love the built-in support for theming every aspect of the app’s UI. The most difficult part in creating the light and dark themes of my app was actually picking the right colour (I created just two, but could have created 10 with the same approach). In terms of code, it’s just a few lines (basically set the theme property of the root MaterialApp object — see this for a full example).
Bonus: This is an advantage of any cross-platform technology in reality, not just Flutter, but I’m still going to mention it: creating an app for both platforms at the same time makes it much easier to keep them aligned at all times. With the traditional development process, you might launch both platforms at the same time and with feature parity but then after a short while you realise that one platform is performing better than the other (in terms of downloads, sales, ad revenues, …). Then you start cutting costs on the other, which means that one is partially left behind.
- As mentioned a couple of times already, Flutter paints the UI on its own custom way, it doesn’t create native components. It does a very good job at replicating Android’s Material Design and also iOS-specific components with its Cupertino library but it’s still not native under the hood. This has a few implications, such as:
(A) If iOS 13 changed the way a segmented control or a UISwitch is rendered, your Flutter app that uses CupertinoSegmentedControl or CupertinoSwitch would keep the old look until Flutter is updated and you rebuild it. One can argue that many users wouldn’t care (most of my non-techy friends wouldn’t care and wouldn’t even notice, for example, they’d just care about the app looking pretty enough rather than whether it’s 100 per cent consistent with the OS’s pure look and feel), but it might be a deal breaker if you’re a purist.
(B) If you plan to use Flutter for just a section of an existing app (which is covered here in Flutter’s wiki, here in an article by Tomek Polański, and here in an article by Jan Reehuis), you might see a difference between the native part and Flutter part. Again, this might bother you (and your users) or not. Much less of a problem for new apps that are 100 per cent Flutter of course.
(C) To make things as easy as possible for you as a dev, and assuming that your users don’t care about the native look of the app, you could just use MaterialApp (which uses Material Design components) and compile it for both Android and iOS. It will work fine, despite the non-native appearance, and it is in fact what I did for my app. If, instead, you do care about this, and decide to use MaterialApp for Android and a CupertinoAppfor iOS, you’ll be duplicating most if not all the code for your UI (which can be a considerable part of your app), and you’ll make the architecture more complex. Consider this carefully and decide if it’d be worthwhile.
- Here is a decent list of great looking UI components and other pluginson GitHub, but it’s nowhere as rich as the plugins you can find for React Native and even Xamarin. It’s probably just because Flutter is much newer and with a smaller community but that’s how things are at the moment. Choices are limited, and many plugins are old, not maintained, and maybe don’t even work anymore with the current Dart/Flutter versions. Some components (especially non-UI ones, that map platform-specific features) are only available for either iOS or Android, but not both (typically they support Android, because Android devs are more into Flutter than iOS devs at the moment, since Flutter comes from Google). It’s true however that filling in the gaps and writing the platform-specific code just for the missing platform is still better than starting from scratch and things will improve for sure if Flutter keeps getting more and more popularity.
- Debugging is not at its best. You can use the print/debugPrintstatements, look at logs, and use tools to profile the CPU/memory or visualise the view hierarchy, but we’re on a different planet in comparison to what you do in Xcode or Android Studio working with the native SDKs. (More about your options in the official docs here.)
- The error screen or logs that you get when there’s a layout error (or something else at a lower level) can be very confusing and obscure, as it points to some line of code of the framework that is maybe many levels of abstractions below what you directly interact with. On native iOS and Android, errors are usually clearer to understand, and, if not, you can typically just copy and paste the full error on Google and be reasonably confident that you’ll get a useful list of links that tell you more. With Flutter, since the community is still relatively small, not so much.
- Creating the UI programmatically (in the same .dart files where your code for the screen is) is easy and direct. It also means there’s not much separation. I would prefer to create the UI with markup code (similar to what you do in native Android apps) in separate files.
- On Android, the vast majority of developers use Clean Architecture and MVP (model-view-presenter). On iOS, it can be MVC, MVVM (model-view-viewmodel) or Viper. In both cases (but even more for Android) there are clear and well known architectural patterns that have proven to work well for large apps. For Flutter (and React Native as well), it feels like it’s all still being defined, there is no ‘standard’ or ‘almost-universally-accepted’ architectural approach. All articles show simple samples, which is normal because they still need to take people onboard before talking about more advanced aspects. However, if you plan to use Flutter for a rather large project, it would be preferable to have a clear idea of how to structure it, so that it’s scalable and easily maintainable as the app grows in size and complexity. I highly recommend watching this video from Brian Egan to start looking deeper into this (it covers layering your code, Redux and testing) and checking out his samples on GitHub. I also recommended this article about Streams and RxDart by Didier Boelensand these other posts about Redux and overall architectural review. Again, I’m definitely not saying Flutter doesn’t allow you to build apps with a clean and maintainable architecture, but just that there’ll be some trial /error / experimentation / study involved, as it’s not something as mature and widely used as what we’re accustomed to in native iOS/Android apps.
I could almost copy and paste what I wrote last year about React Native, but instead, here’s the link to it again. To summarise: there’s lots of potential, it’s very easy to get started and actually create something real, and there are many good principles and ideas. However the community is still small and bits and pieces are missing in terms of cross-platform plugins, or there is not much choice in the best case. Also, you must be OK with the fact that you won’t have a 100 per cent native-looking UI, and that if you want to at least be as close as possible for both iOS and Android, your code and structure will get more complex.
Personally, I think this is a very useful technology (that you can already use) for those situations where:
- You must be as quick as possible to reach the widest user base possible — say a startup that is starting from scratch and wants to release for both platforms. See if there’s traction in one or both, and then invest more, polish things etc. You could see this as a very advanced prototype, which can be further polished later still staying on Flutter or replaced by a native version and a dedicated team.
- The UI is not the biggest concern anyway — for example Enterprise/B2B apps, where you want to have a line-of-business app that employees/customers can use with any type of device, but aren’t too concerned about it being consistent with everything else in the OS ecosystem. This is what Groupon did for its app for merchants (but not end users), for example.
What I can say in closing is that creating my first simple app has been very enjoyable for the most part, and even though I have developed quite a few iOS and Android native apps in the past, I’m sure it took me less time to create this one in Flutter (albeit I started with no knowledge of it) than what it would have taken me to create two separate native apps. Not bad, I’d say!