Flutter: A practical guide to cross-platform development

Flutter: A practical guide to cross-platform development

Symphony Logo
Symphony
September 12th, 2024

In the ever-evolving world of app development, the quest for efficiency, performance, and simplicity is constant. Enter Flutter, an open-source framework created by Google, designed to transform how developers approach building applications across various platforms. In this blog post, we'll dive into what makes Flutter unique, explore its core components, supported platforms, and how it compares to other development methodologies.

What is Flutter?

Flutter is an open-source framework by Google for building beautiful, natively compiled, multi-platform applications from a single codebase. It breaks through traditional barriers by offering a comprehensive UI framework coupled with a set of tools designed to streamline the development process. At its core, Flutter is made up of two key parts:

  • UI framework

Flutter provides a rich collection of packages that facilitate the creation of cross-platform apps with seamless user experiences. This framework is extensive, allowing developers to craft visually consistent and responsive apps across multiple platforms.

  • Collection of tools

From command-line interfaces to various development aids, Flutter equips developers with a robust toolkit to enhance productivity. These tools simplify the process of building and deploying apps across mobile, web, and desktop platforms.

Dart: The backbone of Flutter

While Flutter itself is not a programming language, it leverages Dart, a language developed by Google. Dart plays a pivotal role in building user interfaces within the Flutter framework, offering a blend of efficiency and flexibility.

Why Dart?

Dart is a versatile programming language ideally suited for developing sophisticated applications within the Flutter ecosystem. It’s straightforward to learn, especially for those familiar with languages like JavaScript or Java, and it integrates smoothly with Flutter, resulting in faster compilation and smoother performance.

Platforms supported by Flutter

Flutter’s versatility extends across multiple platforms, enabling developers to target mobile, web, and desktop environments with ease.

Mobile (Android and iOS)

Flutter’s primary focus is on mobile app development, allowing you to deploy apps on both Android and iOS platforms by utilizing its native compilation capabilities.

Web

Flutter also supports the development of web applications, allowing you to create responsive web apps that work seamlessly across modern browsers.

Desktop (Windows, macOS, Linux)

Flutter extends its reach to desktop platforms, enabling developers to create applications for Windows, macOS, and Linux from the same codebase.

Exploring available options

In the realm of cross-platform development, Flutter stands as a compelling alternative to traditional approaches. Here’s a comparison of Flutter with other development methods:

Comparison of Flutter with other development methods:

Comparison of Flutter with other development methods:

Native apps

While Kotlin and Swift offer native integration, Flutter streamlines development by eliminating the need for platform-specific languages, fostering code reusability and efficiency.

Hybrid apps

Cordova and Ionic facilitate the creation of web apps encapsulated within native WebView wrappers. However, Flutter transcends the limitations of hybrid solutions, delivering superior performance and user experiences.

Compiled apps

React Native and Flutter represent the pinnacle of cross-platform development, offering compiled solutions that harness native performance. Flutter, with its Dart-based architecture, emerges as a frontrunner, boasting pre-built components and seamless integration with Material Design principles.

A closer look into compiled apps

When dissecting compiled solutions, Flutter's approach stands out for its robustness and efficiency.

React Native vs. Flutter

React Native, while leveraging JavaScript, does not compile to native code, which can affect performance and efficiency. Additionally, its reliance on pre-built components can introduce complexities in customization.

Flutter, on the other hand, compiles applications to ARM C/C++ libraries, ensuring optimal performance and native integration. Moreover, Flutter’s extensive library of pre-built components and its strong commitment to Material Design principles make it an appealing choice for developers looking to create highly performant apps.

Some potential pitfalls of using Flutter

While Flutter offers many advantages, there are some potential pitfalls to consider:

  • Larger file sizes: Flutter apps can sometimes result in larger file sizes compared to native applications, which may affect download times and device storage.

  • Learning curve: Developers who are new to Dart or reactive programming might face a learning curve when adopting Flutter, potentially slowing down the development process.

  • Ecosystem maturity: Despite its rapid growth, Flutter’s ecosystem and community support might not be as extensive as those of more established frameworks, which can result in fewer resources and solutions for specific issues.

  • Tool integration: While Flutter has strong support for popular IDEs like Visual Studio Code and Android Studio, integration with other tools or IDEs might be less seamless, impacting development workflows for some developers.

Understanding these challenges can help developers make informed decisions when choosing Flutter for their projects.

Real-world example: Nikola’s first Flutter app

Let me take you through a real-world example of how I applied Flutter in developing my first app - a music player.

The Idea

For my first Flutter app, I decided to create a music player that could play audio files stored on the device without requiring an internet connection. While this approach might seem old-fashioned, it gave me a great opportunity to learn and explore various aspects of app development. My main goals included figuring out how to access audio files on the device, display track information like the title, album cover, and duration, and, ultimately, play the audio files directly from the app.

The Challenge

To access data from the device, I needed to request user permissions. While developing on the emulator, everything seemed to work fine. However, when I tested the app on a real device, it crashed without any on-screen notification. The only clue was a vague error message in the terminal:

W/le.music_playe( 7353): Accessing hidden method Landroid/media/AudioTrack;->getLatency()I (unsupported, reflection, allowed)

E/flutter ( 7353): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: RangeError (index): Invalid value: Valid value range is empty: 0

Eventually, I realized that my physical device was running Android 12, while the emulator was running Android 14. This version mismatch led me to investigate further and understand the differences in Android permissions.

The Problem

As I dug deeper, I discovered a significant change in how storage permissions are handled, starting with Android 13. In older Android versions, apps requested broad storage permissions, granting access to all media files on the device, even if only specific types, like audio files, were needed.

This changed starting with Android 13. Apps now need to request more granular permissions, such as access to audio files. Once granted, the app can only access the designated type of media, enhancing security and privacy by limiting access to only what is necessary.

The Solution

Once I understood the process, the fix became straightforward. First, the app needed to determine the device's Android version. If the Android version was below 13, I had to request storage permissions; otherwise, I needed to request audio permissions.

If the app is opened for the first time, the user is prompted to enable the appropriate permissions. On subsequent launches, the app simply checks if these permissions are still granted. If they are, then the next time the user opens the app, the audio files are displayed, and music playback is available.

Conclusion

Building this music player app in Flutter was a valuable learning experience for me, particularly when navigating the challenges of handling device storage permissions across different Android versions. Understanding how Android’s permission system has evolved from broad access to more targeted, secure controls was crucial in solving the app’s crashes and improving its functionality. Ultimately, the process not only enhanced my understanding of Android development but also reinforced the importance of testing on real devices and staying up-to-date with platform-specific changes. Now, the app provides a smoother and more secure user experience, effectively managing permissions and delivering the core functionality of a reliable offline music player.

Final thoughts

Using Flutter for app development offers several advantages, including the ability to target multiple platforms with a single codebase and the ease of use provided by its comprehensive UI framework and toolkit. However, as Nikola’s experience shows, it’s crucial to be aware of platform-specific requirements and thoroughly test your apps on real devices.

Flutter’s growing popularity makes it an attractive option for developers looking to build high-quality, cross-platform applications. Whether you’re just starting or have years of experience, Flutter is worth considering for your next project.

About the author

Nikola Kuzmanovski is a Software Engineer from the Skopje branch with over six years of experience, primarily focused on React and with a solid understanding of Angular. He has worked across industries like Telecommunications, Education, and Retail, recently expanding his skills into mobile development with Flutter. Nikola is known for his expertise in AI integration, rapid prototyping, and delivering top-tier UI/UX solutions.

Contact us if you have any questions about our company or products.

We will try to provide an answer within a few days.