Skip to content

Commit 46c0dfc

Browse files
feat: ✨ Multiple showcase at the same time and improvement (#514)
* chore: 💥 Update min dart sdk to 2.19.6 * fix: 🩹 Fixed the page transition issue * fix: 🩹 Shifted everything to showcase controller
1 parent df39208 commit 46c0dfc

21 files changed

+1283
-727
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@
1111
Improved Tooltip widget
1212
- Feature [#54](https://github.com/SimformSolutionsPvtLtd/flutter_showcaseview/issues/54) - Added
1313
Feasibility to position tooltip left and right to the target widget.
14+
- Feature [#113](https://github.com/SimformSolutionsPvtLtd/flutter_showcaseview/issues/113) - Added
15+
multiple showcase feature
16+
- Improvement [#514](https://github.com/SimformSolutionsPvtLtd/flutter_showcaseview/pull/514) -
17+
Improved showcase widget and showcase with widget, Removed inherited widget, keys and setStates,
18+
Added controller to manage showcase
19+
- CHORE [#514](https://github.com/SimformSolutionsPvtLtd/flutter_showcaseview/pull/514) -
20+
Bumped dart minimum sdk to 2.19.6
1421

1522
## [4.0.1]
1623
- Fixed [#493](https://github.com/SimformSolutionsPvtLtd/flutter_showcaseview/issues/493) - ShowCase.withWidget not showing issue

README.md

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,21 +110,60 @@ Showcase.withWidget(
110110
),
111111
```
112112

113-
5. Starting the `ShowCase`
113+
5. Starting the `ShowCase`:
114114
```dart
115115
someEvent(){
116116
ShowCaseWidget.of(context).startShowCase([_one, _two, _three]);
117117
}
118118
```
119119

120-
If you want to start the `ShowCaseView` as soon as your UI built up then use below code.
120+
If you want to start the `ShowCaseView` as soon as your UI built up then use below code:
121121

122122
```dart
123123
WidgetsBinding.instance.addPostFrameCallback((_) =>
124124
ShowCaseWidget.of(context).startShowCase([_one, _two, _three])
125125
);
126126
```
127127

128+
If you have some animation or transition in your UI and you want to start the `ShowCaseView` after
129+
that then use below code:
130+
131+
```dart
132+
WidgetsBinding.instance.addPostFrameCallback((_) =>
133+
ShowCaseWidget.of(context).startShowCase([_one, _two, _three], delay: "Animation Duration")
134+
);
135+
```
136+
137+
## MultiShowcaseView
138+
To show multiple showcase at the same time provide same key to showcase.
139+
Note: auto scroll to showcase will not work in case of the multi-showcase and we will use property
140+
of first initialized showcase for common things like barrier tap and colors.
141+
142+
```dart
143+
GlobalKey _one = GlobalKey();
144+
...
145+
146+
Showcase(
147+
key: _one,
148+
title: 'Showcase one',
149+
description: 'Click here to see menu options',
150+
child: Icon(
151+
Icons.menu,
152+
color: Colors.black45,
153+
),
154+
),
155+
156+
Showcase(
157+
key: _one,
158+
title: 'Showcase two',
159+
description: 'Click here to see menu options',
160+
child: Icon(
161+
Icons.menu,
162+
color: Colors.black45,
163+
),
164+
),
165+
```
166+
128167
## Functions of `ShowCaseWidget.of(context)`:
129168

130169
| Function Name | Description |
@@ -257,7 +296,7 @@ So, If you want to make a scroll view that contains less number of children widg
257296

258297
If using SingleChildScrollView is not an option, then you can assign a ScrollController to that scrollview and manually scroll to the position where showcase widget gets rendered. You can add that code in onStart method of `ShowCaseWidget`.
259298

260-
Example,
299+
Example:
261300

262301
```dart
263302
// This controller will be assigned to respected sctollview.

example/lib/main.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -468,8 +468,12 @@ class _MailPageState extends State<MailPage> {
468468
),
469469
).then((_) {
470470
setState(() {
471-
ShowCaseWidget.of(context)
472-
.startShowCase([_four, _lastShowcaseWidget]);
471+
ShowCaseWidget.of(context).startShowCase(
472+
[_four, _lastShowcaseWidget],
473+
delay: const Duration(
474+
milliseconds: 200,
475+
),
476+
);
473477
});
474478
});
475479
},

lib/showcaseview.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export 'src/enum.dart';
2626
export 'src/models/action_button_icon.dart';
2727
export 'src/models/tooltip_action_button.dart';
2828
export 'src/models/tooltip_action_config.dart';
29-
export 'src/showcase.dart';
29+
export 'src/showcase/showcase.dart';
3030
export 'src/showcase_widget.dart';
3131
export 'src/tooltip_action_button_widget.dart';
3232
export 'src/widget/floating_action_widget.dart';

lib/src/constants.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import 'package:flutter/material.dart';
2+
3+
import 'widget/showcase_circular_progress_indicator.dart';
4+
15
class Constants {
26
Constants._();
37

@@ -25,4 +29,16 @@ class Constants {
2529
/// i.e if it is bottom position then centerBottom + [extraAlignmentOffset]
2630
/// in bottom
2731
static const double extraAlignmentOffset = 5;
32+
33+
static const Radius defaultTargetRadius = Radius.circular(3.0);
34+
35+
static const ShapeBorder defaultTargetShapeBorder = RoundedRectangleBorder(
36+
borderRadius: BorderRadius.all(Radius.circular(8)),
37+
);
38+
39+
static const double cupertinoActivityIndicatorRadius = 12.0;
40+
static const Widget defaultProgressIndicator =
41+
ShowcaseCircularProgressIndicator();
42+
43+
static const Duration defaultAnimationDuration = Duration(milliseconds: 2000);
2844
}

lib/src/enum.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ enum TooltipDefaultActionType {
9494
void onTap(ShowCaseWidgetState showCaseState) {
9595
switch (this) {
9696
case TooltipDefaultActionType.next:
97-
showCaseState.next();
97+
showCaseState.next(force: true);
9898
break;
9999
case TooltipDefaultActionType.previous:
100100
showCaseState.previous();

lib/src/get_position.dart

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,40 +26,34 @@ import 'package:flutter/material.dart';
2626

2727
class GetPosition {
2828
GetPosition({
29-
required this.key,
29+
required this.renderBox,
3030
required this.screenWidth,
3131
required this.screenHeight,
3232
this.padding = EdgeInsets.zero,
3333
this.rootRenderObject,
3434
}) {
35-
getRenderBox();
35+
_getRenderBoxOffset();
3636
}
3737

38-
final GlobalKey key;
38+
final RenderBox? renderBox;
3939
final EdgeInsets padding;
4040
final double screenWidth;
4141
final double screenHeight;
4242
final RenderObject? rootRenderObject;
4343

44-
late final RenderBox? _box;
45-
late final Offset? _boxOffset;
46-
47-
RenderBox? get box => _box;
48-
49-
void getRenderBox() {
50-
var renderBox = key.currentContext?.findRenderObject() as RenderBox?;
44+
Offset? _boxOffset;
5145

46+
void _getRenderBoxOffset() {
5247
if (renderBox == null) return;
5348

54-
_box = renderBox;
55-
_boxOffset = _box?.localToGlobal(
49+
_boxOffset = renderBox?.localToGlobal(
5650
Offset.zero,
5751
ancestor: rootRenderObject,
5852
);
5953
}
6054

6155
bool _checkBoxOrOffsetIsNull({bool checkDy = false, bool checkDx = false}) {
62-
return _box == null ||
56+
return renderBox == null ||
6357
_boxOffset == null ||
6458
(checkDx && (_boxOffset?.dx.isNaN ?? true)) ||
6559
(checkDy && (_boxOffset?.dy.isNaN ?? true));
@@ -69,13 +63,13 @@ class GetPosition {
6963
if (_checkBoxOrOffsetIsNull(checkDy: true, checkDx: true)) {
7064
return Rect.zero;
7165
}
72-
final topLeft = _box!.size.topLeft(_boxOffset!);
73-
final bottomRight = _box!.size.bottomRight(_boxOffset!);
66+
final topLeft = renderBox!.size.topLeft(_boxOffset!);
67+
final bottomRight = renderBox!.size.bottomRight(_boxOffset!);
7468
final leftDx = topLeft.dx - padding.left;
7569
final leftDy = topLeft.dy - padding.top;
7670
final rect = Rect.fromLTRB(
77-
leftDx.clamp(0, leftDx),
78-
leftDy.clamp(0, leftDy),
71+
leftDx.clamp(0, double.maxFinite),
72+
leftDy.clamp(0, double.maxFinite),
7973
min(bottomRight.dx + padding.right, screenWidth),
8074
min(bottomRight.dy + padding.bottom, screenHeight),
8175
);
@@ -87,7 +81,7 @@ class GetPosition {
8781
if (_checkBoxOrOffsetIsNull(checkDy: true)) {
8882
return padding.bottom;
8983
}
90-
final bottomRight = _box!.size.bottomRight(_boxOffset!);
84+
final bottomRight = renderBox!.size.bottomRight(_boxOffset!);
9185
return bottomRight.dy + padding.bottom;
9286
}
9387

@@ -96,7 +90,7 @@ class GetPosition {
9690
if (_checkBoxOrOffsetIsNull(checkDy: true)) {
9791
return -padding.top;
9892
}
99-
final topLeft = _box!.size.topLeft(_boxOffset!);
93+
final topLeft = renderBox!.size.topLeft(_boxOffset!);
10094
return topLeft.dy - padding.top;
10195
}
10296

@@ -105,7 +99,7 @@ class GetPosition {
10599
if (_checkBoxOrOffsetIsNull(checkDx: true)) {
106100
return -padding.left;
107101
}
108-
final topLeft = _box!.size.topLeft(_boxOffset!);
102+
final topLeft = renderBox!.size.topLeft(_boxOffset!);
109103
return topLeft.dx - padding.left;
110104
}
111105

@@ -114,7 +108,7 @@ class GetPosition {
114108
if (_checkBoxOrOffsetIsNull(checkDx: true)) {
115109
return padding.right;
116110
}
117-
final bottomRight = _box!.size.bottomRight(_boxOffset!);
111+
final bottomRight = renderBox!.size.bottomRight(_boxOffset!);
118112
return bottomRight.dx + padding.right;
119113
}
120114

@@ -123,4 +117,18 @@ class GetPosition {
123117
double getWidth() => getRight() - getLeft();
124118

125119
double getCenter() => (getLeft() + getRight()) * 0.5;
120+
121+
Offset topLeft() {
122+
final box = renderBox;
123+
if (box == null) return Offset.zero;
124+
125+
return box.size.topLeft(
126+
box.localToGlobal(
127+
Offset.zero,
128+
ancestor: rootRenderObject,
129+
),
130+
);
131+
}
132+
133+
Offset getOffset() => renderBox?.size.center(topLeft()) ?? Offset.zero;
126134
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import 'package:flutter/widgets.dart';
2+
3+
/// This model is used to move linked showcase overlay data to parent
4+
/// showcase to crop linked showcase rect
5+
class LinkedShowcaseDataModel {
6+
const LinkedShowcaseDataModel({
7+
required this.rect,
8+
required this.radius,
9+
required this.overlayPadding,
10+
required this.isCircle,
11+
});
12+
13+
final Rect rect;
14+
final EdgeInsets overlayPadding;
15+
final BorderRadius? radius;
16+
final bool isCircle;
17+
18+
@override
19+
bool operator ==(Object other) =>
20+
identical(this, other) ||
21+
other is LinkedShowcaseDataModel &&
22+
runtimeType == other.runtimeType &&
23+
rect == other.rect &&
24+
radius == other.radius &&
25+
overlayPadding == other.overlayPadding &&
26+
isCircle == other.isCircle;
27+
28+
@override
29+
int get hashCode => Object.hash(
30+
rect,
31+
radius,
32+
overlayPadding,
33+
isCircle,
34+
);
35+
}

0 commit comments

Comments
 (0)