-
-
Notifications
You must be signed in to change notification settings - Fork 26
Description
π Bug Report
When using the parameters memCacheWidth
or memCacheHeight
in the OctoImage
widget, it causes unnecessary repaints on rebuilds β even when the image configuration hasn't changed.
Root Cause
The issue arises because ResizeImage.resizeIfNeeded
creates a new ResizeImage
instance every time, and that class does not override equality (==). So this condition in the widget always evaluates to true, even with identical inputs:
Relevant code in lib/src/image/image.dart
:
// ...
image = ResizeImage.resizeIfNeeded(
memCacheWidth,
memCacheHeight,
image,
)
// ...
@override
void didUpdateWidget(OctoImage oldWidget) {
// ....
if (oldWidget.image != widget.image) {
}
// ...
}
As a result, even if the image
, memCacheWidth
, and memCacheHeight
values are the same, the widget incorrectly treats the image as changed and repaints, which negatively impacts performance.
This issue stems from Flutterβs image caching system. I have already reported it here:
Note: This behavior only occurs when memCacheWidth
or memCacheHeight
is provided.
If both are null
, ResizeImage.resizeIfNeeded
returns the original image provider and equality works as expected.
Expected behavior
If image
, memCacheWidth
, and memCacheHeight
values are unchanged, the widget should not trigger a repaint.
Reproduction steps
import 'package:flutter/material.dart';
import 'package:octo_image/octo_image.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
static const NetworkImage flutterLogoUrl = NetworkImage(
'https://storage.googleapis.com/cms-storage-bucket/lockup_flutter_vertical.a9d6ce81aee44ae017ee.png',
);
ImageProvider provider = flutterLogoUrl;
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() {
provider = flutterLogoUrl;
}),
),
body: Center(
child: RepaintBoundary(
child: OctoImage(
image: provider,
memCacheWidth: 300,
memCacheHeight: 400,
),
),
),
);
}
}
- Tap the floating action button β
setState
reassigns the same image, butOctoImage
repaints due to failed equality.
Configuration
Version: 2.1.0
Platform:
- π± iOS
- π€ Android