Skip to content

Commit bf632b7

Browse files
committed
feat: ✨ Global floating action widget
related: feat: ✨Add staticContainer into ToolTipWidget for a custom tooltip that can be placed anywhere on screen and is not affected by the animation SimformSolutionsPvtLtd#396 feat: ✨Add staticContainer into ToolTipWidget for a custom tooltip that can be placed anywhere on screen and is not affected by the animation MichaelCadavillo@c1eb606
1 parent 394d8d1 commit bf632b7

File tree

4 files changed

+133
-14
lines changed

4 files changed

+133
-14
lines changed

example/android/app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
2626
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
2727

2828
android {
29-
compileSdkVersion 33
29+
compileSdkVersion 34
3030

3131
sourceSets {
3232
main.java.srcDirs += 'src/main/kotlin'
@@ -38,7 +38,7 @@ android {
3838

3939
defaultConfig {
4040
applicationId "com.simform.example"
41-
minSdkVersion 16
41+
minSdkVersion flutter.minSdkVersion
4242
targetSdkVersion 33
4343
versionCode flutterVersionCode.toInteger()
4444
versionName flutterVersionName

example/lib/main.dart

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ import 'package:flutter/material.dart';
66
import 'package:flutter/services.dart';
77
import 'package:showcaseview/showcaseview.dart';
88

9+
/// Global key for the first showcase widget
10+
final GlobalKey _firstShowcaseWidget = GlobalKey();
11+
12+
/// Global key for the last showcase widget
13+
final GlobalKey _lastShowcaseWidget = GlobalKey();
14+
915
void main() => runApp(const MyApp());
1016

1117
class MyApp extends StatelessWidget {
@@ -21,6 +27,27 @@ class MyApp extends StatelessWidget {
2127
debugShowCheckedModeBanner: false,
2228
home: Scaffold(
2329
body: ShowCaseWidget(
30+
hideFloatingActionWidgetForShowcase: [_lastShowcaseWidget],
31+
globalFloatingActionWidget: (showcaseContext) => Positioned(
32+
left: 16,
33+
bottom: 16,
34+
child: Padding(
35+
padding: const EdgeInsets.all(16.0),
36+
child: ElevatedButton(
37+
onPressed: ShowCaseWidget.of(showcaseContext).dismiss,
38+
style: ElevatedButton.styleFrom(
39+
backgroundColor: const Color(0xffEE5366),
40+
),
41+
child: const Text(
42+
'Skip',
43+
style: TextStyle(
44+
color: Colors.white,
45+
fontSize: 15,
46+
),
47+
),
48+
),
49+
),
50+
),
2451
onStart: (index, key) {
2552
log('onStart: $index, $key');
2653
},
@@ -53,11 +80,9 @@ class MailPage extends StatefulWidget {
5380

5481
class _MailPageState extends State<MailPage> {
5582
final GlobalKey _searchKey = GlobalKey();
56-
final GlobalKey _one = GlobalKey();
5783
final GlobalKey _two = GlobalKey();
5884
final GlobalKey _three = GlobalKey();
5985
final GlobalKey _four = GlobalKey();
60-
final GlobalKey _five = GlobalKey();
6186
List<Mail> mails = [];
6287

6388
final scrollController = ScrollController();
@@ -69,7 +94,9 @@ class _MailPageState extends State<MailPage> {
6994
//NOTE: remove ambiguate function if you are using
7095
//flutter version greater than 3.x and direct use WidgetsBinding.instance
7196
ambiguate(WidgetsBinding.instance)?.addPostFrameCallback(
72-
(_) => ShowCaseWidget.of(context).startShowCase([_one, _two, _three, _four, _five]),
97+
(_) => ShowCaseWidget.of(context).startShowCase(
98+
[_firstShowcaseWidget, _two, _three, _four, _lastShowcaseWidget],
99+
),
73100
);
74101
mails = [
75102
Mail(
@@ -180,15 +207,25 @@ class _MailPageState extends State<MailPage> {
180207
child: Row(
181208
children: <Widget>[
182209
Showcase(
183-
key: _one,
210+
key: _firstShowcaseWidget,
184211
description: 'Tap ≡ to see menu options',
185212
disableDefaultTargetGestures: true,
186213
visibleBoundReference: _searchKey,
187214
arrowVerticalMargin: EdgeInsets.only(
188215
top: 22 * MediaQuery.textScaleFactorOf(context),
189216
bottom: 27,
190217
),
191-
onBarrierClick: () => debugPrint('Barrier clicked'),
218+
onBarrierClick: () {
219+
debugPrint('Barrier clicked');
220+
debugPrint(
221+
'Floating Action widget for first '
222+
'showcase is now hidden',
223+
);
224+
ShowCaseWidget.of(context).hideFloatingActionWidgetForKeys([
225+
_firstShowcaseWidget,
226+
_lastShowcaseWidget
227+
]);
228+
},
192229
child: GestureDetector(
193230
onTap: () => debugPrint('menu button clicked'),
194231
child: Icon(
@@ -229,6 +266,26 @@ class _MailPageState extends State<MailPage> {
229266
"Tap to see profile which contains user's name, profile picture, mobile number and country",
230267
tooltipBackgroundColor: Theme.of(context).primaryColor,
231268
textColor: Colors.white,
269+
floatingActionWidget: Positioned(
270+
left: 16,
271+
bottom: 16,
272+
child: Padding(
273+
padding: const EdgeInsets.all(16.0),
274+
child: ElevatedButton(
275+
style: ElevatedButton.styleFrom(
276+
backgroundColor: const Color(0xffEE5366),
277+
),
278+
onPressed: ShowCaseWidget.of(context).dismiss,
279+
child: const Text(
280+
'Close Showcase',
281+
style: TextStyle(
282+
color: Colors.white,
283+
fontSize: 15,
284+
),
285+
),
286+
),
287+
),
288+
),
232289
targetShapeBorder: const CircleBorder(),
233290
child: Container(
234291
padding: const EdgeInsets.all(5),
@@ -281,7 +338,7 @@ class _MailPageState extends State<MailPage> {
281338
),
282339
),
283340
floatingActionButton: Showcase(
284-
key: _five,
341+
key: _lastShowcaseWidget,
285342
title: 'Compose Mail',
286343
description: 'Click here to compose mail',
287344
targetShapeBorder: const CircleBorder(),
@@ -295,7 +352,13 @@ class _MailPageState extends State<MailPage> {
295352
* currently rendered so the showcased keys are available in the
296353
* render tree. */
297354
scrollController.jumpTo(0);
298-
ShowCaseWidget.of(context).startShowCase([_one, _two, _three, _four, _five]);
355+
ShowCaseWidget.of(context).startShowCase([
356+
_firstShowcaseWidget,
357+
_two,
358+
_three,
359+
_four,
360+
_lastShowcaseWidget,
361+
]);
299362
});
300363
},
301364
child: const Icon(
@@ -332,7 +395,7 @@ class _MailPageState extends State<MailPage> {
332395
),
333396
).then((_) {
334397
setState(() {
335-
ShowCaseWidget.of(context).startShowCase([_four, _five]);
398+
ShowCaseWidget.of(context).startShowCase([_four, _lastShowcaseWidget]);
336399
});
337400
});
338401
},

lib/src/showcase.dart

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ class Showcase extends StatefulWidget {
106106
/// Typically a [Positioned] widget.
107107
///
108108
/// It can be a button to dismiss the showcase.
109-
final Widget? action;
109+
final Widget? floatingActionWidget;
110110

111111
/// Defines background color for tooltip widget.
112112
///
@@ -286,7 +286,7 @@ class Showcase extends StatefulWidget {
286286
this.showArrow = true,
287287
EdgeInsets? arrowVerticalMargin,
288288
this.arrowPainterBuilder,
289-
this.action,
289+
this.floatingActionWidget,
290290
this.onTargetClick,
291291
this.disposeOnTap,
292292
this.movingAnimationDuration = const Duration(milliseconds: 2000),
@@ -330,7 +330,7 @@ class Showcase extends StatefulWidget {
330330
required this.height,
331331
required this.width,
332332
required this.container,
333-
this.action,
333+
this.floatingActionWidget,
334334
this.alignedFromParent = const EdgeInsets.only(
335335
left: 16,
336336
right: 8,
@@ -569,6 +569,9 @@ class _ShowcaseState extends State<Showcase> with WidgetsBindingObserver {
569569

570570
if (!_showShowCase) return const Offstage();
571571

572+
final Widget? floatingActionWidget = widget.floatingActionWidget
573+
?? showCaseWidgetState.globalFloatingActionWidget?.call(context);
574+
572575
return Stack(
573576
children: [
574577
GestureDetector(
@@ -653,7 +656,7 @@ class _ShowcaseState extends State<Showcase> with WidgetsBindingObserver {
653656
titleTextDirection: widget.titleTextDirection,
654657
descriptionTextDirection: widget.descriptionTextDirection,
655658
),
656-
if (widget.action != null) widget.action!,
659+
if (floatingActionWidget != null) floatingActionWidget,
657660
],
658661
],
659662
);

lib/src/showcase_widget.dart

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ import 'package:flutter/material.dart';
2525
import '../showcaseview.dart';
2626
import 'extension.dart';
2727

28+
typedef FloatingActionBuilderCallback = Widget Function(
29+
BuildContext,
30+
);
31+
2832
class ShowCaseWidget extends StatefulWidget {
2933
final Builder builder;
3034

@@ -84,6 +88,17 @@ class ShowCaseWidget extends StatefulWidget {
8488
/// Enable/disable showcase globally. Enabled by default.
8589
final bool enableShowcase;
8690

91+
/// Custom static floating action widget to show a static widget anywhere
92+
/// on the screen for all the showcase widget
93+
/// Use this context to access showcaseWidget operation otherwise it will
94+
/// throw error.
95+
final FloatingActionBuilderCallback? globalFloatingActionWidget;
96+
97+
/// Hides [globalFloatingActionWidget] for the provided showcase widgets. Add key of
98+
/// showcase in which [globalFloatingActionWidget] should be hidden this list.
99+
/// Defaults to [].
100+
final List<GlobalKey> hideFloatingActionWidgetForShowcase;
101+
87102
const ShowCaseWidget({
88103
required this.builder,
89104
this.onFinish,
@@ -99,6 +114,8 @@ class ShowCaseWidget extends StatefulWidget {
99114
this.enableAutoScroll = false,
100115
this.disableBarrierInteraction = false,
101116
this.enableShowcase = true,
117+
this.globalFloatingActionWidget,
118+
this.hideFloatingActionWidgetForShowcase = const [],
102119
});
103120

104121
static GlobalKey? activeTargetWidget(BuildContext context) {
@@ -146,9 +163,36 @@ class ShowCaseWidgetState extends State<ShowCaseWidget> {
146163

147164
bool get enableShowcase => widget.enableShowcase;
148165

166+
List<GlobalKey> get hiddenFloatingActionKeys =>
167+
_hideFloatingWidgetKeys.keys.toList();
168+
169+
/// This Stores keys of showcase for which we will hide the
170+
/// [globalFloatingActionWidget].
171+
late final _hideFloatingWidgetKeys = {
172+
for (final item in widget.hideFloatingActionWidgetForShowcase) item: true
173+
};
174+
149175
/// Returns value of [ShowCaseWidget.blurValue]
150176
double get blurValue => widget.blurValue;
151177

178+
/// Returns current active showcase key
179+
GlobalKey? get getCurrentActiveShowcaseKey {
180+
if (ids == null || activeWidgetId == null) return null;
181+
182+
if (activeWidgetId! < ids!.length && activeWidgetId! >= 0) {
183+
return ids![activeWidgetId!];
184+
} else {
185+
return null;
186+
}
187+
}
188+
189+
/// Return a [widget.globalFloatingActionWidget] if not need to hide this for
190+
/// current showcase.
191+
FloatingActionBuilderCallback? get globalFloatingActionWidget =>
192+
_hideFloatingWidgetKeys[getCurrentActiveShowcaseKey] ?? false
193+
? null
194+
: widget.globalFloatingActionWidget;
195+
152196
@override
153197
void initState() {
154198
super.initState();
@@ -273,6 +317,15 @@ class ShowCaseWidgetState extends State<ShowCaseWidget> {
273317
activeWidgetId = null;
274318
}
275319

320+
/// Disables the [globalFloatingActionWidget] for the provided keys.
321+
void hideFloatingActionWidgetForKeys(
322+
List<GlobalKey> updatedList,
323+
) {
324+
_hideFloatingWidgetKeys
325+
..clear()
326+
..addAll({for (final item in updatedList) item: true});
327+
}
328+
276329
@override
277330
Widget build(BuildContext context) {
278331
return _InheritedShowCaseView(

0 commit comments

Comments
 (0)