Open pamafe1976 opened 2 months ago
I have found this line in layout_overlays.dart be the one causing the problem, since context is detacched when it attempts findRenderObject.
final box = context.findRenderObject() as RenderBox;
However I dont know how to property guard it.
class AnchoredOverlay extends StatelessWidget {
final bool showOverlay;
final OverlayBuilderCallback? overlayBuilder;
final Widget? child;
final RenderObject? rootRenderObject;
const AnchoredOverlay({
super.key,
this.showOverlay = false,
this.overlayBuilder,
this.child,
this.rootRenderObject,
});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
return OverlayBuilder(
showOverlay: showOverlay,
overlayBuilder: (overlayContext) {
final box = context.findRenderObject() as RenderBox; // THIS LINE IS THE ONE CAUSING THE ERROR
final topLeft = box.size.topLeft(
box.localToGlobal(
const Offset(0.0, 0.0),
ancestor: rootRenderObject,
),
);
final bottomRight = box.size.bottomRight(
box.localToGlobal(
const Offset(0.0, 0.0),
ancestor: rootRenderObject,
),
);
Rect anchorBounds;
anchorBounds = (topLeft.dx.isNaN ||
topLeft.dy.isNaN ||
bottomRight.dx.isNaN ||
bottomRight.dy.isNaN)
? const Rect.fromLTRB(0.0, 0.0, 0.0, 0.0)
: Rect.fromLTRB(
topLeft.dx,
topLeft.dy,
bottomRight.dx,
bottomRight.dy,
);
final anchorCenter = box.size.center(topLeft);
return overlayBuilder!(overlayContext, anchorBounds, anchorCenter);
},
child: child,
);
},
);
}
}
I know that somehow the problem is because of rootRenderObject being added here. I don't know why but for me everything works in showcaseview 2.0.3. and this rootRenderObject is something new. I think this is the cause
Hi @mehrdad1990. The rootRenderObject seems to be there since always. It fails in 2.0.3, I tested it. The problem is not that easy to reproduce, because you have to trigger a rebuild when the app has gone to background in IOS. Maybe thats something that should not happen and its a bug in my app, I dont know, but any case, the showcase should complete the build without throwing, like all other widgets. The code I posted reproduces this issue. Regards
@pamafe1976 what happens if you remove showcaseview as parent of your widget when you trigger a rebuild? no error? for me I had a throw-error with localToGlobal method call in showcaseview in the latest releases. but there was no error in 2.0.3 and I checked the code differences and I thought maybe it is because of rootRenderObject. maybe I'm wrong I'm not sure. one solution that I did was to remove showcaseview as parent using a boolean when I'm done with it.
@mehrdad1990 The problem is with rootRenderObject, specially the one that is inside the GetPosition class. For what I have learned, after app goes to background, IOS will trigger several builds, to make image snapshots of the app in the launcher (probably for landscape and portrait modes, and also dark and light themes) In that builds anything accessing context gets rebuilt. Checking State.mounted in that situation usually returns true, but the context is marked as detached, as seen by context.toString. When findRenderObject is called with such a context, _lifecycleState returns inactive, and therefore the framework throws the error.
Conditionally removing the ShowCaseWidget is not the best solution, since the user can send the app to back before the showcase has ended, and that would anyway trigger the error.
I have created an issue in Flutter just to check if the IOS build calls are normal.
But for sure Showcase and ShowCaseWidget should be able to build in that situation, as every other widget does.
Regards
@mehrdad1990 The problem is with rootRenderObject, specially the one that is inside the GetPosition class. For what I have learned, after app goes to background, IOS will trigger several builds, to make image snapshots of the app in the launcher (probably for landscape and portrait modes, and also dark and light themes) In that builds anything accessing context gets rebuilt. Checking State.mounted in that situation usually returns true, but the context is marked as detached, as seen by context.toString. When findRenderObject is called with such a context, _lifecycleState returns inactive, and therefore the framework throws the error.
Conditionally removing the ShowCaseWidget is not the best solution, since the user can send the app to back before the showcase has ended, and that would anyway trigger the error.
I have created an issue in Flutter just to check if the IOS build calls are normal.
But for sure Showcase and ShowCaseWidget should be able to build in that situation, as every other widget does.
Regards
Does this PR fix the issue? this is a try-catch wrap. check if it fixes the problem but I don't think this is the best.
Hi @mehrdad1990 , Unfortunately I cant test the PR right now, but I'm pretty sure the error will still be thrown as long as findRenderObject is called. What ended up working for me was this replacement for AnchoredOverlay. It only assigns the box the first time, to prevent it from calling findRenderObject when the app goes to background. Its not nice, I know, but it was the only option I found with my limited knowledge of the package. Maybe it helps someone with more insight to find out how to solve it.
Regards
class AnchoredOverlay extends StatefulWidget {
final bool showOverlay;
final OverlayBuilderCallback? overlayBuilder;
final Widget? child;
final RenderObject? rootRenderObject;
const AnchoredOverlay({
super.key,
this.showOverlay = false,
this.overlayBuilder,
this.child,
this.rootRenderObject,
});
@override
State<AnchoredOverlay> createState() => _AnchoredOverlay();
}
class _AnchoredOverlay extends State<AnchoredOverlay> {
RenderBox? box;
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
return (mounted)
? OverlayBuilder(
showOverlay: widget.showOverlay,
overlayBuilder: (overlayContext) {
// To calculate the "anchor" point we grab the render box of
// our parent Container and then we find the center of that box.
box ??= context.findRenderObject() as RenderBox;
if (box!.attached == false) {
return SizedBox(child: widget.child);
}
final topLeft = box!.size.topLeft(
box!.localToGlobal(
const Offset(0.0, 0.0),
ancestor: widget.rootRenderObject,
),
);
final bottomRight = box!.size.bottomRight(
box!.localToGlobal(
const Offset(0.0, 0.0),
ancestor: widget.rootRenderObject,
),
);
Rect anchorBounds;
anchorBounds = (topLeft.dx.isNaN ||
topLeft.dy.isNaN ||
bottomRight.dx.isNaN ||
bottomRight.dy.isNaN)
? const Rect.fromLTRB(0.0, 0.0, 0.0, 0.0)
: Rect.fromLTRB(
topLeft.dx,
topLeft.dy,
bottomRight.dx,
bottomRight.dy,
);
final anchorCenter = box!.size.center(topLeft);
return widget.overlayBuilder!(
overlayContext, anchorBounds, anchorCenter);
},
child: widget.child,
)
: SizedBox(child: widget.child);
},
);
}
}
Hi,
I've been working a couple days trying to isolate this problem, and I simplified it to a point where I know it appears with a combination of Showcaseview, Getx and ResponsiveSizer. I hope someone can point me in the right direction to solve it. I have made a small app to reproduce the problem I was having.
STEPS TO REPRODUCE Start the test app. Close the first tooltip. Press button "ONE" to go to the seconds page Press button "TWO" to return to the first page. Minimize the app.
You will get an exception in framework.dart
FACTS: 1) Only happens with the ResponsiveSizer as parent of GetMaterialApp, apparently because it rebuilds when the app goes to background. 2) Only happens in IOS physical or simulator, probably because the build is not triggered in Android (Dont know why) 3) The culprit seems to be showcase, when it calls "findRenderObject" I have checked that when the error happens, mounted is true, but the context is marked as detached and _lifecycleState is inactive, therefore the error 4) Eventhough crashlytics reports this as a crash, the app is never closed, I guess because this happens when the apps goes to the background. 5) Notice Im using a fork of showcaseview because I need it to work in Fluter 3.22, but I have checked that the issue is the same with flutter 3.19 and the original package
Thanks!
CODE TO REPRODUCE
PUBSPEC.YAML
FLUTTER DOCTOR -V: