#flutter #latex #debug #mobile

Flutter 3.38 quebrou meu app por causa de LaTeX (733 erros por frame!)

Miuna Hamasaki

gente. eu quase chorei.

abri meu app no topico "Tips & Tricks" e o terminal EXPLODIU. tipo, 733 erros por frame. o app ficou inutilizavel, travando, tudo quebrado. achei q era bug no meu checklist (pq ja tinha dado problema antes), mas nao era.

A Investigação (spoiler: foi o LaTeX)

passei um tempao arrumando o checklist (NoteChecklistContent). achei bugs reais la:

  • IntrinsicWidth brigando com Flexible (conflito de constraints)
  • Checkbox com sizing zuado dentro de Row
  • mainAxisSize: MainAxisSize.min num Row com filhos Flexible

corrigi tudo: troquei Checkbox por Icon + GestureDetector, FlexibleExpanded, etc. eram correções validas, mas nao resolveram o problema principal. (一_一)

ai redirecionei a saida do flutter run pra um arquivo de log e finalmente vi a cascata real:

Erro Quantidade
Invalid argument(s): string is not well-formed UTF-16 3
RenderLine does not implement "computeDryBaseline" 1
RenderBox was not laid out: RenderIntrinsicWidth 1
RenderBox was not laid out: RenderIndexedSemantics 288
'parentDataDirty' assertion failures 440
TOTAL ~733/frame

A Causa Raiz

o topico tinha uma nota com LaTeX inline: $E = mc^2$. o GptMarkdown converte $...$ pra \(...\) e manda pro flutter_math_fork renderizar via Math.tex().

acontece q o flutter_math_fork v0.7.4 usa uma classe RenderLine (custom RenderBox) que nao implementa computeDryBaseline, um metodo que virou obrigatorio no Flutter 3.38.

A CASCATA DO INFERNO

resultado: o RenderLine falha → o RenderIntrinsicWidth pai nao consegue fazer layout → o RenderIndexedSemantics (acessibilidade) falha em cascata → o framework detecta parentDataDirty em centenas de render objects → repete a cada frame. loop infinito de dor e sofrimento.

e o pior: nao tem versao nova de nenhum dos dois pacotes (gpt_markdown 1.1.5 e flutter_math_fork 0.7.4). os dois tao abandonados e incompativeis com Flutter 3.38. (ꐦ°᷄д°᷅)

O Workaround (sim, gambiarra)

ja q nao da pra usar o Math.tex() sem crashar, criei um builder seguro q renderiza LaTeX como texto estilizado (monospace + italico) em vez de formula bonitinha:

static Widget safeLatexBuilder(
  BuildContext context,
  String tex,
  TextStyle textStyle,
  bool inline,
) {
  final theme = Theme.of(context);
  return Text(
    tex,
    style: textStyle.copyWith(
      fontFamily: 'monospace',
      fontStyle: FontStyle.italic,
      color: theme.colorScheme.onSurface.withValues(alpha: 0.85),
    ),
  );
}

tambem fiz um pre-processamento proprio pra converter $...$\(...\) fora do GptMarkdown, pq o regex interno dele tava gerando strings UTF-16 malformadas:

static String _convertDollarLatex(String text) {
  var result = text.replaceAllMapped(
    RegExp(r'(?<!\\)\$\$(.*?)(?<!\\)\$\$', dotAll: true),
    (match) => '\\[${match[1] ?? ""}\\]',
  );
  if (!result.contains(r'\(')) {
    result = result.replaceAllMapped(
      RegExp(r'(?<!\\)\$(.*?)(?<!\\)\$'),
      (match) => '\\(${match[1] ?? ""}\\)',
    );
  }
  return result;
}

ai chamei o GptMarkdown com useDollarSignsForLatex: false (ja q a conversao foi feita na mao) e apliquei o latexBuilder seguro em todos os lugares q usam GptMarkdown diretamente:

  • study_session_screen.dart (3 usos)
  • edit_note_dialog.dart (1 uso)
  • flashcard_bubble_content.dart (1 uso)

Resultado

Metrica Antes Depois
parentDataDirty assertions 440/frame 0
RenderBox was not laid out 289/frame 0
computeDryBaseline errors 1/frame 0
App usavel nao sim

o trade-off eh q o LaTeX nao renderiza como formula bonita (aparece E = mc^2 em monospace em vez da formula formatada). mas pelo menos o app funciona. quando os mantenedores dos pacotes acordarem e lançarem update, eu reverto tudo.

MORAL DA HISTÓRIA

sempre redirecione o log pra um arquivo. se eu tivesse ficado olhando o terminal scrollando 733 erros por frame eu nunca ia achar a causa raiz. e desconfie dos seus proprios palpites, eu perdi tempo no checklist sendo q o problema era o LaTeX o tempo todo. ¯\_(ツ)_/¯