Skip to content

Commit

Permalink
Feature/scroll active showcase into view (SimformSolutionsPvtLtd#192)
Browse files Browse the repository at this point in the history
* feat: scroll active showcase into view

* fix: revert example app compileSdk to 30

* fix: reset listview before restarting showcase

* fix: formatting
  • Loading branch information
RobertHeim authored Apr 30, 2022
1 parent 799ea11 commit c680229
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 86 deletions.
147 changes: 89 additions & 58 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,18 @@ class _MailPageState extends State<MailPage> {
final GlobalKey _three = GlobalKey();
final GlobalKey _four = GlobalKey();
final GlobalKey _five = GlobalKey();
final GlobalKey _six = GlobalKey();
List<Mail> mails = [];

final scrollController = ScrollController();

@override
void initState() {
super.initState();
//Start showcase view after current widget frames are drawn.
WidgetsBinding.instance!.addPostFrameCallback(
(_) => ShowCaseWidget.of(context)!
.startShowCase([_one, _two, _three, _four, _five]),
.startShowCase([_one, _two, _three, _four, _five, _six]),
);
mails = [
Mail(
Expand Down Expand Up @@ -133,6 +136,12 @@ class _MailPageState extends State<MailPage> {
];
}

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

@override
Widget build(BuildContext context) {
return Scaffold(
Expand Down Expand Up @@ -243,10 +252,13 @@ class _MailPageState extends State<MailPage> {
const Padding(padding: EdgeInsets.only(top: 8)),
Expanded(
child: ListView.builder(
controller: scrollController,
physics: const BouncingScrollPhysics(),
itemBuilder: (context, index) {
if (index == 0) {
return showcaseMailTile(context);
return showcaseMailTile(_three, true, context);
} else if (index == mails.length - 1) {
return showcaseMailTile(_six, false, context);
}
return MailTile(mails[index % mails.length]);
},
Expand All @@ -264,8 +276,12 @@ class _MailPageState extends State<MailPage> {
backgroundColor: Theme.of(context).primaryColor,
onPressed: () {
setState(() {
/* reset ListView to ensure that the showcased widgets are
* currently rendered so the showcased keys are available in the
* render tree. */
scrollController.jumpTo(0);
ShowCaseWidget.of(context)!
.startShowCase([_one, _two, _three, _four, _five]);
.startShowCase([_one, _two, _three, _four, _five, _six]);
});
},
child: const Icon(
Expand All @@ -276,7 +292,8 @@ class _MailPageState extends State<MailPage> {
);
}

GestureDetector showcaseMailTile(BuildContext context) {
GestureDetector showcaseMailTile(GlobalKey<State<StatefulWidget>> key,
bool showCaseDetail, BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.push<void>(
Expand All @@ -289,7 +306,7 @@ class _MailPageState extends State<MailPage> {
child: Container(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Showcase(
key: _three,
key: key,
description: 'Tap to check mail',
disposeOnTap: true,
onTargetClick: () {
Expand All @@ -315,64 +332,47 @@ class _MailPageState extends State<MailPage> {
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Showcase.withWidget(
key: _four,
height: 50,
width: 140,
shapeBorder: const CircleBorder(),
radius: const BorderRadius.all(Radius.circular(150)),
container: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
width: 45,
height: 45,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Color(0xffFCD8DC),
),
child: Center(
child: Text(
'S',
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold,
fontSize: 16,
if (showCaseDetail)
Showcase.withWidget(
key: _four,
height: 50,
width: 140,
shapeBorder: const CircleBorder(),
radius: const BorderRadius.all(Radius.circular(150)),
container: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
width: 45,
height: 45,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Color(0xffFCD8DC),
),
child: Center(
child: Text(
'S',
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
),
),
const SizedBox(
height: 10,
),
const Text(
"Your sender's profile ",
style: TextStyle(color: Colors.white),
)
],
),
child: Container(
margin: const EdgeInsets.all(10),
child: Container(
width: 45,
height: 45,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Color(0xffFCD8DC),
),
child: Center(
child: Text(
'S',
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold,
fontSize: 16,
),
const SizedBox(
height: 10,
),
),
const Text(
"Your sender's profile ",
style: TextStyle(color: Colors.white),
)
],
),
),
),
child: const SAvatarExampleChild(),
)
else
const SAvatarExampleChild(),
const Padding(padding: EdgeInsets.only(left: 8)),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
Expand Down Expand Up @@ -438,6 +438,37 @@ class _MailPageState extends State<MailPage> {
}
}

class SAvatarExampleChild extends StatelessWidget {
const SAvatarExampleChild({
Key? key,
}) : super(key: key);

@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(10),
child: Container(
width: 45,
height: 45,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Color(0xffFCD8DC),
),
child: Center(
child: Text(
'S',
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
),
);
}
}

class Mail {
Mail({
required this.sender,
Expand Down
85 changes: 57 additions & 28 deletions lib/src/showcase.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class Showcase extends StatefulWidget {
final Widget? container;
final Color showcaseBackgroundColor;
final Color textColor;
final Widget scrollLoadingWidget;
final bool showArrow;
final double? height;
final double? width;
Expand Down Expand Up @@ -79,6 +80,8 @@ class Showcase extends StatefulWidget {
this.descTextStyle,
this.showcaseBackgroundColor = Colors.white,
this.textColor = Colors.black,
this.scrollLoadingWidget = const CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Colors.white)),
this.showArrow = true,
this.onTargetClick,
this.disposeOnTap,
Expand Down Expand Up @@ -122,6 +125,8 @@ class Showcase extends StatefulWidget {
this.descTextStyle,
this.showcaseBackgroundColor = Colors.white,
this.textColor = Colors.black,
this.scrollLoadingWidget = const CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Colors.white)),
this.onTargetClick,
this.disposeOnTap,
this.animationDuration = const Duration(milliseconds: 2000),
Expand All @@ -140,6 +145,7 @@ class Showcase extends StatefulWidget {

class _ShowcaseState extends State<Showcase> {
bool _showShowCase = false;
bool _isScrollRunning = false;
Timer? timer;
GetPosition? position;

Expand All @@ -164,6 +170,7 @@ class _ShowcaseState extends State<Showcase> {
});

if (activeStep == widget.key) {
_scrollIntoView();
if (ShowCaseWidget.of(context)!.autoPlay) {
timer = Timer(
Duration(
Expand All @@ -173,6 +180,22 @@ class _ShowcaseState extends State<Showcase> {
}
}

void _scrollIntoView() {
WidgetsBinding.instance!.addPostFrameCallback((timeStamp) async {
setState(() {
_isScrollRunning = true;
});
await Scrollable.ensureVisible(
widget.key.currentContext!,
duration: ShowCaseWidget.of(context)!.widget.scrollDuration,
alignment: 0.5,
);
setState(() {
_isScrollRunning = false;
});
});
}

@override
Widget build(BuildContext context) {
return AnchoredOverlay(
Expand Down Expand Up @@ -241,10 +264,13 @@ class _ShowcaseState extends State<Showcase> {
onTap: _nextIfAny,
child: ClipPath(
clipper: RRectClipper(
area: rectBound,
area: _isScrollRunning ? Rect.zero : rectBound,
isCircle: widget.shapeBorder == CircleBorder(),
radius: widget.radius,
overlayPadding: widget.overlayPadding,
radius:
_isScrollRunning ? BorderRadius.zero : widget.radius,
overlayPadding: _isScrollRunning
? EdgeInsets.zero
: widget.overlayPadding,
),
child: blur != 0
? BackdropFilter(
Expand All @@ -268,31 +294,34 @@ class _ShowcaseState extends State<Showcase> {
),
),
),
_TargetWidget(
offset: offset,
size: size,
onTap: _getOnTargetTap,
shapeBorder: widget.shapeBorder,
),
ToolTipWidget(
position: position,
offset: offset,
screenSize: screenSize,
title: widget.title,
description: widget.description,
titleTextStyle: widget.titleTextStyle,
descTextStyle: widget.descTextStyle,
container: widget.container,
tooltipColor: widget.showcaseBackgroundColor,
textColor: widget.textColor,
showArrow: widget.showArrow,
contentHeight: widget.height,
contentWidth: widget.width,
onTooltipTap: _getOnTooltipTap,
contentPadding: widget.contentPadding,
disableAnimation: widget.disableAnimation,
animationDuration: widget.animationDuration,
),
if (_isScrollRunning) Center(child: widget.scrollLoadingWidget),
if (!_isScrollRunning)
_TargetWidget(
offset: offset,
size: size,
onTap: _getOnTargetTap,
shapeBorder: widget.shapeBorder,
),
if (!_isScrollRunning)
ToolTipWidget(
position: position,
offset: offset,
screenSize: screenSize,
title: widget.title,
description: widget.description,
titleTextStyle: widget.titleTextStyle,
descTextStyle: widget.descTextStyle,
container: widget.container,
tooltipColor: widget.showcaseBackgroundColor,
textColor: widget.textColor,
showArrow: widget.showArrow,
contentHeight: widget.height,
contentWidth: widget.width,
onTooltipTap: _getOnTooltipTap,
contentPadding: widget.contentPadding,
disableAnimation: widget.disableAnimation,
animationDuration: widget.animationDuration,
),
],
)
: SizedBox.shrink();
Expand Down
2 changes: 2 additions & 0 deletions lib/src/showcase_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class ShowCaseWidget extends StatefulWidget {
final bool autoPlay;
final Duration autoPlayDelay;
final bool autoPlayLockEnable;
final Duration scrollDuration;

/// Default overlay blur used by showcase. if [Showcase.blurValue]
/// is not provided.
Expand All @@ -48,6 +49,7 @@ class ShowCaseWidget extends StatefulWidget {
this.autoPlayDelay = const Duration(milliseconds: 2000),
this.autoPlayLockEnable = false,
this.blurValue = 0,
this.scrollDuration = const Duration(milliseconds: 300),
});

static GlobalKey? activeTargetWidget(BuildContext context) {
Expand Down

0 comments on commit c680229

Please sign in to comment.