| .. | .. |
|---|
| 1 | 1 | import 'dart:convert'; |
|---|
| 2 | 2 | import 'dart:math'; |
|---|
| 3 | +import 'dart:typed_data'; |
|---|
| 3 | 4 | |
|---|
| 4 | 5 | import 'package:flutter/material.dart'; |
|---|
| 5 | 6 | import 'package:flutter/services.dart'; |
|---|
| .. | .. |
|---|
| 8 | 9 | import '../models/message.dart'; |
|---|
| 9 | 10 | import '../theme/app_theme.dart'; |
|---|
| 10 | 11 | import 'image_viewer.dart'; |
|---|
| 12 | + |
|---|
| 13 | +// Cache decoded image bytes to prevent flicker on widget rebuild |
|---|
| 14 | +final Map<String, Uint8List> _imageCache = {}; |
|---|
| 11 | 15 | |
|---|
| 12 | 16 | /// Chat message bubble with support for text, voice, and image types. |
|---|
| 13 | 17 | class MessageBubble extends StatelessWidget { |
|---|
| .. | .. |
|---|
| 208 | 212 | return const Text('Image unavailable'); |
|---|
| 209 | 213 | } |
|---|
| 210 | 214 | |
|---|
| 211 | | - final bytes = base64Decode( |
|---|
| 212 | | - message.imageBase64!.contains(',') |
|---|
| 213 | | - ? message.imageBase64!.split(',').last |
|---|
| 214 | | - : message.imageBase64!, |
|---|
| 215 | | - ); |
|---|
| 215 | + // Cache decoded bytes to prevent flicker on rebuild |
|---|
| 216 | + final bytes = _imageCache.putIfAbsent(message.id, () { |
|---|
| 217 | + final raw = message.imageBase64!; |
|---|
| 218 | + return Uint8List.fromList(base64Decode( |
|---|
| 219 | + raw.contains(',') ? raw.split(',').last : raw, |
|---|
| 220 | + )); |
|---|
| 221 | + }); |
|---|
| 216 | 222 | |
|---|
| 217 | 223 | return Column( |
|---|
| 218 | 224 | crossAxisAlignment: CrossAxisAlignment.start, |
|---|
| .. | .. |
|---|
| 232 | 238 | width: 260, |
|---|
| 233 | 239 | height: 180, |
|---|
| 234 | 240 | fit: BoxFit.cover, |
|---|
| 241 | + gaplessPlayback: true, |
|---|
| 235 | 242 | errorBuilder: (_, e, st) => const SizedBox( |
|---|
| 236 | 243 | width: 260, |
|---|
| 237 | 244 | height: 60, |
|---|