diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d43509c..27ba5be2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [0.1.3] - Added feature + +# Updated syntax to pass new context to each ShowCaseWidget +# Added onTargetTap callback +# Added onSHowCaseFinishCallback + +## [0.1.3] - Added feature [#10](https://github.com/simformsolutions/flutter_showcaseview/issues/10). + ## [0.1.2] - Fixed issue [#6](https://github.com/simformsolutions/flutter_showcaseview/issues/6). ## [0.1.1] - Fixed maintenance issues. diff --git a/README.md b/README.md index ac7ed67a..9438f101 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ It is inspired from [Fluttery's Flutter challange](https://www.youtube.com/watch ```dart dependencies: - showcaseview: ^0.1.2 + showcaseview: ^0.1.3 ``` 2. Import the package @@ -27,7 +27,9 @@ import 'package:showcaseview/showcaseview.dart'; 3. Adding a `ShowCaseWidget` widget. ```dart ShowCaseWidget( - child: Somewidget(), + builder: Builder( + builder : (context) ()=> Somewidget() + ), ), ``` @@ -98,7 +100,7 @@ WidgetsBinding.instance.addPostFrameCallback((_) => ## How to use -Check out the **example** app in the [example](example) directory or the 'Example' tab on pub.dartlang.org for a more complete example. +Check out the **example** app in the [example](example) directory or the 'Example' tab on pub.dev for a more complete example. ## Getting Started diff --git a/example/ios/Podfile b/example/ios/Podfile new file mode 100644 index 00000000..f8e42b25 --- /dev/null +++ b/example/ios/Podfile @@ -0,0 +1,10 @@ +# Uncomment the next line to define a global platform for your project +# platform :ios, '9.0' + +target 'Runner' do + # Comment the next line if you're not using Swift and don't want to use dynamic frameworks + use_frameworks! + + # Pods for Runner + +end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock new file mode 100644 index 00000000..6b6f7201 --- /dev/null +++ b/example/ios/Podfile.lock @@ -0,0 +1,3 @@ +PODFILE CHECKSUM: d9f7b2970df4f27dd65791d86121189a9b2b9b40 + +COCOAPODS: 1.6.1 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 41224498..d8236159 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 28828A1230B8B6B026967569 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7ECC35C82670F1890A6607D2 /* Pods_Runner.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -38,11 +39,15 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 15559D9BE4294900849B9525 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 196DC4AB8013491EC5C95290 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 20F35DD64B1C698657581EA3 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7ECC35C82670F1890A6607D2 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; @@ -60,12 +65,21 @@ files = ( 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, + 28828A1230B8B6B026967569 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 023D98649FEA86492AEFE603 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 7ECC35C82670F1890A6607D2 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -85,6 +99,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, + B9A8FC7416F05110E43FECF4 /* Pods */, + 023D98649FEA86492AEFE603 /* Frameworks */, ); sourceTree = ""; }; @@ -119,6 +135,17 @@ name = "Supporting Files"; sourceTree = ""; }; + B9A8FC7416F05110E43FECF4 /* Pods */ = { + isa = PBXGroup; + children = ( + 196DC4AB8013491EC5C95290 /* Pods-Runner.debug.xcconfig */, + 15559D9BE4294900849B9525 /* Pods-Runner.release.xcconfig */, + 20F35DD64B1C698657581EA3 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -126,6 +153,7 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 9C20D77F8B4680DEBDF93D35 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, @@ -219,6 +247,28 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; + 9C20D77F8B4680DEBDF93D35 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -509,7 +559,6 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ - }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a16..21a3cc14 100644 --- a/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/example/lib/SecondPage.dart b/example/lib/SecondPage.dart new file mode 100644 index 00000000..6afef8d6 --- /dev/null +++ b/example/lib/SecondPage.dart @@ -0,0 +1,102 @@ +import 'package:flutter/material.dart'; +import 'package:showcaseview/showcase_widget.dart'; +import 'package:showcaseview/showcaseview.dart'; + +class Detail extends StatefulWidget { + @override + _DetailState createState() => _DetailState(); +} + +class _DetailState extends State { + final GlobalKey _one = GlobalKey(); + BuildContext myContext; + + @override + void initState() { + // TODO: implement initState + super.initState(); + WidgetsBinding.instance.addPostFrameCallback( + (_) { + Future.delayed(Duration(milliseconds: 200), () => + ShowCaseWidget.of(myContext).startShowCase([_one]) + ); + } + ); + } + + @override + Widget build(BuildContext context) { + return ShowCaseWidget( + builder: Builder( + builder: (context) { + myContext = context; + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + leading: IconButton( + icon: Icon( + Icons.arrow_back, + color: Colors.black, + ), + onPressed: () { + Navigator.pop(context); + }, + ), + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: ListView( + children: [ + Showcase( + key: _one, + title: 'Title', + description: 'Desc', + child: InkWell( + onTap: () {}, + child: Text( + 'Flutter Notification', + style: TextStyle( + fontSize: 25, fontWeight: FontWeight.w600), + ), + ), + ), + SizedBox( + height: 16, + ), + Text( + 'Hi, you have new Notification from flutter group, open slack and check it out', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500), + ), + SizedBox( + height: 16, + ), + RichText( + text: TextSpan( + style: TextStyle( + fontWeight: FontWeight.w400, color: Colors.black), + children: [ + TextSpan(text: 'Hi team,\n\n'), + TextSpan( + text: + 'As some of you know, we’re moving to Slack for our internal team communications. Slack is a messaging app where we can talk, share files, and work together. It also connects with tools we already use, like [add your examples here], plus 900+ other apps.\n\n'), + TextSpan( + text: 'Why are we moving to Slack?\n\n', + style: TextStyle( + fontWeight: FontWeight.w600, + color: Colors.black)), + TextSpan( + text: + 'We want to use the best communication tools to make our lives easier and be more productive. Having everything in one place will help us work together better and faster, rather than jumping around between emails, IMs, texts and a bunch of other programs. Everything you share in Slack is automatically indexed and archived, creating a searchable archive of all our work.'), + ], + ), + ), + ], + ), + ), + ); + }, + ), + ); + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart index 4fff7ea4..476aaec4 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:showcaseview/showcaseview.dart'; +import 'SecondPage.dart'; + void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @@ -11,7 +13,13 @@ class MyApp extends StatelessWidget { theme: ThemeData( primarySwatch: Colors.blue, ), - home: ShowCaseWidget(child: MailPage()), + home: Scaffold( + body: ShowCaseWidget( + builder: Builder( + builder: (context) => MailPage() + ), + ), + ), ); } } @@ -28,14 +36,31 @@ class _MailPageState extends State { GlobalKey _four = GlobalKey(); GlobalKey _five = GlobalKey(); + final scaffoldKey = GlobalKey(); + + _showSnackbar(String msg) { + final snackbar = SnackBar( + content: Text(msg), + backgroundColor: Colors.blue, + ); + scaffoldKey.currentState.showSnackBar(snackbar); + } + @override - Widget build(BuildContext context) { + void initState() { + super.initState(); //Start showcase view after current widget frames are drawn. WidgetsBinding.instance.addPostFrameCallback((_) => - ShowCaseWidget.startShowCase( - context, [_one, _two, _three, _four, _five])); + ShowCaseWidget.of(context).startShowCase([_one, _two, _three, _four, _five])); + ShowCaseWidget.setOnShowCaseFinish(() { + _showSnackbar('ShowcaseView Finished'); + }); + } + @override + Widget build(BuildContext context) { return Scaffold( + key: scaffoldKey, body: SafeArea( child: ListView( children: [ @@ -118,29 +143,73 @@ class _MailPageState extends State { ], ), Padding(padding: EdgeInsets.only(top: 8)), - Container( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Showcase( - key: _three, - description: 'Tap to check mail', - child: Container( - padding: const EdgeInsets.only(left: 6, right: 16), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Showcase.withWidget( - key: _four, - height: 50, - width: 140, - shapeBorder: CircleBorder(), - container: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => Detail(), + ), + ); + }, + child: Container( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Showcase( + key: _three, + description: 'Tap to check mail', + disposeOnTap: true, + onTargetClick: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => Detail(), + ), + ).then((_) { + setState(() { + ShowCaseWidget.of(context).startShowCase([_four, _five]); + }); + }); + }, + child: Container( + padding: const EdgeInsets.only(left: 6, right: 16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Showcase.withWidget( + key: _four, + height: 50, + width: 140, + shapeBorder: CircleBorder(), + container: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 45, + height: 45, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.blue[200], + ), + child: Center( + child: Text('S'), + ), + ), + SizedBox( + height: 10, + ), + Text( + 'Your sender\'s profile ', + style: TextStyle(color: Colors.white), + ) + ], + ), + child: Container( + margin: const EdgeInsets.all(10), + child: Container( width: 45, height: 45, decoration: BoxDecoration( @@ -151,75 +220,54 @@ class _MailPageState extends State { child: Text('S'), ), ), - SizedBox( - height: 10, + ), + ), + Padding(padding: EdgeInsets.only(left: 8)), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Slack', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 17, + ), ), Text( - 'Your sender\'s profile ', - style: TextStyle(color: Colors.white), - ) - ], - ), - child: Container( - margin: const EdgeInsets.all(10), - child: Container( - width: 45, - height: 45, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Colors.blue[200], + 'Flutter Notification', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + ), ), - child: Center( - child: Text('S'), + Text( + 'Hi, you have new Notification', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 15, + ), ), - ), + ], + ) + ], + ), + ), + Column( + children: [ + Text( + '1 Jun', + style: TextStyle( + fontWeight: FontWeight.bold, ), ), - Padding(padding: EdgeInsets.only(left: 8)), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Slack', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 17, - ), - ), - Text( - 'Flutter Notification', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - Text( - 'Hi, you have new Notification', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 15, - ), - ), - ], + Icon( + Icons.star_border, + color: Colors.grey, ) ], - ), - ), - Column( - children: [ - Text( - '1 Jun', - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ), - Icon( - Icons.star_border, - color: Colors.grey, - ) - ], - ) - ], + ) + ], + ), ), ), ), @@ -278,7 +326,9 @@ class _MailPageState extends State { child: FloatingActionButton( backgroundColor: Colors.white, onPressed: () { - setState(() {}); + setState(() { + ShowCaseWidget.of(context).startShowCase([_one, _two, _three, _four, _five]); + }); }, child: Icon( Icons.add, diff --git a/example/pubspec.lock b/example/pubspec.lock index 872a2131..350d4890 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -87,7 +87,7 @@ packages: path: ".." relative: true source: path - version: "0.1.0" + version: "0.1.4" sky_engine: dependency: transitive description: flutter diff --git a/lib/showcase.dart b/lib/showcase.dart index 21fb1715..08b8081b 100644 --- a/lib/showcase.dart +++ b/lib/showcase.dart @@ -23,26 +23,42 @@ class Showcase extends StatefulWidget { final double height; final double width; final Duration animationDuration; + final VoidCallback onToolTipClick; + final VoidCallback onTargetClick; + final bool disposeOnTap; - const Showcase({ - @required this.key, - @required this.child, - this.title, - @required this.description, - this.shapeBorder, - this.overlayColor = Colors.black, - this.overlayOpacity = 0.75, - this.titleTextStyle, - this.descTextStyle, - this.showcaseBackgroundColor = Colors.white, - this.textColor = Colors.black, - this.showArrow = true, - this.animationDuration = const Duration(milliseconds: 2000), - }) : height = null, + const Showcase( + {@required this.key, + @required this.child, + this.title, + @required this.description, + this.shapeBorder, + this.overlayColor = Colors.black, + this.overlayOpacity = 0.75, + this.titleTextStyle, + this.descTextStyle, + this.showcaseBackgroundColor = Colors.white, + this.textColor = Colors.black, + this.showArrow = true, + this.onTargetClick, + this.disposeOnTap, + this.animationDuration = const Duration(milliseconds: 2000)}) + : height = null, width = null, container = null, + this.onToolTipClick = null, assert(overlayOpacity >= 0.0 && overlayOpacity <= 1.0, "overlay opacity should be >= 0.0 and <= 1.0."), + assert( + onTargetClick == null + ? true + : (disposeOnTap == null ? false : true), + "disposeOnTap is required if you're using onTargetClick"), + assert( + disposeOnTap == null + ? true + : (onTargetClick == null ? false : true), + "onTargetClick is required if you're using disposeOnTap"), assert(key != null || child != null || title != null || @@ -57,23 +73,26 @@ class Showcase extends StatefulWidget { shapeBorder != null || animationDuration != null); - const Showcase.withWidget({ - this.key, - @required this.child, - @required this.container, - @required this.height, - @required this.width, - this.title, - this.description, - this.shapeBorder, - this.overlayColor = Colors.black, - this.overlayOpacity = 0.75, - this.titleTextStyle, - this.descTextStyle, - this.showcaseBackgroundColor = Colors.white, - this.textColor = Colors.black, - this.animationDuration = const Duration(milliseconds: 2000), - }) : this.showArrow = false, + const Showcase.withWidget( + {this.key, + @required this.child, + @required this.container, + @required this.height, + @required this.width, + this.title, + this.description, + this.shapeBorder, + this.overlayColor = Colors.black, + this.overlayOpacity = 0.75, + this.titleTextStyle, + this.descTextStyle, + this.showcaseBackgroundColor = Colors.white, + this.textColor = Colors.black, + this.onTargetClick, + this.disposeOnTap, + this.animationDuration = const Duration(milliseconds: 2000)}) + : this.showArrow = false, + this.onToolTipClick = null, assert(overlayOpacity >= 0.0 && overlayOpacity <= 1.0, "overlay opacity should be >= 0.0 and <= 1.0."), assert(key != null || @@ -162,10 +181,40 @@ class _ShowcaseState extends State with TickerProviderStateMixin { } _nextIfAny() { - ShowCaseWidget.completed(context, widget.key); + ShowCaseWidget.of(context).completed(widget.key); _slideAnimationController.forward(); } + _getOnTargetTap() { + if (widget.disposeOnTap == true) { + return widget.onTargetClick == null + ? () { + ShowCaseWidget.of(context).dismiss(); + } + : () { + ShowCaseWidget.of(context).dismiss(); + widget.onTargetClick(); + }; + } else { + return widget.onTargetClick ?? _nextIfAny; + } + } + + _getOnTooltipTap() { + if (widget.disposeOnTap == true) { + return widget.onToolTipClick == null + ? () { + ShowCaseWidget.of(context).dismiss(); + } + : () { + ShowCaseWidget.of(context).dismiss(); + widget.onToolTipClick(); + }; + } else { + return widget.onToolTipClick ?? () {}; + } + } + buildOverlayOnTarget( Offset offset, Size size, @@ -195,7 +244,7 @@ class _ShowcaseState extends State with TickerProviderStateMixin { _TargetWidget( offset: offset, size: size, - onTap: _nextIfAny, + onTap: _getOnTargetTap(), shapeBorder: widget.shapeBorder, ), ToolTipWidget( @@ -213,6 +262,7 @@ class _ShowcaseState extends State with TickerProviderStateMixin { showArrow: widget.showArrow, contentHeight: widget.height, contentWidth: widget.width, + onTooltipTap: _getOnTooltipTap(), ), ], ), diff --git a/lib/showcase_widget.dart b/lib/showcase_widget.dart index 364fd254..6e24f7d5 100644 --- a/lib/showcase_widget.dart +++ b/lib/showcase_widget.dart @@ -1,12 +1,9 @@ import 'package:flutter/material.dart'; class ShowCaseWidget extends StatefulWidget { - final Widget child; + final Builder builder; - const ShowCaseWidget({ - Key key, - @required this.child, - }) : super(key: key); + const ShowCaseWidget({@required this.builder}); static activeTargetWidget(BuildContext context) { return (context.inheritFromWidgetOfExactType(_InheritedShowCaseView) @@ -14,34 +11,26 @@ class ShowCaseWidget extends StatefulWidget { .activeWidgetIds; } - static startShowCase(BuildContext context, List widgetIds) { - _ShowCaseWidgetState state = - context.ancestorStateOfType(TypeMatcher<_ShowCaseWidgetState>()) - as _ShowCaseWidgetState; - - state.startShowCase(widgetIds); - } - - static completed(BuildContext context, GlobalKey widgetIds) { - _ShowCaseWidgetState state = - context.ancestorStateOfType(TypeMatcher<_ShowCaseWidgetState>()) - as _ShowCaseWidgetState; - - state.completed(widgetIds); + static ShowCaseWidgetState of(BuildContext context) { + ShowCaseWidgetState state = context.ancestorStateOfType( + const TypeMatcher()); + if (state != null) { + return context.ancestorStateOfType( + const TypeMatcher()); + } else { + throw Exception('Please provide ShowCaseView context'); + } } - static dismiss(BuildContext context) { - _ShowCaseWidgetState state = - context.ancestorStateOfType(TypeMatcher<_ShowCaseWidgetState>()) - as _ShowCaseWidgetState; - state.dismiss(); + static setOnShowCaseFinish(VoidCallback onFinish) { + ShowCaseOnFinish._onShowCaseFinish = onFinish; } @override - _ShowCaseWidgetState createState() => _ShowCaseWidgetState(); + ShowCaseWidgetState createState() => ShowCaseWidgetState(); } -class _ShowCaseWidgetState extends State { +class ShowCaseWidgetState extends State { List ids; int activeWidgetId; @@ -59,6 +48,10 @@ class _ShowCaseWidgetState extends State { if (activeWidgetId >= ids.length) { _cleanupAfterSteps(); + if (ShowCaseOnFinish._onShowCaseFinish != null) { + ShowCaseOnFinish._onShowCaseFinish(); + ShowCaseOnFinish._onShowCaseFinish = null; + } } }); } @@ -78,7 +71,7 @@ class _ShowCaseWidgetState extends State { @override Widget build(BuildContext context) { return _InheritedShowCaseView( - child: widget.child, + child: widget.builder, activeWidgetIds: ids?.elementAt(activeWidgetId), ); } @@ -96,3 +89,7 @@ class _InheritedShowCaseView extends InheritedWidget { bool updateShouldNotify(_InheritedShowCaseView oldWidget) => oldWidget.activeWidgetIds != activeWidgetIds; } + +class ShowCaseOnFinish { + static VoidCallback _onShowCaseFinish; +} diff --git a/lib/tooltip_widget.dart b/lib/tooltip_widget.dart index 8e39a99b..24cf0839 100644 --- a/lib/tooltip_widget.dart +++ b/lib/tooltip_widget.dart @@ -17,6 +17,7 @@ class ToolTipWidget extends StatelessWidget { final double contentHeight; final double contentWidth; static bool isArrowUp; + final VoidCallback onTooltipTap; ToolTipWidget({ this.position, @@ -33,6 +34,7 @@ class ToolTipWidget extends StatelessWidget { this.showArrow, this.contentHeight, this.contentWidth, + this.onTooltipTap, }); bool isCloseToTopOrBottom(Offset position) { @@ -154,42 +156,45 @@ class ToolTipWidget extends StatelessWidget { EdgeInsets.only(top: paddingTop, bottom: paddingBottom), child: ClipRRect( borderRadius: BorderRadius.circular(8), - child: Container( - width: _getTooltipWidth(), - padding: EdgeInsets.symmetric(vertical: 8), - color: tooltipColor, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - child: Column( - crossAxisAlignment: title != null - ? CrossAxisAlignment.start - : CrossAxisAlignment.center, - children: [ - title != null - ? Text( - title, - style: titleTextStyle ?? - Theme.of(context) - .textTheme - .title - .merge(TextStyle( - color: textColor)), - ) - : Container(), - Text( - description, - style: descTextStyle ?? - Theme.of(context) - .textTheme - .subtitle - .merge(TextStyle(color: textColor)), - ), - ], - ), - ) - ], + child: GestureDetector( + onTap: onTooltipTap, + child: Container( + width: _getTooltipWidth(), + padding: EdgeInsets.symmetric(vertical: 8), + color: tooltipColor, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + child: Column( + crossAxisAlignment: title != null + ? CrossAxisAlignment.start + : CrossAxisAlignment.center, + children: [ + title != null + ? Text( + title, + style: titleTextStyle ?? + Theme.of(context) + .textTheme + .title + .merge(TextStyle( + color: textColor)), + ) + : Container(), + Text( + description, + style: descTextStyle ?? + Theme.of(context) + .textTheme + .subtitle + .merge(TextStyle(color: textColor)), + ), + ], + ), + ) + ], + ), ), ), ), @@ -215,13 +220,16 @@ class ToolTipWidget extends StatelessWidget { ).animate(animationOffset), child: Material( color: Colors.transparent, - child: Container( - padding: EdgeInsets.only( - top: paddingTop, - ), - color: Colors.transparent, - child: Center( - child: container, + child: GestureDetector( + onTap: onTooltipTap, + child: Container( + padding: EdgeInsets.only( + top: paddingTop, + ), + color: Colors.transparent, + child: Center( + child: container, + ), ), ), ), diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 00000000..76422296 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,139 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.14.11" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.5" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.6" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.6.2" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.0" + quiver: + dependency: transitive + description: + name: quiver + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.5" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.3" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.5" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.6" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.8" +sdks: + dart: ">=2.2.2 <3.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index d3840674..d1cbbf27 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: showcaseview description: A Flutter package to Showcase/Highlight widgets step by step. -version: 0.1.2 +version: 0.1.4 author: Simform solutions homepage: https://github.com/simformsolutions/flutter_showcaseview issue_tracker: https://github.com/simformsolutions/flutter_showcaseview/issues