Сьогодні важко знайти користувача, який ніколи б не стикався з електронними книгами у форматі EPUB: він відкритий, гнучкий та сумісний практично з будь-якими пристроями. Втім реалізація відтворення EPUB в кросплатформному мобільному додатку може виявитись несподівано складним завданням.
Саме з таким завданням ми зіткнулись у нашому нещодавньому проєкті для провідного українського видавництва КСД. Деталі кейса ви скоро зможете роздивитись у портфоліо WEZOM: клієнт звернувся до нас із запитом на розробку мобільного додатку з кастомним EPUB-рідером. Потрібно було не просто "зробити читалку", а забезпечити у продукті стабільність, швидкодію та комфорт для читача, поєднавши усе це із надійним захистом авторського контенту в екосистемі КСД.
Сьогодні ми розкажемо, як команді вдалося реалізувати кастомний EPUB рідер на Flutter без написання рушія з нуля, завдяки застосуванню гібридних технологій. Це дозволило суттєво пришвидшити розробку та заощадити величезні ресурси, надавши клієнтам та користувачам гідний продукт.
Виклики при створенні EPUB рідера на Flutter
Видавництво потребувало якісного мобільного додатку для Android та iOS, що працював би і як онлайн-магазин, і як зручний рідер для куплених там електронних книг. Ключовими вимогами до цього рішення був високоякісний UX, продумана естетика та надійний захист авторського контенту від недобросовісного копіювання.
Кросплатформна розробка EPUB‑рідера в додатку була оптимальним варіантом, тож технічна команда зробила очевидний вибір на користь фреймворка Flutter. Він має власний графічний рушій і дозволяє створювати складні додатки на Android/iOS з єдиною кодовою базою, що практично ні в чому не поступаються нативним. Але гібридний Flutter-додаток з EPUB-рідером поставив перед розробниками нетривіальні завдання.
В чому крилась проблема?
Річ у тім, що EPUB, попри свою відкритість, залишається доволі складним для роботи форматом. Якби йшлося лише про текст, проблем би не було. Але EPUB – це фактично цілий веб-сайт у ZIP-архіві. Він містить HTML-файли (розділи книги) та CSS-стилі, що визначають вигляд книги в цифрі. До архіву також входять шрифти, зображення тощо.
Flutter як такий не має інструментів для роботи з веб-контентом: він не вміє рендерити HTML та CSS. Його головна задача – відмальовувати на екрані власні віджети (кнопки, текст, контейнери).
Наявні пакети Flutter можуть перетворювати HTML-теги на віджети фреймворка. Для простої статті в блозі цього могло б вистачити, але в нашому випадку йшлося про цілі книги зі складною версткою, стилями та інтерактивністю (як в EPUB 3). Потрібно було рішення, що підтримує складний CSS і може виконувати JavaScript.
Відтак аби реалізувати задум проєкту в повному обсязі, команді фактично треба було б написати власний браузерний рушій, який вміє:
-
Парсити структуру EPUB, тобто правильно "читати" службові файли, щоб зрозуміти порядок розділів, зміст та метадані;
-
Рендерити HTML/CSS, аби коректно відобразити кожну сторінку-розділ з усіма стилями та можливостями налаштування;
-
Проводити пагінацію – тобто динамічно розбивати “плавучий” HTML-текст на сторінки, з урахуванням розміру екрану та обраного користувачем шрифта. Це дуже нетривіальний алгоритм, робота над ним була б найскладнішим аспектом розробки.
Створення такого рушія з нуля – це цілий проєкт у проєкті, колосальна робота. І проводити цю роботу в рамках запиту на створення додатка було б невиправдано дорого та довго. Тож ми почали шукати альтернативи.
Рішення: гібридний підхід з WebView
Якщо розробити власний веб-рушій у межах проєкту неможливо, потрібно було знайте десь готове рішення і пристосувати його під наші завдання. І такий інструмент є – це вбудовуваний компонент WebView. По суті це браузер, який можна вбудувати в додаток.
Повноцінна інтеграція WebView у Flutter дала проєкту важливі переваги:
-
Відпала потреба у власному рушії для рендерингу – розробники взяли готовий, потужний та оптимізований рушій, який вже є в кожному смартфоні.
-
Повна підтримка веб-технологій. Адже WebView "з коробки" розуміє HTML, CSS, JavaScript, що ідеально підходить для формату EPUB 3.
-
Безліч готових рішень. Команда отримала змогу використовувати будь-які наявні JS-бібліотеки, створені спеціально для відображення книг. Саме вони взяли на себе всю складну логіку пагінації всередині WebView.
Подібні рішення існували і раніше. Наприклад, демонстраційний опенсорсний проєкт BookReader App використовує зв’язку epub.js, Flutter та WebView для відтворення EPUB у форматі мобільного додатку. За подібною логікою працює і Kotobee Reader – популярний “читач” електронних книг EPUB на Android та iOS. Наше завдання полягало в тому, аби реалізувати цю логіку кастомно – на високому рівні, з урахуванням усіх потреб бізнесу клієнта.
Скорочення часу розробки через WebView дозволило команді вивільнити величезні ресурси та сконцентруватись на інших аспектах розробки, мінімізуючи витрати проєкту та наближаючи реліз продукту.
Застосування WebView для відображення EPUB: технічні тонкощі
Flutter має базову підтримку WebView через плагіни (webview_flutter для Android і iOS). Але для складніших сценаріїв, таких як передача команд між Flutter і JavaScript чи робота з нативним парсингом, необхідна гібридна архітектура із використанням Method Channels. Аби зробити WebView “слухняним”, важливо чітко визначити точки входу/виходу – лише так Flutter, нативний код і JS можуть працювати як одне ціле.
Аби реалізувати у WebView повноцінний парсинг та рендеринг EPUB-файлів, необхідно застосувати одну з відповідних JS-бібліотек. Коротко погляньмо на найпопулярніші з них:
- Epub.js
Це, мабуть, найпопулярніша JavaScript бібліотека EPUB, створена для браузерів. Вона автоматично обробляє розбивку на сторінки та навігацію, дозволяє налаштовувати стилі й теми для комфортного читання.
- Readium
Readium JS / Readium Web – це комплексний набір інструментів, створений консорціумом Readium Foundation. Він фокусується на повній відповідності стандартам EPUB, включаючи розширену підтримку EPUB 3 та функції доступності для інклюзивного читання.
- Vivliostyle
Це потужна система верстки, яка розширює можливості браузера для друкованих та цифрових публікацій. Вона дозволяє створювати складні макети сторінок, включаючи виноски, колонтитули та посторінкові елементи, що робить її гарним вибором для відображення EPUB/веб-публікацій.
Першою думкою команди розробників було звернення до найпопулярнішої бібліотеки. Втім JS бібліотека Epub.js не підійшла, оскільки призначена для браузерів. Вона виконує весь парсинг EPUB-файлу у JavaScript і не має нативної підтримки під Kotlin/Swift. Це робило Epub.js непридатною для використання в мобайлі з високими вимогами до продуктивності та стабільності.
Як ми реалізували парсинг EPUB?
Отже, звичайний парсинг EPUB в JavaScript – це неефективне рішення. Така обробка була б занадто повільною, особливо для великих книг. То що ж робити? Ми знайшли більш дієвий підхід: реалізувати парсинг на нативній стороні за допомогою спеціалізованих бібліотек Readium. Екосистеми Readium Foundation пропонує для цього незамінні інструменти:
-
readium/kotlin-toolkit для Android.
-
readium/swift-toolkit для iOS.
Ці бібліотеки роблять всю важку роботу: розпаковують архів, аналізують структуру, готують контент. Після цього Readium запускає локальний веб-сервер безпосередньо в додатку. Цей сервер "роздає" файли книги (HTML, CSS, картинки) для відтворення у WebView.
Що в підсумку? Парсинг EPUB на Flutter насправді здійснюється через нативний код на Kotlin/Swift. А JS-код у WebView відповідає лише за відображення (рендеринг) вже опрацьованого контенту.
Як влаштований гібридний Flutter-додаток EPUB
Аби створити EPUB-рідер/мобільний додаток, що поєднує зручність Flutter, потужність нативного коду та гнучкість веб-рішень, ми реалізували трирівневу гібридну архітектуру. Це дозволило нам досягти високої продуктивності, стабільності та гнучкої взаємодії між усіма компонентами продукту.
В загальних рисах наша архітектура виглядає так:
1. Flutter (рівень UI)
Усе, що бачить користувач, з чим він взаємодіє – це віджети Flutter: кнопки, панелі, налаштування, інтерактивні елементи тощо. Сам рідер (модуль для читання) вбудовується в додаток як окремий компонент у Flutter UI.
2. Native (логіка + парсинг EPUB)
На рівні Android (Kotlin) та iOS (Swift) в додатку використовується Readium SDK, що відповідає за парсинг. Це “чорна скринька”, де відбувається уся “магія” навколо EPUB:
-
Розпаковування файлу;
-
Парсинг його структури;
-
Запуск локального веб-сервера, який “роздає” контент у WebView
3. JavaScript у WebView (рендеринг)
WebView підключається до локального сервера й завантажує EPUB-рідер на базі Readium Web. Саме тут відбувається:
-
Відображення тексту
-
Пагінація
-
Функціонал UI: пошук, виділення, анотації, зміна шрифтів, тем
-
Обробка жестів (гортання) тощо.
Як Flutter взаємодіє з JavaScript
Прямої взаємодії тут немає. Для налаштування зв’язку між Flutter і WebView ми використовували Method Channel – офіційний механізм Flutter для обміну даними з нативним кодом. Найпростіше буде пояснити цю логіку за допомогою практичних прикладів взаємодії читача з додатком:
- Зміна розміру шрифту:
-
Користувач тисне на кнопку + у Flutter-інтерфейсі.
-
Flutter відправляє команду на нативну сторону: {'action': 'setFontSize', 'value': '18px'}.
-
Нативний код (Kotlin/Swift) отримує команду і виконує JS-код у WebView: webView.evaluateJavascript("reader.setFontSize('18px')").
-
Код JS змінює CSS-стилі книги.
-
- Гортання сторінок
-
Код JS на стороні WebView фіксує гортання і визначає нову позицію в книзі.
-
JS викликає спеціальну функцію, яка передає цю позицію нативному коду.
-
Нативний код отримує позицію і відправляє її у Flutter.
-
Flutter отримує дані і оновлює свій віджет, наприклад, Сторінка 5 з 120.
-
Така взаємодія “Flutter-WebView” у EPUB-рідері може здаватися не дуже елегантною, але насправді вона працює в рази швидше, ніж обробка усіх інтеракцій безпосередньо на стороні WebView.
Оптимізація EPUB рідера для Android та iOS
Аби забезпечити комфортний досвід в додатку на всіх пристроях, ми провели низку технічних оптимізацій, орієнтованих на продуктивність, адаптивність і стабільність гібридної розробки.
- Адаптація інтерфейсу під різні платформи
Логіка віджетів Flutter дала команді змогу легко адаптувати візуал та функціональність продукту під Android та iOS, дотримуючись нативних гайдлайнів. Панелі управління, кнопки та жести гортання поводяться однаково передбачувано на обох платформах. Понад те, ми врахували специфіку системних WebView: Safari на iOS та Chrome на Android.
Таблиця: Гайдлайни дизайну мобільних додатків для Android та IOS |
Характеристика | Android (Material Design) | iOS (Human Interface Guidelines) |
Дизайн-філософія | Яскраві кольори, глибина, тіні, чітка ієрархія елементів | Простота, мінімалізм, акцент на контент і плавність |
Платформні відмінності UI | Floating Action, насичені кольори, акценти | Простір, чистота, легкість, мінімалізм |
Компоненти інтерфейсу | Матеріальні кнопки, чекбокси, розділені поля вводу | Тумблери, списки налаштувань, інтегровані текстові поля |
- Прискорення завантаження сторінок EPUB
Висока продуктивність EPUB Reader на Flutter була однією з головних вимог проєкту. Ключем до швидкодії став поділ парсингу й рендерингу, який ми вже описували вище. Readium SDK дозволив використовувати для парсингу нативний код та миттєво виводити його у WebView. Книги будь-якого обсягу та складності відкриваються миттєво, ніби додаток має власний нативний рушій для рендерингу EPUB.
- Безпека та стабільність
Ми забезпечили:
- Контрольований та стабільний обмін даними: Flutter - нативний EPUB reader - WebView. Через Method Channels передаються лише мінімально необхідні дані (команди, номери сторінок).
- Обмеження доступу до файлової системи: книги не зберігаються у відкритому вигляді;
- Стабільний рендеринг у випадку нестандартних EPUB-файлів, адже Readium SDK "перетравлює" навіть пошкоджені чи застарілі книги.
Додатковий функціонал для читача електронних книг
Окрім базового відображення контенту, сучасний EPUB-рідер має забезпечувати комфортне, гнучке й персоналізоване читання. У нашому проєкті ми реалізували низку функцій, які роблять цей досвід приємним і зручним незалежно від уподобань користувача чи типу пристрою.
- Збереження прогресу та закладок
Читач може продовжити читання з того місця, де зупинився — позиція автоматично зберігається в пам’яті застосунку. У нього також є можливість створювати закладки та вільно переходити між ними через інтерфейс рідера. Усі дані зберігаються локально.
- Пошук по тексту та масштабування
У кастомному рідері передбачена можливість пошуку по тексту через ключові слова та фрази. Користувач також має повну свободу налаштування відображення сторінок EPUB WebView: вибір та масштабування шрифтів, зміна розмірів інтервалів, полів тощо. Ці функції працюють через Method Channels.
- Темна тема та кастомізація інтерфейсу
Сучасне читання — це читання у будь-яких умовах: вдень, вночі, в транспорті чи на вулиці. Тому ми реалізували темну тему, вибір кольору фону й тексту, а також широкі можливості кастомізації інтерфейсу. Усі стилі застосовуються динамічно через CSS і JavaScript, зберігаючи при цьому швидкість рендерингу сторінок.
Тестування та налагодження додатка
Продукт виявився для нашої команди дещо нетиповим, тож ми приділили особливу увагу тестуванню, оптимізації та попередженню можливих проблем сумісності.
- Як протестувати EPUB-рідер?
Передусім в арсеналі Readium є набір Test Books. Він містить набір тестових EPUB-файлів, які охоплюють специфічні випадки: застарілі формати, пошкоджені файли тощо. Понад те, команда зібрала власну колекцію EPUB-файлів з різними рівнями складності: прості книги, екзотичні формати, файли з нестандартною версткою, книги з купою зображень, книги зі складними та перевантаженими стилями тощо. Ми декілька разів прогнали усі набори через додаток, аби переконатись, що він відтворює будь-які файли коректно.
- Забезпечення сумісності
У цьому напрямку команда не зіткнулась із суттєвими проблемами. Передусім завдяки тому, що Readium – це стандарт індустрії. Його бібліотеки здатні "перетравлювати" практично будь-які EPUB-файли та виправляти незначні проблеми “на льоту”. Використання системного WebView (Chrome – на Android, Safari – на iOS) гарантує, що книга буде коректно відкриватися та виглядати однаково на 99% пристроїв. З урахуванням відмінностей між рушіями Chrome/Safari стилі та скрипти додатково тестувались окремо на обох платформах.
Висновки
Як виявилось, кросплатформна розробка мобільного EPUB-рідера – це не просто технічне завдання, а справжній виклик, який вимагає балансу між продуктивністю, гнучкістю й зручністю для користувача. У кейсі КСД ми реалізували гібридну архітектуру, що об'єднує Flutter, нативний код і WebView, аби забезпечити стабільну роботу з EPUB-контентом без компромісів у швидкодії чи якості.
Завдяки використанню Readium SDK нам вдалося створити швидку та стабільну кросплатформну EPUB-читалку, яка працює однаково добре на Android та iOS. Так ми надали нашому клієнту продукт гідної якості, а кінцевим користувачам – якісний досвід споживання контенту. Видавництво КСД забезпечило своїм користувачам унікальний сервіс і захистило інтелектуальну власність своїх партнерів. Це рішення має небагато аналогів не лише на українському, але й на глобальному ринку.
Команда WEZOM отримала в даному проєкті унікальний досвід та готова масштабувати й розвивати продукт, адже життєвий шлях нового додатку ледве розпочався. Якщо ви маєте додаткові питання щодо такої розробки, чи шукаєте кастомні рішення для власного бізнесу. Звертайтеся просто зараз! Ми готові допомогти.
FAQ
Як налаштувати epub.js для роботи з Flutter WebView?
Щоб налаштувати epub.js у Flutter, потрібно завантажити його у WebView через локальний HTML-файл, підключити бібліотеку та ініціалізувати рідер у JavaScript. Але з нашого досвіду, така інтеграція буде не найкращим рішенням. Краще звернути увагу на бібліотеки Readium.
Чи можна додати офлайн-режим у гібридний EPUB рідер?
Так, можна. У нашому кейсі EPUB-файл зберігається локально, а WebView працює через локальний веб-сервер, тому весь контент доступний без Інтернету. Офлайн-режим реалізується повністю на пристрої користувача.
Як реалізувати підтримку великих EPUB файлів у Flutter?
У нашому кейсі це завдання вдалося вирішити завдяки нативному парсингу через Readium SDK. Втім в інших проєктах ця задача може вирішуватись іншими шляхами – через підбір відповідної JS-бібліотеки, або ж через написання власного браузерного рушія. Усе залежить від потреб бізнесу.
Які переваги гібридного підходу перед нативними EPUB рідерами?
Різниця між нативним та гібридним рідером криється у складності розробки. Гібридний підхід дозволяє швидко розробити EPUB-читалку із використанням WebView та готових бібліотек, забезпечуючи повну підтримку HTML/CSS. Це економить ресурси, спрощує оновлення й дає гнучкість у кастомізації інтерфейсу без розробки власного рушія з нуля.
Чи можлива інтеграція сторонніх плагінів для EPUB у Flutter додаток?
Так, можлива. Сторонні JavaScript-плагіни для EPUB можна інтегрувати у WebView, а взаємодію з Flutter реалізувати через метод-канали. Це дозволяє розширювати функціонал рідера без змін у самому Flutter-додатку.
Як налаштувати захист від копіювання тексту у EPUB рідері?
Аби захистити текст від копіювання, можна відключити виділення тексту та контекстне меню через CSS (user-select: none) і JavaScript. Також WebView дозволяє блокувати довгі натискання. Варто також обмежити доступ до файлової системи і не зберігати EPUB у відкритому вигляді. Захист можуть надати спеціалізовані DRM-рішення, такі як Readium LCP.
