Lessons Learned Migrating a Production App to Flutter
Lessons that I learned the hard way to help you be more effective in your next Flutter project
I have been building native Android applications for over two years now. The only downside I’ve faced, apart from the over-complicated way of doing simple things in Android, is that I can only reach out to Android users. I have to count on an iOS dev to release the same application for iOS users. That always felt like some limitation to my powers as a developer.
Enter Flutter, looking promising to boost my powers and to keep the building of simple things simple. I still had to depend on an iOS dev for some configuration stuff, but it’s a far smaller dependency now.
We had a native app running for both Android and iOS that’s not so complicated internally but requires more devs to maintain it. We had the next features requirements coming in.
I and my team chose Flutter for the next requirements. It made sense because of not-so-complex functionality but a complex UI. If successful, it could have been our first step towards migrating an existing app to Flutter as well.
We made it through successfully, but only after learning some things the hard way. It was a great learning experience and much more fun to build cross-platform apps.
Here I’m sharing our experiences and the lessons learned.
Bottom line: I still love Flutter!
1. Starting is gonna be hard
When you move to Flutter from native app development, it’s gonna be a big pain in the ass to write code.
You’ll find yourself struggling because you’ll be confused about where to put your business logic and your UI logic, especially when you love writing clean code.
A single file for UI and business logic may sound like an advantage because you don’t have to create separate layout and logic files, but I found it more a potential liability than an asset.
2. Writing cleaner code is a bigger challenge and more important than ever
With great power comes great responsibility.
It's not just about having to write your UI and business logic cleanly. The way you are communicating between your different widgets in Flutter is extremely important.
Flutter builds things following a widget tree, and if you’re not careful enough planning your data flow cleanly, then you’ll run into issues where your UI is not updating properly. You’ll get frustrated over time, and the next thing you know, you’ll be writing hacks just to make it work.
That’s just bad code in play.
Look what just happened. You write bad code first, and then you’re writing more bad code trying to fix things because of that bad code. Once it works, you’ll be afraid to make any changes. Then you’ll get occupied with your next deadlines. That bad code will be sitting there, haunting you. You’ll write more code around this bad code to make other things work.
Overtime your Flutter app will become an unmaintainable mess, and much faster than a native app.
3. Plan your approach on paper before actually coding
First, plan your widget tree structure.
Starting from your
main.dart file to your first screen and its widgets, consider:
- How those widgets will store the state
- Whether you’ll use
StatefulWidgetor the Provider package
- Whether any of the widgets are required to listen to a common data source, and if so, how you’re going to achieve that
- How you’re going to move to another screen, and how to share data with that screen, if required
- How to update a parent widget from a child widget
- How you’re going to structure your code: repositories, network, utils, models, views, view models, etc.
- How to keep your code testable, for instance, whether you can use dependency injection
Plan out the whole flow of your MVP on paper. If you hit a roadblock, do your research, and find packages and design patterns that can help.
Search for the best ways to implement your requirements in the cleanest possible way.
This might take a little extra time, but it’s an investment that’s worth it in the long run.
4. Don’t reinvent the wheel
You know how we say, “There’s an app for that”? In Flutter, it’s mostly true if you say, “There’s a package for that.”
There are a lot of packages out there, thanks to the amazing Flutter community. But you should do your research well when choosing which Flutter packages to use.
There are some Flutter packages that only work for Android and some that might currently be in development for Android.
If there is a specific functionality you want to achieve for both Android and iOS and you depend on a package, be sure you build a small POC checking the package’s functionality on both platforms.
You don’t want to find out when you’re going to production that the package you’re depending on doesn’t work for one of the platforms.
5. Don’t over-engineer your solution
Yes, Bloc is cool. But have you ever tried the Provider package?
For some complex-looking situations, you can easily get away with just implementing your feature using some basic building blocks, such as the Provider package.
Even so, at times when you’re trying to fix a bug in complicated logic, you might end up writing more complex code which still won’t fix your solution.
Just take a step back, get back to pen and paper, and plan it out. I’m sure you can always find an easier and more efficient way out.
6. Hot reload is cool, but…
When I first heard about Flutter and tried out hot reload, I was just so damned happy and amazed. When I got into building more complicated apps with managing states, I found myself doing a cold restart of the app more often than a hot reload.
The restart sometimes takes unexpectedly long, which gets a little disappointing after all the high expectations Flutter set with hot reload. It’s still faster than native app building. It’s just that certain expectations weren’t fulfilled.
Whether you’re learning Flutter and are going to jump into developing apps straight away or you’re still learning from multiple sources and aren’t confident about Flutter, here are a few topics that should be enough to set you off building beautiful, scalable apps.
- Building layouts
StatefulWidget—When to use each, and when you can you avoid using
ChangeNotifier/ Provider package — How to have a common data observer for multiple widgets
- Dependency injection with
- Writing tests in Flutter
If you feel I’ve missed any important topic, please let me know in the comments.
8. Great sources to learn from — for free
Flutter is still evolving, so you can’t stick to a year-old course to set you off with the latest best practices. It’s important you keep the following sources close and keep learning from time to time, to stay on top of the game.
I personally am really thankful for all these sources that help me learn Flutter, and for their authors. These guys are consistently putting in more content, so stay tuned.
9. Practice building layouts
The number one quality of Flutter you’d think of while pitching it for a solution would be your freedom to build amazing-looking, smooth, responsive UI.
Make sure, then, that it’s your strong skill. Go online to various sources like Dribble and practice making those layouts. Use Flutter to its maximum potential.
Be sure to share your work online to help others learn more from you. Knowledge only grows when it's shared.
10. Interoperability is here, but…
If you have your old native application running both on Android and iOS, you can consider building your next feature in Flutter — but that comes at the cost of bloated app size.
If that’s not a big issue, though, maybe you can consider migrating your native apps to Flutter one screen at a time, and then finally complete the migration and get back to a much smaller app size.
Be sure you check how to achieve interoperability on both platforms before jumping into your project and implementing it. Maybe also learn how you’ll send data from your native app to the Flutter screen.
Thanks for reading!