Flutter, который не смог провал кроссплатформенного решения

Впервые я погрузился в мир разработки мобильных приложений около десяти лет назад. Я только что купил свой первый iPhone (3GS), и мне было любопытно сделать что-нибудь для этого чудесного маленького устройства (статья Георга фон дер Ховена публикуется в переводе – прим. ред.).

Сначала я написал стандартное веб-приложение с использованием библиотеки jQTouch и завернул его в контейнер PhoneGap для iOS. После запуска мой проект очень быстро попал в список рекомендованных приложений Apple в медицинской категории. Кажется, мне удалось сделать что-то хорошее.

Спустя несколько лет я переписал его как нативное мобильное приложение на Swift, а потом решил расширять охват и портировать проект на Flutter . Сейчас я расскажу, что из этого получилось.

Flutter, который не смог провал кроссплатформенного решения

Flutter: Святой Грааль для кросс-платформенных приложений?

Когда я только принял решение наконец создать Android-версию проекта, встал логичный вопрос – как это сделать. Писать нативное приложение или использовать кросс-платформенные решения? Ionic c React’ом или Vue? Или, может быть, новый модный Flutter вокруг которого сейчас много хайпа?

Поразительно, но сначала все шло очень хорошо. Я ничего не знал о Dart или принципах проектирования пользовательского интерфейса Flutter (пришедших из iOS Swift и XCode Interface Designer), но почти за одну неделю смог запустить проект на Android и iOS.

Когда дело дошло до тестирования, я столкнулся с очень странным поведением. Во время загрузки главы электронной книги отображается спиннер. Как только данные загружены и визуализированы, я заменяю спиннер на WebView. Это очень просто, и в 99% случаев все было нормально. Но иногда все зависало на спиннере. Очевидно, это не то поведение, которое вы ожидаете от приложения для чрезвычайных ситуаций.

Около дня или двух мне потребовалось, чтобы найти проблему. Ошибка возникала в модуле Flutter HTTPServer, который крашился на iOS, если пользователь ненадолго переключался на другое приложение, а затем возвращался к книге. Я отправил подробный отчет о баге , но в ближайшее время исправлений не будет.

Так оставлять было нельзя, поэтому я начал искать альтернативные подходы. Я решил отказаться от запросов через HTTPServer, при первом запуске копировать содержимое книги в каталог документов и открывать HTML прямо в WebView используя протокол file://.

После нескольких тестов и рефакторинга это решение казалось вполне жизнеспособным. До тех пор, пока некоторые ссылки в книге не стали ломаться на физическом устройстве iOS. При этом на Android и в эмуляторе iOS все работало нормально. Оказывается, я нашел еще один баг – в этот раз в пакете Flutter WebView. Я отправил отчет, который быстро был подтвержден. Однако обе ошибки все еще не исправлены (по состоянию на февраль 2021 года).

Flutter, который не смог провал кроссплатформенного решения

Ад зависимостей

Модули HTTPServer и WebView – это две самые важные зависимости моего проекта. Без одного или другого мое приложение просто не будет работать. После некоторого опыта работы с iOS и CocoaPods я смотрю на каждую зависимость как на технический долг. Когда вы заимствуете чей-то код и полагаетесь на него, возможно, вам однажды придется заплатить за это.

Я был крайне удивлен, что эти модули получают так мало любви и внимания от разработчиков Flutter. Вероятно, нынешняя команда Google недостаточно велика, чтобы поддерживать и развивать этот проект (8200+ незакрытых ишьюс на Github), либо их приоритеты больше его не включают.

Учитывая эти соображения, давайте взглянем на зависимости моего приложения Flutter и нативной версии проекта на Swift.

Нативное iOS приложение (Swift)

  • AEXML – для анализа XHTML-файлов. Собственный iOS SDK не предоставляет DOM-парсер.
  • FontAwesome.swift – позволяет использовать иконки FontAwesome.

Версия на Flutter

  • cupertino_icons
  • HTTP
  • provider
  • shared_preferences
  • font_awesome_flutter
  • xml2json
  • path
  • path_provider
  • mime
  • flutter_web_browser
  • webview_flutter
  • url_launcher
  • geolocator
  • geocoding
  • map_launcher
  • wakelock
  • device_info
  • package_info
  • scrollable_positioned_list
  • in_app_review
  • share

Ничего себе! Для чего мне нужно так много пакетов?

Так как мой проект – это не просто электронная книга, я добавил в него некоторые дополнительные фичи и сервисы, например:

  • онлайн-поиск с использованием стороннего API;
  • отображение информации о текущем местоположении пользователя с использованием геолокации и обратного геокодирования для помощи аварийным службам;
  • быстрый набор экстренных номеров.

Для каждой фичи требуется взаимодействие с нативным SDK платформы: геолокацией, геокодированием или выполнением вызовов. Также требуется предотвращать уход в спящий режим, запрашивать у пользователя оценку, открывать карту, делиться контентом и делать другие вещи, которые делают все приличные приложения.

Таким образом, ваше приложение Flutter зависит от бесчисленных внешних библиотек, которые обеспечивают функциональность нативных приложений. И вы не знаете, кто и как их разрабатывает, какие у них планы, ограничения и мотивация. Каждая из них увеличивает технический долг вашего приложения.

Flutter, который не смог провал кроссплатформенного решения

Кроссплатформенность добавляет сложности

Если вы хотите свести технический долг к минимуму, придется написать 100% кода вашего приложения самостоятельно и использовать только нативные SDK для доступа к возможностям устройства. Конечно это практически невозможно. Поэтому, по крайней мере, тщательно выбирайте сторонние библиотеки, которые вы добавляете к вашему техническому долгу.

К счастью, моих навыков было достаточно, чтобы быстро найти эту ошибку, пофиксить ее и даже отправить патч владельцу репозитория. Но я смог это сделать только потому, что библиотека была написана на том же языке, что и мое приложение – на языке, который я хорошо знаю.

Если сторонняя библиотека написала на знакомом вам языке, вы вполне можете жить с этим долгом. Но каждое кросс-платформенное решение добавляет вашему приложению несколько уровней сложности и точек возможного сбоя.

В каждом пакете Flutter может встретиться баг в коде Dart, или в коде Swift/Objective-C, или в коде Java/Kotlin. А то и в нескольких частях сразу. Его может сломать любое обновление Flutter, Dart, iOS или Android – а вместе с ним сломается и ваше приложение на одной или всех платформах. Если вы не владеет всеми тремя языками и всеми поддерживаемыми платформами, то, вероятно, не захотите искать и устранять проблемы самостоятельно.

Даже если мне удастся найти баг в iOS-части плагина Flutter WebView, я не разбираюсь в Objective-C и не хочу тратить время на бесплодные попытки исправить его. По моему мнению это должно просто работать, если оно не находится в состоянии альфа- или бета-тестирования.

Flutter, который не смог провал кроссплатформенного решения

На кого вы хотите положиться?

Дополнительная сложность, которую привносит в проект Flutter, не была бы столь ощутимой и серьезной, если бы за ним стояла сильная организация, которая чувствует ответственность за обеспечение стабильной работы и взаимодействия Dart с нативными функциями SDK.

Все, что вам реально нужно от Flutter, – это написать свое приложение на Dart и развернуть его на iOS и Android. И вы верите, что кросс-платформенное решение позволит вам это сделать, сэкономив время и деньги по сравнению с альтернативными решениями.

Теоретически Flutter может это сделать, но лишь теоретически. Его способность выполнять заявленные обещания все еще оставляет желать лучшего, особенно на iOS. Может быть это связано с уклоном в сторону Android или создателям продукта просто не хватает опыта, но как кросс-платформенная среда разработки Flutter пока проваливается. Если кросс-платформенное решение не может надежно обеспечивать работу с часто используемыми функциями на всех поддерживаемых платформах, оно теряет свои преимущества и лояльность разработчиков.

Я не уверен, что Google сможет сделать Flutter таким же удобным, как нативная разработка. Если они действительно стремятся к этому, то придется вложить больше усилий и денег в проект.

Может быть мне просто не повезло, и моими основными зависимостями оказались единственные два неисправных пакета из экосистемы Flutter. Читая открытые ишьюс на GitHub, я почему-то в этом сомневаюсь. Глядя на накопленный моим приложением с Flutter технический долг , я желаю удачи своим коллегам-разработчикам, которые уже запрыгнули в этот поезд.

Мне сейчас интересно, сможет ли Ionic справиться с моими задачами лучше. Судя по всему, они сталкиваются с очень похожими проблемами, но пока я не собираюсь переписывать свое приложение еще раз.

Вы пропустили

AEGIS Algorithms Android Angular Apache Airflow Apache Druid Apache Flink Apache Spark API API Canvas AppSec Architecture Artificial Intelligence Astro Authentication Authorization AutoGPT AWS AWS Aurora AWS Boto3 AWS EC2 AWS Lambda Azure Babylon.js Backend bash Beautiful Soup Bento UI Big Data Binary Tree Browser API Bun Career Cassandra Charts ChatGPT Chrome Extension Clean Code CLI ClickHouse Coding Codux Combine Compose Computer Context Fusion Copilot Cosmo Route CProgramming cron Cryptography CSS CTF Cypress DALL-E Data Analysis Data science Database dbt dbt Cloud deno Design Design Patterns Detekt Development Distributed Systems Django Docker Docker Hub Drizzle DRY DuckDB Express FastAPI Flask Flutter For Beginners Front End Development Game Development GCN GCP Geospatial Git GitHub Actions GitHub Pages Gitlab GMS GoFr Golang Google Google Sheets Google Wire GPT-3 GPT3 Gradio Gradle Grafana Graphic Design GraphQL gRPC Guidance HMS Hotwire HTML Huawei HuggingFace IndexedDB InfoSec Interview iOS Jackknife Java JavaScript Jetpack Compose JSON Kafka Kotlin Kubernetes LangChain Laravel Linux LlaMA LLM localStorage Logging Machine Learning Magento Math Mermaid Micro Frontends Mobile Mobile App Development mondayDB MongoDB Mongoose MySQL Naming NestJS NET NetMock Networks NextJS NLP Node.js Nodejs NoSQL NPM OOP OpenAI OTP Pandas PDF PHP Playwright Plotly Polars PostgreSQL Prefect Productivity Programming Prometheus Puppeteer Pushover Python Pytorch Quarkus Rabbitmq RAG Ramda Raspberry Pi React React Native Reactor Redis REST API Revolut Riverpod RProgramming Ruby Ruby on Rails Rust Scalene SCDB ScyllaDB Selenium Servers Sklearn SLO SnowFlake Snowkase Software Architecture Software Development Solara Solid Spring Boot SQL SQLite Streamlit SudoLang Supabase Swift SwiftUI Tailwind CSS Taipy Terraform Testing Transformers TURN TypeScript Ubuntu UI Design Unix UX UX Design Vim Vite VSCode Vue Web Architecture Web Components Web Development Web Frameworks Web Scraping Web-разработка Webassembly Websocket Whisper Widgets WordPress YAML YouTube Zed Наука о данных Разное Тренды

Современный подход к разработке с использованием Next.js