From 7b9261b1f9e34118ebcc528c0d3da028d8b216bf Mon Sep 17 00:00:00 2001 From: Devarsh Ranpara Date: Tue, 3 Sep 2019 11:25:44 +0530 Subject: [PATCH 1/7] Added feature (onClick event) [0.1.3] - Added feature [#10](https://github.com/simformsolutions/flutter_showcaseview/issues/10). --- CHANGELOG.md | 2 + README.md | 4 +- example/ios/Podfile | 10 +++ example/ios/Podfile.lock | 3 + example/ios/Runner.xcodeproj/project.pbxproj | 51 +++++++++++++- .../contents.xcworkspacedata | 3 + example/lib/main.dart | 3 + example/pubspec.lock | 2 +- lib/showcase.dart | 69 ++++++++++--------- pubspec.yaml | 2 +- 10 files changed, 111 insertions(+), 38 deletions(-) create mode 100644 example/ios/Podfile create mode 100644 example/ios/Podfile.lock diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d43509c..70be9ce6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## [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..a9ab55de 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 @@ -98,7 +98,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/main.dart b/example/lib/main.dart index 4fff7ea4..1bf0b6b9 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -59,6 +59,9 @@ class _MailPageState extends State { Showcase( key: _one, description: 'Tap to see menu options', + onClick: () { + print('I am tapped...!'); + }, child: Icon( Icons.menu, color: Colors.black45, diff --git a/example/pubspec.lock b/example/pubspec.lock index 872a2131..465134b4 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.3" sky_engine: dependency: transitive description: flutter diff --git a/lib/showcase.dart b/lib/showcase.dart index 21fb1715..79bae93d 100644 --- a/lib/showcase.dart +++ b/lib/showcase.dart @@ -23,22 +23,24 @@ class Showcase extends StatefulWidget { final double height; final double width; final Duration animationDuration; + final VoidCallback onClick; - 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.animationDuration = const Duration(milliseconds: 2000), + this.onClick}) + : height = null, width = null, container = null, assert(overlayOpacity >= 0.0 && overlayOpacity <= 1.0, @@ -57,23 +59,24 @@ 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.animationDuration = const Duration(milliseconds: 2000), + this.onClick}) + : this.showArrow = false, assert(overlayOpacity >= 0.0 && overlayOpacity <= 1.0, "overlay opacity should be >= 0.0 and <= 1.0."), assert(key != null || @@ -195,7 +198,7 @@ class _ShowcaseState extends State with TickerProviderStateMixin { _TargetWidget( offset: offset, size: size, - onTap: _nextIfAny, + onTap: widget.onClick ?? _nextIfAny, shapeBorder: widget.shapeBorder, ), ToolTipWidget( diff --git a/pubspec.yaml b/pubspec.yaml index d3840674..75faa09d 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.3 author: Simform solutions homepage: https://github.com/simformsolutions/flutter_showcaseview issue_tracker: https://github.com/simformsolutions/flutter_showcaseview/issues From e188244347a9e256d68e50ceec956d4427270b7b Mon Sep 17 00:00:00 2001 From: Devarsh Ranpara Date: Mon, 9 Sep 2019 18:11:53 +0530 Subject: [PATCH 2/7] Added on click methods --- example/lib/main.dart | 35 ++++++++++++-- lib/showcase.dart | 12 +++-- lib/tooltip_widget.dart | 105 ++++++++++++++++++++++------------------ 3 files changed, 96 insertions(+), 56 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 1bf0b6b9..e05f76ee 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -28,14 +28,35 @@ class _MailPageState extends State { GlobalKey _four = GlobalKey(); GlobalKey _five = GlobalKey(); + final scaffoldKey = GlobalKey(); + + _showSnakbar(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])); + } + @override + void dispose() { + super.dispose(); + print('Main desposed'); + } + + @override + Widget build(BuildContext context) { return Scaffold( + key: scaffoldKey, body: SafeArea( child: ListView( children: [ @@ -59,8 +80,11 @@ class _MailPageState extends State { Showcase( key: _one, description: 'Tap to see menu options', - onClick: () { - print('I am tapped...!'); + onTargetClick: () { + _showSnakbar('Menu button clicked'); + }, + onTooltipClick: () { + _showSnakbar('Tooltip widget clicked'); }, child: Icon( Icons.menu, @@ -281,7 +305,10 @@ class _MailPageState extends State { child: FloatingActionButton( backgroundColor: Colors.white, onPressed: () { - setState(() {}); + setState(() { + ShowCaseWidget.startShowCase( + context, [_one, _two, _three, _four, _five]); + }); }, child: Icon( Icons.add, diff --git a/lib/showcase.dart b/lib/showcase.dart index 79bae93d..90b011fe 100644 --- a/lib/showcase.dart +++ b/lib/showcase.dart @@ -23,7 +23,8 @@ class Showcase extends StatefulWidget { final double height; final double width; final Duration animationDuration; - final VoidCallback onClick; + final VoidCallback onTargetClick; + final VoidCallback onTooltipClick; const Showcase( {@required this.key, @@ -39,7 +40,8 @@ class Showcase extends StatefulWidget { this.textColor = Colors.black, this.showArrow = true, this.animationDuration = const Duration(milliseconds: 2000), - this.onClick}) + this.onTargetClick, + this.onTooltipClick}) : height = null, width = null, container = null, @@ -75,7 +77,8 @@ class Showcase extends StatefulWidget { this.showcaseBackgroundColor = Colors.white, this.textColor = Colors.black, this.animationDuration = const Duration(milliseconds: 2000), - this.onClick}) + this.onTargetClick, + this.onTooltipClick}) : this.showArrow = false, assert(overlayOpacity >= 0.0 && overlayOpacity <= 1.0, "overlay opacity should be >= 0.0 and <= 1.0."), @@ -198,7 +201,7 @@ class _ShowcaseState extends State with TickerProviderStateMixin { _TargetWidget( offset: offset, size: size, - onTap: widget.onClick ?? _nextIfAny, + onTap: widget.onTargetClick ?? _nextIfAny, shapeBorder: widget.shapeBorder, ), ToolTipWidget( @@ -216,6 +219,7 @@ class _ShowcaseState extends State with TickerProviderStateMixin { showArrow: widget.showArrow, contentHeight: widget.height, contentWidth: widget.width, + onTooltipClick: widget.onTooltipClick, ), ], ), diff --git a/lib/tooltip_widget.dart b/lib/tooltip_widget.dart index 8e39a99b..9066b7d9 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 onTooltipClick; ToolTipWidget({ this.position, @@ -33,6 +34,7 @@ class ToolTipWidget extends StatelessWidget { this.showArrow, this.contentHeight, this.contentWidth, + this.onTooltipClick, }); bool isCloseToTopOrBottom(Offset position) { @@ -149,47 +151,51 @@ class ToolTipWidget extends StatelessWidget { ).animate(animationOffset), child: Material( color: Colors.transparent, - child: Container( - padding: - 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: onTooltipClick, + child: Container( + padding: 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)), + ), + ], + ), + ) + ], + ), ), ), ), @@ -215,13 +221,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: onTooltipClick, + child: Container( + padding: EdgeInsets.only( + top: paddingTop, + ), + color: Colors.transparent, + child: Center( + child: container, + ), ), ), ), From ff3cbb74fcfad2049dad91921d721f8e91c9eca5 Mon Sep 17 00:00:00 2001 From: Devarsh Ranpara Date: Tue, 17 Sep 2019 13:20:44 +0530 Subject: [PATCH 3/7] Added onFinish() callback Added onFinish() callback on ShowCaseView plugin. --- example/lib/main.dart | 16 +++-- lib/showcase.dart | 13 +--- lib/showcase_widget.dart | 24 +++++-- lib/tooltip_widget.dart | 106 ++++++++++++++--------------- pubspec.lock | 139 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 219 insertions(+), 79 deletions(-) create mode 100644 pubspec.lock diff --git a/example/lib/main.dart b/example/lib/main.dart index e05f76ee..63cb0826 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -11,7 +11,11 @@ class MyApp extends StatelessWidget { theme: ThemeData( primarySwatch: Colors.blue, ), - home: ShowCaseWidget(child: MailPage()), + home: Scaffold( + body: ShowCaseWidget( + child: MailPage(), + ), + ), ); } } @@ -45,6 +49,10 @@ class _MailPageState extends State { WidgetsBinding.instance.addPostFrameCallback((_) => ShowCaseWidget.startShowCase( context, [_one, _two, _three, _four, _five])); + + ShowCaseWidget.setOnShowCaseFinish(() { + _showSnakbar('ShowcaseView Finished'); + }); } @override @@ -80,12 +88,6 @@ class _MailPageState extends State { Showcase( key: _one, description: 'Tap to see menu options', - onTargetClick: () { - _showSnakbar('Menu button clicked'); - }, - onTooltipClick: () { - _showSnakbar('Tooltip widget clicked'); - }, child: Icon( Icons.menu, color: Colors.black45, diff --git a/lib/showcase.dart b/lib/showcase.dart index 90b011fe..8375ca49 100644 --- a/lib/showcase.dart +++ b/lib/showcase.dart @@ -23,8 +23,6 @@ class Showcase extends StatefulWidget { final double height; final double width; final Duration animationDuration; - final VoidCallback onTargetClick; - final VoidCallback onTooltipClick; const Showcase( {@required this.key, @@ -39,9 +37,7 @@ class Showcase extends StatefulWidget { this.showcaseBackgroundColor = Colors.white, this.textColor = Colors.black, this.showArrow = true, - this.animationDuration = const Duration(milliseconds: 2000), - this.onTargetClick, - this.onTooltipClick}) + this.animationDuration = const Duration(milliseconds: 2000)}) : height = null, width = null, container = null, @@ -76,9 +72,7 @@ class Showcase extends StatefulWidget { this.descTextStyle, this.showcaseBackgroundColor = Colors.white, this.textColor = Colors.black, - this.animationDuration = const Duration(milliseconds: 2000), - this.onTargetClick, - this.onTooltipClick}) + this.animationDuration = const Duration(milliseconds: 2000)}) : this.showArrow = false, assert(overlayOpacity >= 0.0 && overlayOpacity <= 1.0, "overlay opacity should be >= 0.0 and <= 1.0."), @@ -201,7 +195,7 @@ class _ShowcaseState extends State with TickerProviderStateMixin { _TargetWidget( offset: offset, size: size, - onTap: widget.onTargetClick ?? _nextIfAny, + onTap: _nextIfAny, shapeBorder: widget.shapeBorder, ), ToolTipWidget( @@ -219,7 +213,6 @@ class _ShowcaseState extends State with TickerProviderStateMixin { showArrow: widget.showArrow, contentHeight: widget.height, contentWidth: widget.width, - onTooltipClick: widget.onTooltipClick, ), ], ), diff --git a/lib/showcase_widget.dart b/lib/showcase_widget.dart index 364fd254..b64f154d 100644 --- a/lib/showcase_widget.dart +++ b/lib/showcase_widget.dart @@ -3,10 +3,7 @@ import 'package:flutter/material.dart'; class ShowCaseWidget extends StatefulWidget { final Widget child; - const ShowCaseWidget({ - Key key, - @required this.child, - }) : super(key: key); + const ShowCaseWidget({Key key, @required this.child}) : super(key: key); static activeTargetWidget(BuildContext context) { return (context.inheritFromWidgetOfExactType(_InheritedShowCaseView) @@ -37,6 +34,10 @@ class ShowCaseWidget extends StatefulWidget { state.dismiss(); } + static setOnShowCaseFinish(VoidCallback onFinish) { + ShowCaseOnFinish._onShowCaseFinish = onFinish; + } + @override _ShowCaseWidgetState createState() => _ShowCaseWidgetState(); } @@ -56,9 +57,11 @@ class _ShowCaseWidgetState extends State { if (ids != null && ids[activeWidgetId] == id) { setState(() { ++activeWidgetId; - if (activeWidgetId >= ids.length) { _cleanupAfterSteps(); + if (ShowCaseOnFinish._onShowCaseFinish != null) { + ShowCaseOnFinish._onShowCaseFinish(); + } } }); } @@ -82,6 +85,13 @@ class _ShowCaseWidgetState extends State { activeWidgetIds: ids?.elementAt(activeWidgetId), ); } + + @override + void dispose() { + ShowCaseOnFinish._onShowCaseFinish = null; + super.dispose(); + } + } class _InheritedShowCaseView extends InheritedWidget { @@ -96,3 +106,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 9066b7d9..19c852c8 100644 --- a/lib/tooltip_widget.dart +++ b/lib/tooltip_widget.dart @@ -17,7 +17,6 @@ class ToolTipWidget extends StatelessWidget { final double contentHeight; final double contentWidth; static bool isArrowUp; - final VoidCallback onTooltipClick; ToolTipWidget({ this.position, @@ -34,7 +33,6 @@ class ToolTipWidget extends StatelessWidget { this.showArrow, this.contentHeight, this.contentWidth, - this.onTooltipClick, }); bool isCloseToTopOrBottom(Offset position) { @@ -151,51 +149,48 @@ class ToolTipWidget extends StatelessWidget { ).animate(animationOffset), child: Material( color: Colors.transparent, - child: GestureDetector( - onTap: onTooltipClick, - child: Container( - padding: 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: Container( + padding: 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)), + ), + ], + ), + ) + ], ), ), ), @@ -221,16 +216,13 @@ class ToolTipWidget extends StatelessWidget { ).animate(animationOffset), child: Material( color: Colors.transparent, - child: GestureDetector( - onTap: onTooltipClick, - child: Container( - padding: EdgeInsets.only( - top: paddingTop, - ), - color: Colors.transparent, - child: Center( - child: container, - ), + 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" From 30902b1a7811b8a411376f92075b8f26310eef33 Mon Sep 17 00:00:00 2001 From: Devarsh Ranpara Date: Tue, 17 Sep 2019 18:21:56 +0530 Subject: [PATCH 4/7] Added onTooltipClick event --- example/lib/SecondPage.dart | 65 ++++++++++++ example/lib/main.dart | 193 ++++++++++++++++++++---------------- lib/showcase.dart | 47 ++++++++- lib/showcase_widget.dart | 7 +- lib/tooltip_widget.dart | 99 +++++++++--------- 5 files changed, 279 insertions(+), 132 deletions(-) create mode 100644 example/lib/SecondPage.dart diff --git a/example/lib/SecondPage.dart b/example/lib/SecondPage.dart new file mode 100644 index 00000000..18758efa --- /dev/null +++ b/example/lib/SecondPage.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; + +class Second extends StatelessWidget { + @override + Widget build(BuildContext 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: [ + 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.'), + ]), + ), +// Text( +// "Hi team,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.Why are we moving to Slack? 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.", +// style: TextStyle(fontWeight: FontWeight.w400), +// ), + ], + ), + ), + ); + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart index 63cb0826..630e723c 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 { @@ -147,29 +149,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: (_) => Second(), + ), + ); + }, + 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: (_) => Second(), + ), + ).then((_) { + setState(() { + ShowCaseWidget.startShowCase(context, [_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( @@ -180,75 +226,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, - ) - ], - ) - ], + ) + ], + ), ), ), ), diff --git a/lib/showcase.dart b/lib/showcase.dart index 8375ca49..d52a864b 100644 --- a/lib/showcase.dart +++ b/lib/showcase.dart @@ -23,6 +23,9 @@ 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, @@ -37,12 +40,20 @@ class Showcase extends StatefulWidget { 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 with use of onTargetClick"), assert(key != null || child != null || title != null || @@ -72,8 +83,11 @@ class Showcase extends StatefulWidget { 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 || @@ -166,6 +180,36 @@ class _ShowcaseState extends State with TickerProviderStateMixin { _slideAnimationController.forward(); } + _getOnTargetTap() { + if (widget.disposeOnTap == true) { + return widget.onTargetClick == null + ? () { + ShowCaseWidget.dismiss(context); + } + : () { + ShowCaseWidget.dismiss(context); + widget.onTargetClick(); + }; + } else { + return widget.onTargetClick ?? _nextIfAny; + } + } + + _getOnTooltipTap() { + if (widget.disposeOnTap == true) { + return widget.onToolTipClick == null + ? () { + ShowCaseWidget.dismiss(context); + } + : () { + ShowCaseWidget.dismiss(context); + widget.onToolTipClick(); + }; + } else { + return widget.onToolTipClick ?? () {}; + } + } + buildOverlayOnTarget( Offset offset, Size size, @@ -195,7 +239,7 @@ class _ShowcaseState extends State with TickerProviderStateMixin { _TargetWidget( offset: offset, size: size, - onTap: _nextIfAny, + onTap: _getOnTargetTap(), shapeBorder: widget.shapeBorder, ), ToolTipWidget( @@ -213,6 +257,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 b64f154d..74d5dedf 100644 --- a/lib/showcase_widget.dart +++ b/lib/showcase_widget.dart @@ -46,6 +46,12 @@ class _ShowCaseWidgetState extends State { List ids; int activeWidgetId; + @override + void didChangeDependencies() { + + super.didChangeDependencies(); + } + void startShowCase(List widgetIds) { setState(() { this.ids = widgetIds; @@ -91,7 +97,6 @@ class _ShowCaseWidgetState extends State { ShowCaseOnFinish._onShowCaseFinish = null; super.dispose(); } - } class _InheritedShowCaseView extends InheritedWidget { diff --git a/lib/tooltip_widget.dart b/lib/tooltip_widget.dart index 19c852c8..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) { @@ -150,47 +152,49 @@ class ToolTipWidget extends StatelessWidget { child: Material( color: Colors.transparent, child: Container( - padding: EdgeInsets.only( - top: paddingTop, bottom: paddingBottom), + padding: + 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)), + ), + ], + ), + ) + ], + ), ), ), ), @@ -216,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, + ), ), ), ), From 1001022676ae8655097ecf1b2f59fb542f54956d Mon Sep 17 00:00:00 2001 From: Devarsh Ranpara Date: Thu, 19 Sep 2019 15:45:05 +0530 Subject: [PATCH 5/7] Added builder in ShowCaseWidget Added onFinish method Added onTap callback --- example/lib/SecondPage.dart | 140 ++++++++++++++++++++++-------------- example/lib/main.dart | 13 ++-- lib/showcase_widget.dart | 49 +++++-------- 3 files changed, 109 insertions(+), 93 deletions(-) diff --git a/example/lib/SecondPage.dart b/example/lib/SecondPage.dart index 18758efa..880a8838 100644 --- a/example/lib/SecondPage.dart +++ b/example/lib/SecondPage.dart @@ -1,64 +1,96 @@ import 'package:flutter/material.dart'; +import 'package:showcaseview/showcase_widget.dart'; +import 'package:showcaseview/showcaseview.dart'; + +class Second extends StatefulWidget { + @override + _SecondState createState() => _SecondState(); +} + +class _SecondState extends State { + final GlobalKey _one = GlobalKey(); + BuildContext myContext; + + @override + void didChangeDependencies() { + //Start showcase view after current widget frames are drawn. + WidgetsBinding.instance.addPostFrameCallback( + (_) => ShowCaseWidget.startShowCase(myContext, [_one])); + super.didChangeDependencies(); + } -class Second extends StatelessWidget { @override Widget build(BuildContext 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: [ - 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, + 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); + }, + ), ), - 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', + 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( - 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.'), - ]), + 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.'), + ], + ), + ), + ], + ), ), -// Text( -// "Hi team,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.Why are we moving to Slack? 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.", -// style: TextStyle(fontWeight: FontWeight.w400), -// ), - ], - ), + ); + }, ), ); } diff --git a/example/lib/main.dart b/example/lib/main.dart index 630e723c..45f3aeae 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -15,7 +15,11 @@ class MyApp extends StatelessWidget { ), home: Scaffold( body: ShowCaseWidget( - child: MailPage(), + builder: Builder( + builder: (context) { + return MailPage(); + }, + ), ), ), ); @@ -51,18 +55,11 @@ class _MailPageState extends State { WidgetsBinding.instance.addPostFrameCallback((_) => ShowCaseWidget.startShowCase( context, [_one, _two, _three, _four, _five])); - ShowCaseWidget.setOnShowCaseFinish(() { _showSnakbar('ShowcaseView Finished'); }); } - @override - void dispose() { - super.dispose(); - print('Main desposed'); - } - @override Widget build(BuildContext context) { return Scaffold( diff --git a/lib/showcase_widget.dart b/lib/showcase_widget.dart index 74d5dedf..8355c489 100644 --- a/lib/showcase_widget.dart +++ b/lib/showcase_widget.dart @@ -1,9 +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) @@ -11,27 +11,24 @@ class ShowCaseWidget extends StatefulWidget { .activeWidgetIds; } - static startShowCase(BuildContext context, List widgetIds) { - _ShowCaseWidgetState state = - context.ancestorStateOfType(TypeMatcher<_ShowCaseWidgetState>()) - as _ShowCaseWidgetState; + static ShowCaseWidgetState of(BuildContext context) => + context.ancestorStateOfType(const TypeMatcher()); - state.startShowCase(widgetIds); + static startShowCase(BuildContext context, List widgetIds) { + ShowCaseWidgetState state = ShowCaseWidget.of(context); + if (state != null) { + state.startShowCase(widgetIds); + } else { + throw Exception('Please provide ShowCaseView context'); + } } static completed(BuildContext context, GlobalKey widgetIds) { - _ShowCaseWidgetState state = - context.ancestorStateOfType(TypeMatcher<_ShowCaseWidgetState>()) - as _ShowCaseWidgetState; - - state.completed(widgetIds); + ShowCaseWidget.of(context).completed(widgetIds); } static dismiss(BuildContext context) { - _ShowCaseWidgetState state = - context.ancestorStateOfType(TypeMatcher<_ShowCaseWidgetState>()) - as _ShowCaseWidgetState; - state.dismiss(); + ShowCaseWidget.of(context).dismiss(); } static setOnShowCaseFinish(VoidCallback onFinish) { @@ -39,19 +36,13 @@ class ShowCaseWidget extends StatefulWidget { } @override - _ShowCaseWidgetState createState() => _ShowCaseWidgetState(); + ShowCaseWidgetState createState() => ShowCaseWidgetState(); } -class _ShowCaseWidgetState extends State { +class ShowCaseWidgetState extends State { List ids; int activeWidgetId; - @override - void didChangeDependencies() { - - super.didChangeDependencies(); - } - void startShowCase(List widgetIds) { setState(() { this.ids = widgetIds; @@ -63,10 +54,12 @@ class _ShowCaseWidgetState extends State { if (ids != null && ids[activeWidgetId] == id) { setState(() { ++activeWidgetId; + if (activeWidgetId >= ids.length) { _cleanupAfterSteps(); if (ShowCaseOnFinish._onShowCaseFinish != null) { ShowCaseOnFinish._onShowCaseFinish(); + ShowCaseOnFinish._onShowCaseFinish = null; } } }); @@ -87,16 +80,10 @@ class _ShowCaseWidgetState extends State { @override Widget build(BuildContext context) { return _InheritedShowCaseView( - child: widget.child, + child: widget.builder, activeWidgetIds: ids?.elementAt(activeWidgetId), ); } - - @override - void dispose() { - ShowCaseOnFinish._onShowCaseFinish = null; - super.dispose(); - } } class _InheritedShowCaseView extends InheritedWidget { From 1045d9b12a690188b43b9a39949c47d3bd7824a2 Mon Sep 17 00:00:00 2001 From: Devarsh Ranpara Date: Fri, 20 Sep 2019 17:10:27 +0530 Subject: [PATCH 6/7] - of method added and other improvements --- example/lib/SecondPage.dart | 19 ++++++++++++------- example/lib/main.dart | 16 +++++++--------- lib/showcase.dart | 17 +++++++++++------ lib/showcase_widget.dart | 19 +++++-------------- 4 files changed, 35 insertions(+), 36 deletions(-) diff --git a/example/lib/SecondPage.dart b/example/lib/SecondPage.dart index 880a8838..6afef8d6 100644 --- a/example/lib/SecondPage.dart +++ b/example/lib/SecondPage.dart @@ -2,21 +2,26 @@ import 'package:flutter/material.dart'; import 'package:showcaseview/showcase_widget.dart'; import 'package:showcaseview/showcaseview.dart'; -class Second extends StatefulWidget { +class Detail extends StatefulWidget { @override - _SecondState createState() => _SecondState(); + _DetailState createState() => _DetailState(); } -class _SecondState extends State { +class _DetailState extends State { final GlobalKey _one = GlobalKey(); BuildContext myContext; @override - void didChangeDependencies() { - //Start showcase view after current widget frames are drawn. + void initState() { + // TODO: implement initState + super.initState(); WidgetsBinding.instance.addPostFrameCallback( - (_) => ShowCaseWidget.startShowCase(myContext, [_one])); - super.didChangeDependencies(); + (_) { + Future.delayed(Duration(milliseconds: 200), () => + ShowCaseWidget.of(myContext).startShowCase([_one]) + ); + } + ); } @override diff --git a/example/lib/main.dart b/example/lib/main.dart index 45f3aeae..6d172ecd 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -40,7 +40,7 @@ class _MailPageState extends State { final scaffoldKey = GlobalKey(); - _showSnakbar(String msg) { + _showSnackbar(String msg) { final snackbar = SnackBar( content: Text(msg), backgroundColor: Colors.blue, @@ -53,10 +53,9 @@ class _MailPageState extends State { 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(() { - _showSnakbar('ShowcaseView Finished'); + _showSnackbar('ShowcaseView Finished'); }); } @@ -151,7 +150,7 @@ class _MailPageState extends State { Navigator.push( context, MaterialPageRoute( - builder: (_) => Second(), + builder: (_) => Detail(), ), ); }, @@ -165,11 +164,11 @@ class _MailPageState extends State { Navigator.push( context, MaterialPageRoute( - builder: (_) => Second(), + builder: (_) => Detail(), ), ).then((_) { setState(() { - ShowCaseWidget.startShowCase(context, [_four, _five]); + ShowCaseWidget.of(context).startShowCase([_four, _five]); }); }); }, @@ -330,8 +329,7 @@ class _MailPageState extends State { backgroundColor: Colors.white, onPressed: () { setState(() { - ShowCaseWidget.startShowCase( - context, [_one, _two, _three, _four, _five]); + ShowCaseWidget.of(context).startShowCase([_one, _two, _three, _four, _five]); }); }, child: Icon( diff --git a/lib/showcase.dart b/lib/showcase.dart index d52a864b..08b8081b 100644 --- a/lib/showcase.dart +++ b/lib/showcase.dart @@ -53,7 +53,12 @@ class Showcase extends StatefulWidget { onTargetClick == null ? true : (disposeOnTap == null ? false : true), - "disposeOnTap is required with use of onTargetClick"), + "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 || @@ -176,7 +181,7 @@ class _ShowcaseState extends State with TickerProviderStateMixin { } _nextIfAny() { - ShowCaseWidget.completed(context, widget.key); + ShowCaseWidget.of(context).completed(widget.key); _slideAnimationController.forward(); } @@ -184,10 +189,10 @@ class _ShowcaseState extends State with TickerProviderStateMixin { if (widget.disposeOnTap == true) { return widget.onTargetClick == null ? () { - ShowCaseWidget.dismiss(context); + ShowCaseWidget.of(context).dismiss(); } : () { - ShowCaseWidget.dismiss(context); + ShowCaseWidget.of(context).dismiss(); widget.onTargetClick(); }; } else { @@ -199,10 +204,10 @@ class _ShowcaseState extends State with TickerProviderStateMixin { if (widget.disposeOnTap == true) { return widget.onToolTipClick == null ? () { - ShowCaseWidget.dismiss(context); + ShowCaseWidget.of(context).dismiss(); } : () { - ShowCaseWidget.dismiss(context); + ShowCaseWidget.of(context).dismiss(); widget.onToolTipClick(); }; } else { diff --git a/lib/showcase_widget.dart b/lib/showcase_widget.dart index 8355c489..6e24f7d5 100644 --- a/lib/showcase_widget.dart +++ b/lib/showcase_widget.dart @@ -11,26 +11,17 @@ class ShowCaseWidget extends StatefulWidget { .activeWidgetIds; } - static ShowCaseWidgetState of(BuildContext context) => - context.ancestorStateOfType(const TypeMatcher()); - - static startShowCase(BuildContext context, List widgetIds) { - ShowCaseWidgetState state = ShowCaseWidget.of(context); + static ShowCaseWidgetState of(BuildContext context) { + ShowCaseWidgetState state = context.ancestorStateOfType( + const TypeMatcher()); if (state != null) { - state.startShowCase(widgetIds); + return context.ancestorStateOfType( + const TypeMatcher()); } else { throw Exception('Please provide ShowCaseView context'); } } - static completed(BuildContext context, GlobalKey widgetIds) { - ShowCaseWidget.of(context).completed(widgetIds); - } - - static dismiss(BuildContext context) { - ShowCaseWidget.of(context).dismiss(); - } - static setOnShowCaseFinish(VoidCallback onFinish) { ShowCaseOnFinish._onShowCaseFinish = onFinish; } From 1d005908218ee773e8255ecbb9bc7d5c3a10f254 Mon Sep 17 00:00:00 2001 From: Devarsh Ranpara Date: Fri, 20 Sep 2019 18:26:52 +0530 Subject: [PATCH 7/7] version bump --- CHANGELOG.md | 6 ++++++ README.md | 4 +++- example/lib/main.dart | 4 +--- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70be9ce6..27ba5be2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [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). diff --git a/README.md b/README.md index a9ab55de..9438f101 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,9 @@ import 'package:showcaseview/showcaseview.dart'; 3. Adding a `ShowCaseWidget` widget. ```dart ShowCaseWidget( - child: Somewidget(), + builder: Builder( + builder : (context) ()=> Somewidget() + ), ), ``` diff --git a/example/lib/main.dart b/example/lib/main.dart index 6d172ecd..476aaec4 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -16,9 +16,7 @@ class MyApp extends StatelessWidget { home: Scaffold( body: ShowCaseWidget( builder: Builder( - builder: (context) { - return MailPage(); - }, + builder: (context) => MailPage() ), ), ), diff --git a/example/pubspec.lock b/example/pubspec.lock index 465134b4..350d4890 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -87,7 +87,7 @@ packages: path: ".." relative: true source: path - version: "0.1.3" + version: "0.1.4" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 75faa09d..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.3 +version: 0.1.4 author: Simform solutions homepage: https://github.com/simformsolutions/flutter_showcaseview issue_tracker: https://github.com/simformsolutions/flutter_showcaseview/issues