Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## Unreleased

- Feature [#616](https://github.com/SimformSolutionsPvtLtd/showcaseview/issues/616) - Add overlay animation duration and curve for showcase barrier. Fixed by @[vasu-nageshri](https://github.com/vasu-nageshri)

## 5.1.0

- Fixed [#650](https://github.com/SimformSolutionsPvtLtd/showcaseview/issues/650) - Fix null-check crash in ShowcaseService.getController during didUpdateWidget. Fixed by @[vatsaltanna-simformsolutions](https://github.com/vatsaltanna-simformsolutions)
Expand All @@ -6,7 +10,7 @@
- Fixed [#645](https://github.com/SimformSolutionsPvtLtd/showcaseview/issues/645) - Prevent re-entrant calls to _onComplete during rapid barrier taps. Fixed by @[apizon](https://github.com/apizon)
- Fixed [#639](https://github.com/SimformSolutionsPvtLtd/showcaseview/issues/639) - Add null-safety guards for async sequence transitions. Fixed by @[vasu-nageshri](https://github.com/vasu-nageshri)
- Fixed [#622](https://github.com/SimformSolutionsPvtLtd/showcaseview/issues/622) - Resolve Semantics issue when using `go_router` and `showSemanticsDebugger` flag. Fixed by @[Sahil-Simform](https://github.com/Sahil-Simform)
- Fix [#620](https://github.com/SimformSolutionsPvtLtd/showcaseview/pull/620)- Set MouseRegion opaque property to false for improved interaction in TargetWidget, TooltipWrapper, and FloatingActionWidget. Fixed by @[RuslanTsitser](https://github.com/RuslanTsitser)
- Fixed [#620](https://github.com/SimformSolutionsPvtLtd/showcaseview/pull/620)- Set MouseRegion opaque property to false for improved interaction in TargetWidget, TooltipWrapper, and FloatingActionWidget. Fixed by @[RuslanTsitser](https://github.com/RuslanTsitser)

## [5.0.2]

Expand Down
4 changes: 4 additions & 0 deletions doc/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ Reference](https://pub.dev/documentation/showcaseview/latest/showcaseview/).
ShowcaseView.register(
autoPlayDelay: const Duration(seconds: 3),
semanticEnable: true, // Enable accessibility support globally
// Overlay barrier fades in when showcase first appears (default 200ms).
// Set overlayAnimationDuration to Duration.zero to disable.
overlayAnimationDuration: const Duration(milliseconds: 200),
overlayAnimationCurve: Curves.easeInOut,
globalFloatingActionWidget: (showcaseContext) => FloatingActionWidget(
left: 16,
bottom: 16,
Expand Down
20 changes: 20 additions & 0 deletions lib/src/showcase/showcase_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ class ShowcaseView {
this.blurValue = 0,
this.overlayColor,
this.overlayOpacity,
this.overlayAnimationDuration = Constants.defaultOverlayAnimationDuration,
this.overlayAnimationCurve = Constants.defaultOverlayAnimationCurve,
this.globalTooltipActionConfig,
this.globalTooltipActions,
this.globalFloatingActionWidget,
Expand Down Expand Up @@ -161,6 +163,24 @@ class ShowcaseView {
/// Opacity apply on [overlayColor] (which ranges from 0.0 to 1.0)
final double? overlayOpacity;

/// Duration of the fade-in animation applied to the overlay barrier
/// (background color, opacity and blur) when the showcase first appears.
///
/// This animates only the initial appearance of the barrier and not the
/// transition between individual showcase steps.
///
/// Defaults to 200 milliseconds. Set to [Duration.zero] to show the barrier
/// instantly.
final Duration overlayAnimationDuration;

/// Curve used for the overlay barrier fade-in animation.
///
/// Only applies when [overlayAnimationDuration] is greater than
/// [Duration.zero].
///
/// Defaults to [Curves.easeInOut].
final Curve overlayAnimationCurve;

/// Whether to enable semantic properties for accessibility.
///
/// When set to true, semantic widgets will be added to improve
Expand Down
8 changes: 8 additions & 0 deletions lib/src/utils/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,12 @@ class Constants {

static const Duration defaultAutoPlayDelay = Duration(milliseconds: 2000);
static const Duration defaultScrollDuration = Duration(milliseconds: 300);

/// Default duration for the overlay barrier fade-in animation.
/// Set to [Duration.zero] to make the barrier appear instantly.
static const Duration defaultOverlayAnimationDuration =
Duration(milliseconds: 200);

/// Default curve for the overlay barrier fade-in animation.
static const Curve defaultOverlayAnimationCurve = Curves.easeInOut;
}
68 changes: 48 additions & 20 deletions lib/src/utils/overlay_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import '../showcase/showcase.dart';
import '../showcase/showcase_controller.dart';
import '../showcase/showcase_service.dart';
import '../showcase/showcase_view.dart';
import '../widget/animated_overlay_barrier.dart';
import 'extensions.dart';
import 'shape_clipper.dart';

Expand Down Expand Up @@ -181,30 +182,57 @@ class OverlayManager {
child: const Align(),
);

Widget barrier = GestureDetector(
onTap: firstController.handleBarrierTap,
child: ClipPath(
clipper: ShapeClipper(
linkedObjectData: _getLinkedShowcasesData(controllers),
),
child: firstController.blur <= 0.2
? backgroundContainer
: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: firstController.blur,
sigmaY: firstController.blur,
),
child: backgroundContainer,
),
),
);

final overlayAnimationDuration =
firstController.showcaseView.overlayAnimationDuration;
if (overlayAnimationDuration > Duration.zero) {
// The barrier is kept outside the per-step keyed stack and given a stable
// key so its fade-in animation runs only once when the showcase first
// appears, instead of restarting (and flickering) on every step.
barrier = AnimatedOverlayBarrier(
key: const ValueKey('showcase_overlay_barrier'),
duration: overlayAnimationDuration,
curve: firstController.showcaseView.overlayAnimationCurve,
child: barrier,
);
}

final overlayChild = Stack(
// This key is used to force rebuild the overlay when needed.
// this key enables `_overlayEntry?.markNeedsBuild();` to detect that
// output of the builder has changed.
key: ValueKey(firstController.id),
children: [
GestureDetector(
onTap: firstController.handleBarrierTap,
child: ClipPath(
clipper: ShapeClipper(
linkedObjectData: _getLinkedShowcasesData(controllers),
),
child: firstController.blur <= 0.2
? backgroundContainer
: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: firstController.blur,
sigmaY: firstController.blur,
),
child: backgroundContainer,
),
barrier,
// Tooltips are kept in their own stack so the barrier (and its
// optional fade-in) lives outside this keyed subtree. `Positioned.fill`
// is required because the inner stack has only `Positioned` children
// and would otherwise collapse, clipping the tooltips.
Positioned.fill(
child: Stack(
// This key forces the tooltips to rebuild when needed. It enables
// `_overlayEntry?.markNeedsBuild();` to detect that the output of
// the builder has changed and restarts the per-step tooltip
// animations between showcase steps.
key: ValueKey(firstController.id),
children: [
...controllers.expand((object) => object.tooltipWidgets),
],
),
),
...controllers.expand((object) => object.tooltipWidgets),
],
);

Expand Down
84 changes: 84 additions & 0 deletions lib/src/widget/animated_overlay_barrier.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright (c) 2021 Simform Solutions
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import 'package:flutter/material.dart';

/// Fades the showcase overlay barrier (background color, opacity and blur) in
/// when the showcase first appears.
///
/// The fade only runs once for the lifetime of this widget's [State]. The
/// overlay rebuilds on every showcase step to reposition the highlight cut-out,
/// so this widget is kept outside the per-step keyed subtree in
/// `OverlayManager` to preserve its animation state across steps and avoid the
/// barrier flickering between showcases.
class AnimatedOverlayBarrier extends StatefulWidget {
const AnimatedOverlayBarrier({
required this.duration,
required this.curve,
required this.child,
super.key,
});

/// Duration of the fade-in animation.
final Duration duration;

/// Curve of the fade-in animation.
final Curve curve;

/// The barrier widget (background color/blur) to fade in.
final Widget child;

@override
State<AnimatedOverlayBarrier> createState() => _AnimatedOverlayBarrierState();
}

class _AnimatedOverlayBarrierState extends State<AnimatedOverlayBarrier>
with SingleTickerProviderStateMixin {
late final AnimationController _controller = AnimationController(
vsync: this,
duration: widget.duration,
);

late final Animation<double> _opacity = CurvedAnimation(
parent: _controller,
curve: widget.curve,
);

@override
void initState() {
super.initState();
_controller.forward();
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return FadeTransition(
opacity: _opacity,
child: widget.child,
);
}
}
7 changes: 4 additions & 3 deletions lib/src/widget/floating_action_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,10 @@ class FloatingActionWidget extends StatelessWidget {
width: width,
height: height,
child: Material(
type: MaterialType.transparency,
color: Colors.transparent,
child: child),
type: MaterialType.transparency,
color: Colors.transparent,
child: child,
),
);
}
}
Loading