Реализация генерации отчетов в мобильном приложении
Экспорт отчёта в мобильном приложении — задача, где пользователь ждёт кнопку «Скачать PDF», а разработчик сталкивается с ограничениями памяти на устройстве, асинхронной генерацией, сложным версткой многостраничного документа и шерингом в нужный формат.
PDF-генерация: три подхода
1. Нативный рендеринг через Canvas (iOS: PDFKit, Android: PdfDocument)
На iOS — UIGraphicsPDFRenderer, самый контролируемый вариант. Рисуем каждый элемент вручную через Core Graphics:
func generateReport(data: ReportData) -> Data {
let pageRect = CGRect(x: 0, y: 0, width: 595, height: 842) // A4 в points
let renderer = UIGraphicsPDFRenderer(bounds: pageRect)
return renderer.pdfData { context in
context.beginPage()
let ctx = context.cgContext
// Заголовок
let titleFont = UIFont.boldSystemFont(ofSize: 18)
let title = data.title as NSString
title.draw(at: CGPoint(x: 40, y: 40), withAttributes: [
.font: titleFont,
.foregroundColor: UIColor.black
])
// Таблица данных
drawTable(ctx, rows: data.rows, startY: 80, pageWidth: pageRect.width)
// Если данные не помещаются — новая страница
if needsNewPage {
context.beginPage()
// продолжаем...
}
}
}
Трудоёмко, но результат — точный контроль над пиксельным выводом. Хорошо для фиксированного шаблона отчёта.
На Android через PdfDocument:
val document = PdfDocument()
val pageInfo = PdfDocument.PageInfo.Builder(595, 842, 1).create()
val page = document.startPage(pageInfo)
val canvas = page.canvas
val paint = Paint().apply { textSize = 18f; isFakeBoldText = true }
canvas.drawText(data.title, 40f, 60f, paint)
document.finishPage(page)
val stream = ByteArrayOutputStream()
document.writeTo(stream)
document.close()
2. HTML → PDF через WebView (Flutter: printing package)
Генерируем HTML-шаблон, конвертируем в PDF. Проще для сложных макетов, таблиц и текстовых блоков — HTML/CSS гибче Canvas. На Flutter printing + pdf package:
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
Future<Uint8List> buildPdf(ReportData data) async {
final doc = pw.Document();
final font = await PdfGoogleFonts.notoSansRegular();
final boldFont = await PdfGoogleFonts.notoSansBold();
doc.addPage(
pw.MultiPage(
pageFormat: PdfPageFormat.a4,
build: (context) => [
pw.Header(text: data.title, textStyle: pw.TextStyle(font: boldFont, fontSize: 18)),
pw.SizedBox(height: 16),
pw.TableHelper.fromTextArray(
headers: data.headers,
data: data.rows,
cellStyle: pw.TextStyle(font: font, fontSize: 10),
),
],
),
);
return doc.save();
}
pw.MultiPage автоматически разбивает на страницы — решает главную проблему длинных отчётов.
3. Серверная генерация (рекомендовано для сложных отчётов)
Отчёты с большим объёмом данных, сложными диаграммами или корпоративным брендингом лучше генерировать на сервере. Puppeteer (Node.js) рендерит HTML + Charts в PDF с точностью браузерного движка. Клиент получает ссылку или файл по WebSocket после завершения.
Это единственный правильный путь если в отчёт нужно включить интерактивные графики (Echarts, Highcharts) — на устройстве их не отрендеришь без WebView.
CSV-экспорт
Проще, чем PDF, но есть нюансы с кодировкой. Excel ожидает UTF-8 с BOM — иначе кириллица отображается мусором. На Flutter:
String buildCsv(List<List<dynamic>> rows) {
final buffer = StringBuffer();
buffer.write('\uFEFF'); // UTF-8 BOM для корректного открытия в Excel
for (final row in rows) {
buffer.writeln(row.map((cell) {
final str = cell.toString();
// Экранируем ячейки с запятыми и кавычками
return str.contains(',') || str.contains('"')
? '"${str.replaceAll('"', '""')}"'
: str;
}).join(','));
}
return buffer.toString();
}
Шеринг сгенерированного файла
share_plus на Flutter, UIActivityViewController на iOS, FileProvider + Intent.ACTION_SEND на Android. Сохранение в галерею / Файлы — через path_provider + open_filex.
На iOS нужен NSPhotoLibraryAddUsageDescription в Info.plist для сохранения в Photos, UIFileSharingEnabled для доступа к файлам через Files.app.
Типичные проблемы
Генерация PDF с таблицей из 10 000 строк на устройстве — OutOfMemoryError на Android или memory warning на iOS. Решение: пагинация данных, генерация постранично, для больших объёмов — сервер.
Кириллица в PDF без встроенного шрифта — белые прямоугольники вместо букв. Обязательно встраивать кастомный font с поддержкой Unicode через pw.Font.ttf(...).
Что входит в работу
- Выбор подхода под специфику данных и макет (нативный / HTML→PDF / сервер)
- Реализация шаблонов отчётов (PDF/CSV/Excel)
- Интеграция шеринга и сохранения файлов
- Обработка edge cases: пустые данные, большие объёмы, переносы страниц
Сроки
Один шаблон PDF-отчёта с базовым форматированием: 2–3 дня. Несколько типов отчётов с диаграммами и серверной генерацией: 1–2 недели. Стоимость рассчитывается индивидуально.







