Skip to content

Commit

Permalink
fix: 🐛 Targeted widget focusing issue when we are applying size const…
Browse files Browse the repository at this point in the history
…raints on root widget(#390 & #383)
  • Loading branch information
jaiminrana05 committed Sep 21, 2023
1 parent 5ee2d91 commit 069a99e
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 39 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## [2.0.4] (Un-Released)
- Fixed [#390](https://github.com/SimformSolutionsPvtLtd/flutter_showcaseview/issues/390) - Targeted widget focusing issue when we are applying size constraint on root widget(MaterialApp).
- Fixed [#383](https://github.com/SimformSolutionsPvtLtd/flutter_showcaseview/issues/383) - Targeted widget focusing issue when we applying size constraint on root widget(MaterialApp).

## [2.0.3]
- Feature [#148](https://github.com/SimformSolutionsPvtLtd/flutter_showcaseview/issues/148) - Add feasibility to add `textDirection` of `title` and `description`.
- Feature [#272](https://github.com/SimformSolutionsPvtLtd/flutter_showcaseview/issues/272) - Add barrier click callback.
Expand Down Expand Up @@ -83,7 +87,7 @@

## [1.0.0]

- Fixed [#95](https://github.com/SimformSolutionsPvtLtd/flutter_showcaseview/issues/95) - Migrated to null safety.
- Fixed [#95](https://github.com/SimformSolutionsPvtLtd/flutter_showcaseview/issues/95) - Migrated to null safety.
- Fixed [#74](https://github.com/SimformSolutionsPvtLtd/flutter_showcaseview/issues/74) - Long text description is hidden.
- Fixed [#76](https://github.com/SimformSolutionsPvtLtd/flutter_showcaseview/issues/76) - Overlay is not displayed properly on web.
- Fixed [#81](https://github.com/SimformSolutionsPvtLtd/flutter_showcaseview/issues/81) - Crash on hot reload.
Expand Down
6 changes: 3 additions & 3 deletions example/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
buildscript {
ext.kotlin_version = '1.4.32'
ext.kotlin_version = '1.5.20'
repositories {
google()
jcenter()
}

dependencies {
classpath 'com.android.tools.build:gradle:4.1.3'
classpath 'com.android.tools.build:gradle:7.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
Expand All @@ -26,6 +26,6 @@ subprojects {
project.evaluationDependsOn(':app')
}

task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
2 changes: 1 addition & 1 deletion example/android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
33 changes: 26 additions & 7 deletions lib/src/get_position.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,23 @@ class GetPosition {
final EdgeInsets padding;
final double? screenWidth;
final double? screenHeight;
final RenderObject? rootRenderObject;

GetPosition({
this.key,
this.padding = EdgeInsets.zero,
this.screenWidth,
this.screenHeight,
this.rootRenderObject,
});

Rect getRect() {
final box = key!.currentContext!.findRenderObject() as RenderBox;

var boxOffset = box.localToGlobal(const Offset(0.0, 0.0));
var boxOffset = box.localToGlobal(
const Offset(0.0, 0.0),
ancestor: rootRenderObject,
);
if (boxOffset.dx.isNaN || boxOffset.dy.isNaN) {
return const Rect.fromLTRB(0, 0, 0, 0);
}
Expand All @@ -61,7 +66,10 @@ class GetPosition {
///Get the bottom position of the widget
double getBottom() {
final box = key!.currentContext!.findRenderObject() as RenderBox;
final boxOffset = box.localToGlobal(const Offset(0.0, 0.0));
final boxOffset = box.localToGlobal(
const Offset(0.0, 0.0),
ancestor: rootRenderObject,
);
if (boxOffset.dy.isNaN) return padding.bottom;
final bottomRight = box.size.bottomRight(boxOffset);
return bottomRight.dy + padding.bottom;
Expand All @@ -70,7 +78,10 @@ class GetPosition {
///Get the top position of the widget
double getTop() {
final box = key!.currentContext!.findRenderObject() as RenderBox;
final boxOffset = box.localToGlobal(const Offset(0.0, 0.0));
final boxOffset = box.localToGlobal(
const Offset(0.0, 0.0),
ancestor: rootRenderObject,
);
if (boxOffset.dy.isNaN) return 0 - padding.top;
final topLeft = box.size.topLeft(boxOffset);
return topLeft.dy - padding.top;
Expand All @@ -79,7 +90,10 @@ class GetPosition {
///Get the left position of the widget
double getLeft() {
final box = key!.currentContext!.findRenderObject() as RenderBox;
final boxOffset = box.localToGlobal(const Offset(0.0, 0.0));
final boxOffset = box.localToGlobal(
const Offset(0.0, 0.0),
ancestor: rootRenderObject,
);
if (boxOffset.dx.isNaN) return 0 - padding.left;
final topLeft = box.size.topLeft(boxOffset);
return topLeft.dx - padding.left;
Expand All @@ -88,10 +102,15 @@ class GetPosition {
///Get the right position of the widget
double getRight() {
final box = key!.currentContext!.findRenderObject() as RenderBox;
final boxOffset = box.localToGlobal(const Offset(0.0, 0.0));
final boxOffset = box.localToGlobal(
const Offset(0.0, 0.0),
ancestor: rootRenderObject,
);
if (boxOffset.dx.isNaN) return padding.right;
final bottomRight =
box.size.bottomRight(box.localToGlobal(const Offset(0.0, 0.0)));
final bottomRight = box.size.bottomRight(box.localToGlobal(
const Offset(0.0, 0.0),
ancestor: rootRenderObject,
));
return bottomRight.dx + padding.right;
}

Expand Down
21 changes: 16 additions & 5 deletions lib/src/layout_overlays.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,14 @@ class AnchoredOverlay extends StatelessWidget {
final bool showOverlay;
final OverlayBuilderCallback? overlayBuilder;
final Widget? child;
final RenderObject? rootRenderObject;

const AnchoredOverlay({
Key? key,
this.showOverlay = false,
this.overlayBuilder,
this.child,
this.rootRenderObject,
}) : super(key: key);

@override
Expand All @@ -65,10 +67,15 @@ class AnchoredOverlay extends StatelessWidget {
// To calculate the "anchor" point we grab the render box of
// our parent Container and then we find the center of that box.
final box = context.findRenderObject() as RenderBox;
final topLeft =
box.size.topLeft(box.localToGlobal(const Offset(0.0, 0.0)));
final bottomRight =
box.size.bottomRight(box.localToGlobal(const Offset(0.0, 0.0)));

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 ||
Expand All @@ -82,7 +89,11 @@ class AnchoredOverlay extends StatelessWidget {
bottomRight.dy,
);
final anchorCenter = box.size.center(topLeft);
return overlayBuilder!(overlayContext, anchorBounds, anchorCenter);
return overlayBuilder!(
overlayContext,
anchorBounds,
anchorCenter,
);
},
child: child,
);
Expand Down
55 changes: 47 additions & 8 deletions lib/src/showcase.dart
Original file line number Diff line number Diff line change
Expand Up @@ -359,20 +359,50 @@ class _ShowcaseState extends State<Showcase> {
bool _enableShowcase = true;
Timer? timer;
GetPosition? position;
Size? rootWidgetSize;
RenderObject? rootRenderObject;

ShowCaseWidgetState get showCaseWidgetState => ShowCaseWidget.of(context);

@override
void initState() {
super.initState();
initRootWidget();
}

void initRootWidget() {
ambiguate(WidgetsBinding.instance)?.addPostFrameCallback((_) {
rootWidgetSize = showCaseWidgetState.rootWidgetSize;
rootRenderObject = showCaseWidgetState.rootRenderObject;
});
}

void reCalculateRoot() {
ambiguate(WidgetsBinding.instance)?.addPostFrameCallback((_) {
final rootWidget =
context.findRootAncestorStateOfType<State<WidgetsApp>>();
rootRenderObject = rootWidget?.context.findRenderObject();
rootWidgetSize = rootWidget == null
? MediaQuery.of(context).size
: (rootRenderObject as RenderBox).size;
});
}

@override
void didChangeDependencies() {
super.didChangeDependencies();
_enableShowcase = showCaseWidgetState.enableShowcase;

reCalculateRoot();

if (_enableShowcase) {
final size = MediaQuery.of(context).size;
position ??= GetPosition(
rootRenderObject: rootRenderObject,
key: widget.key,
padding: widget.targetPadding,
screenWidth: MediaQuery.of(context).size.width,
screenHeight: MediaQuery.of(context).size.height,
screenWidth: rootWidgetSize?.width ?? size.width,
screenHeight: rootWidgetSize?.height ?? size.height,
);
showOverlay();
}
Expand Down Expand Up @@ -414,15 +444,23 @@ class _ShowcaseState extends State<Showcase> {
Widget build(BuildContext context) {
if (_enableShowcase) {
return AnchoredOverlay(
key: showCaseWidgetState.anchoredOverlayKey,
rootRenderObject: rootRenderObject,
overlayBuilder: (context, rectBound, offset) {
final size = MediaQuery.of(context).size;
final size = rootWidgetSize ?? MediaQuery.of(context).size;
position = GetPosition(
rootRenderObject: rootRenderObject,
key: widget.key,
padding: widget.targetPadding,
screenWidth: size.width,
screenHeight: size.height,
);
return buildOverlayOnTarget(offset, rectBound.size, rectBound, size);
return buildOverlayOnTarget(
offset,
rectBound.size,
rectBound,
size,
);
},
showOverlay: true,
child: widget.child,
Expand Down Expand Up @@ -476,6 +514,7 @@ class _ShowcaseState extends State<Showcase> {
Rect rectBound,
Size screenSize,
) {
final mediaQuerySize = MediaQuery.of(context).size;
var blur = 0.0;
if (_showShowCase) {
blur = widget.blurValue ?? showCaseWidgetState.blurValue;
Expand Down Expand Up @@ -510,17 +549,17 @@ class _ShowcaseState extends State<Showcase> {
? BackdropFilter(
filter: ImageFilter.blur(sigmaX: blur, sigmaY: blur),
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
width: mediaQuerySize.width,
height: mediaQuerySize.height,
decoration: BoxDecoration(
color: widget.overlayColor
.withOpacity(widget.overlayOpacity),
),
),
)
: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
width: mediaQuerySize.width,
height: mediaQuerySize.height,
decoration: BoxDecoration(
color: widget.overlayColor
.withOpacity(widget.overlayOpacity),
Expand Down
21 changes: 21 additions & 0 deletions lib/src/showcase_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import 'package:flutter/material.dart';

import '../showcaseview.dart';
import 'extension.dart';

class ShowCaseWidget extends StatefulWidget {
final Builder builder;
Expand Down Expand Up @@ -122,6 +123,9 @@ class ShowCaseWidget extends StatefulWidget {
class ShowCaseWidgetState extends State<ShowCaseWidget> {
List<GlobalKey>? ids;
int? activeWidgetId;
RenderObject? rootRenderObject;
Size? rootWidgetSize;
Key? anchoredOverlayKey;

/// These properties are only here so that it can be accessed by
/// [Showcase]
Expand All @@ -144,6 +148,23 @@ class ShowCaseWidgetState extends State<ShowCaseWidget> {
/// Returns value of [ShowCaseWidget.blurValue]
double get blurValue => widget.blurValue;

@override
void initState() {
super.initState();
initRootWidget();
}

void initRootWidget() {
ambiguate(WidgetsBinding.instance)?.addPostFrameCallback((_) {
final rootWidget = context.findAncestorStateOfType<State<WidgetsApp>>();
rootRenderObject = rootWidget?.context.findRenderObject();
rootWidgetSize = rootWidget == null
? MediaQuery.of(context).size
: (rootRenderObject as RenderBox).size;
anchoredOverlayKey = UniqueKey();
});
}

/// Starts Showcase view from the beginning of specified list of widget ids.
/// If this function is used when showcase has been disabled then it will
/// throw an exception.
Expand Down
Loading

0 comments on commit 069a99e

Please sign in to comment.