From 0b280433d272ba8ff398b90dab2a18b93d2ec243 Mon Sep 17 00:00:00 2001 From: Aman Date: Sat, 20 Nov 2021 20:46:51 +0530 Subject: [PATCH 01/14] feat: implement showcase view on interactive book --- lib/models/ib/ib_showcase.dart | 42 +++++++++++ lib/services/local_storage_service.dart | 13 ++++ lib/ui/views/ib/ib_landing_view.dart | 42 +++++++++-- lib/ui/views/ib/ib_page_view.dart | 92 ++++++++++++++++++------- lib/utils/router.dart | 11 ++- pubspec.lock | 7 ++ pubspec.yaml | 1 + 7 files changed, 179 insertions(+), 29 deletions(-) create mode 100644 lib/models/ib/ib_showcase.dart diff --git a/lib/models/ib/ib_showcase.dart b/lib/models/ib/ib_showcase.dart new file mode 100644 index 00000000..ecf32e3c --- /dev/null +++ b/lib/models/ib/ib_showcase.dart @@ -0,0 +1,42 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; + +class IBShowCase { + bool nextButton, prevButton, contentButton, drawerButton; + + IBShowCase({ + @required this.nextButton, + @required this.prevButton, + @required this.contentButton, + }); + + factory IBShowCase.fromJson(Map json) { + return IBShowCase( + nextButton: json['next'] ?? false, + prevButton: json['prev'] ?? false, + contentButton: json['content'] ?? false, + ); + } + + IBShowCase copyWith({ + bool nextButton, + bool prevButton, + bool contentButton, + }) { + return IBShowCase( + nextButton: nextButton ?? this.nextButton, + prevButton: prevButton ?? this.prevButton, + contentButton: contentButton ?? this.contentButton, + ); + } + + @override + String toString() { + return json.encode({ + 'next': nextButton, + 'prev': prevButton, + 'content': contentButton, + }); + } +} diff --git a/lib/services/local_storage_service.dart b/lib/services/local_storage_service.dart index 0064b604..8cc5f11e 100644 --- a/lib/services/local_storage_service.dart +++ b/lib/services/local_storage_service.dart @@ -14,6 +14,7 @@ class LocalStorageService { static const String IS_LOGGED_IN = 'is_logged_in'; static const String IS_FIRST_TIME_LOGIN = 'is_first_time_login'; static const String AUTH_TYPE = 'auth_type'; + static const String IB_SHOWCASE_STATE = 'ib_showcase_state'; static Future getInstance() async { _preferences ??= await SharedPreferences.getInstance(); @@ -86,4 +87,16 @@ class LocalStorageService { set authType(AuthType authType) { _saveToDisk(AUTH_TYPE, authTypeValues.reverse[authType]); } + + Map get getShowcaseState { + final Map result = + Map.castFrom( + json.decode(_getFromDisk(IB_SHOWCASE_STATE) ?? '{}') + as Map); + return result; + } + + set setShowcaseState(String state) { + _saveToDisk(IB_SHOWCASE_STATE, state); + } } diff --git a/lib/ui/views/ib/ib_landing_view.dart b/lib/ui/views/ib/ib_landing_view.dart index cbd7f780..9fc756e2 100644 --- a/lib/ui/views/ib/ib_landing_view.dart +++ b/lib/ui/views/ib/ib_landing_view.dart @@ -2,11 +2,15 @@ import 'package:animations/animations.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:mobile_app/ib_theme.dart'; +import 'package:mobile_app/locator.dart'; import 'package:mobile_app/models/ib/ib_chapter.dart'; +import 'package:mobile_app/models/ib/ib_showcase.dart'; +import 'package:mobile_app/services/local_storage_service.dart'; import 'package:mobile_app/ui/components/cv_drawer_tile.dart'; import 'package:mobile_app/ui/views/base_view.dart'; import 'package:mobile_app/ui/views/ib/ib_page_view.dart'; import 'package:mobile_app/viewmodels/ib/ib_landing_viewmodel.dart'; +import 'package:showcaseview/showcaseview.dart'; import 'package:theme_provider/theme_provider.dart'; class IbLandingView extends StatefulWidget { @@ -26,11 +30,16 @@ class _IbLandingViewState extends State { ); IbChapter _selectedChapter; ValueNotifier _tocNotifier; + IBShowCase _showCaseState; + final GlobalKey _menu = GlobalKey(); + final LocalStorageService _localStorageService = + locator(); @override void initState() { _tocNotifier = ValueNotifier(null); _selectedChapter = _homeChapter; + _showCaseState = IBShowCase.fromJson(_localStorageService.getShowcaseState); super.initState(); } @@ -58,11 +67,31 @@ class _IbLandingViewState extends State { ValueListenableBuilder( valueListenable: _tocNotifier, builder: (context, value, child) { + if (!_showCaseState.contentButton && + value != null && + ShowCaseWidget.of(context).activeWidgetId == null) { + WidgetsBinding.instance.addPostFrameCallback((_) { + ShowCaseWidget.of(context).startShowCase([_menu]); + }); + } + return value != null - ? IconButton( - icon: const Icon(Icons.menu_book_rounded), - tooltip: 'Show Table of Contents', - onPressed: value, + ? Showcase( + key: _menu, + description: 'Show Table of Contents', + child: IconButton( + icon: const Icon(Icons.menu_book_rounded), + onPressed: value, + ), + onTargetClick: () { + setState(() { + _showCaseState = _showCaseState.copyWith( + contentButton: true, + ); + }); + value(); + }, + disposeOnTap: true, ) : Container(); }, @@ -209,6 +238,7 @@ class _IbLandingViewState extends State { setState(() => _selectedChapter = _homeChapter); return Future.value(false); } + _localStorageService.setShowcaseState = _showCaseState.toString(); return Future.value(true); }, child: Theme( @@ -242,6 +272,10 @@ class _IbLandingViewState extends State { setState(() => _selectedChapter = chapter); }, chapter: _selectedChapter, + setShowCase: (updatedState) { + setState(() => _showCaseState = updatedState); + }, + showCase: _showCaseState, ), ), ), diff --git a/lib/ui/views/ib/ib_page_view.dart b/lib/ui/views/ib/ib_page_view.dart index d02d7612..36fa2b2f 100644 --- a/lib/ui/views/ib/ib_page_view.dart +++ b/lib/ui/views/ib/ib_page_view.dart @@ -8,6 +8,7 @@ import 'package:mobile_app/ib_theme.dart'; import 'package:mobile_app/models/ib/ib_chapter.dart'; import 'package:mobile_app/models/ib/ib_content.dart'; import 'package:mobile_app/models/ib/ib_page_data.dart'; +import 'package:mobile_app/models/ib/ib_showcase.dart'; import 'package:mobile_app/services/ib_engine_service.dart'; import 'package:mobile_app/ui/views/base_view.dart'; import 'package:mobile_app/ui/views/ib/builders/ib_chapter_contents_builder.dart'; @@ -27,10 +28,12 @@ import 'package:mobile_app/ui/views/ib/syntaxes/ib_md_tag_syntax.dart'; import 'package:mobile_app/utils/url_launcher.dart'; import 'package:mobile_app/viewmodels/ib/ib_page_viewmodel.dart'; import 'package:scroll_to_index/scroll_to_index.dart'; +import 'package:showcaseview/showcaseview.dart'; import 'package:url_launcher/url_launcher.dart'; typedef TocCallback = void Function(Function); typedef SetPageCallback = void Function(IbChapter); +typedef SetShowCaseStateCallback = void Function(IBShowCase); class IbPageView extends StatefulWidget { const IbPageView({ @@ -38,12 +41,16 @@ class IbPageView extends StatefulWidget { @required this.tocCallback, @required this.chapter, @required this.setPage, + @required this.showCase, + @required this.setShowCase, }) : super(key: key); static const String id = 'ib_page_view'; final TocCallback tocCallback; final SetPageCallback setPage; final IbChapter chapter; + final IBShowCase showCase; + final SetShowCaseStateCallback setShowCase; @override _IbPageViewState createState() => _IbPageViewState(); @@ -53,12 +60,18 @@ class _IbPageViewState extends State { IbPageViewModel _model; AutoScrollController _hideButtonController; bool _isFabsVisible = true; + List _list; /// To track index through slug for scroll_to_index final Map _slugMap = {}; + //Global Keys + final GlobalKey _nextPage = GlobalKey(); + final GlobalKey _prevPage = GlobalKey(); + @override void initState() { + _list = []; super.initState(); _isFabsVisible = true; _hideButtonController = AutoScrollController(axis: Axis.vertical); @@ -72,6 +85,17 @@ class _IbPageViewState extends State { setState(() => _isFabsVisible = true); } }); + if (!widget.showCase.nextButton) _list.add(_nextPage); + if (!widget.showCase.prevButton) _list.add(_prevPage); + if (_list.isNotEmpty) { + Future.delayed(const Duration(milliseconds: 500), () { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + ShowCaseWidget.of(context).startShowCase(_list); + } + }); + }); + } } Widget _buildDivider() { @@ -356,20 +380,30 @@ class _IbPageViewState extends State { AnimatedOpacity( duration: const Duration(milliseconds: 500), opacity: _isFabsVisible ? 1.0 : 0.0, - child: FloatingActionButton( - heroTag: 'previousPage', - mini: true, - backgroundColor: Theme.of(context).primaryIconTheme.color, - onPressed: () { - //If FAB are not visible do not do anything. - if (!_isFabsVisible) { - return; - } + child: Showcase( + key: _prevPage, + description: 'Tap to navigate to previous page', + shapeBorder: const CircleBorder(), + onTargetClick: () { + widget.setShowCase(widget.showCase.copyWith(prevButton: true)); widget.setPage(widget.chapter.prev); }, - child: const Icon( - Icons.arrow_back_rounded, - color: IbTheme.primaryColor, + disposeOnTap: true, + child: FloatingActionButton( + heroTag: 'previousPage', + mini: true, + backgroundColor: Theme.of(context).primaryIconTheme.color, + onPressed: () { + //If FAB are not visible do not do anything. + if (!_isFabsVisible) { + return; + } + widget.setPage(widget.chapter.prev); + }, + child: const Icon( + Icons.arrow_back_rounded, + color: IbTheme.primaryColor, + ), ), ), ), @@ -385,20 +419,30 @@ class _IbPageViewState extends State { AnimatedOpacity( duration: const Duration(milliseconds: 500), opacity: _isFabsVisible ? 1.0 : 0.0, - child: FloatingActionButton( - heroTag: 'nextPage', - mini: true, - backgroundColor: Theme.of(context).primaryIconTheme.color, - onPressed: () { - //If FAB are not visible do not do anything. - if (!_isFabsVisible) { - return; - } + child: Showcase( + key: _nextPage, + description: 'Tap to navigate to next page', + shapeBorder: const CircleBorder(), + onTargetClick: () { + widget.setShowCase(widget.showCase.copyWith(nextButton: true)); widget.setPage(widget.chapter.next); }, - child: const Icon( - Icons.arrow_forward_rounded, - color: IbTheme.primaryColor, + disposeOnTap: false, + child: FloatingActionButton( + heroTag: 'nextPage', + mini: true, + backgroundColor: Theme.of(context).primaryIconTheme.color, + onPressed: () { + //If FAB are not visible do not do anything. + if (!_isFabsVisible) { + return; + } + widget.setPage(widget.chapter.next); + }, + child: const Icon( + Icons.arrow_forward_rounded, + color: IbTheme.primaryColor, + ), ), ), ), diff --git a/lib/utils/router.dart b/lib/utils/router.dart index d036ebbe..5030c256 100644 --- a/lib/utils/router.dart +++ b/lib/utils/router.dart @@ -24,6 +24,7 @@ import 'package:mobile_app/ui/views/projects/featured_projects_view.dart'; import 'package:mobile_app/ui/views/projects/project_details_view.dart'; import 'package:mobile_app/ui/views/projects/project_preview_fullscreen_view.dart'; import 'package:mobile_app/ui/views/teachers/teachers_view.dart'; +import 'package:showcaseview/showcaseview.dart'; class CVRouter { static Route generateRoute(RouteSettings settings) { @@ -110,7 +111,15 @@ class CVRouter { ), ); case IbLandingView.id: - return MaterialPageRoute(builder: (_) => const IbLandingView()); + return MaterialPageRoute( + builder: (_) => ShowCaseWidget( + builder: Builder( + builder: (context) { + return const IbLandingView(); + }, + ), + ), + ); case ProjectPreviewFullScreen.id: var _project = settings.arguments as Project; return MaterialPageRoute( diff --git a/pubspec.lock b/pubspec.lock index 18b181ea..d1bb90c1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -835,6 +835,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.1" + showcaseview: + dependency: "direct main" + description: + name: showcaseview + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.3" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index a9da8f5b..93b39e79 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,6 +57,7 @@ dependencies: hive_flutter: ^1.1.0 flutter_math_fork: ^0.5.0 scroll_to_index: ^2.0.0 + showcaseview: ^1.1.3 dev_dependencies: flutter_test: From 76792bf1c197b01a2ad8337bd2060f27e6a6fb8a Mon Sep 17 00:00:00 2001 From: Aman Date: Sat, 20 Nov 2021 20:56:48 +0530 Subject: [PATCH 02/14] feat: add test for showcase view --- lib/ui/views/ib/ib_landing_view.dart | 2 ++ lib/ui/views/ib/ib_page_view.dart | 2 +- test/ui_tests/ib/ib_page_view_test.dart | 19 ++++++++++++++----- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/ui/views/ib/ib_landing_view.dart b/lib/ui/views/ib/ib_landing_view.dart index 9fc756e2..2700ff8a 100644 --- a/lib/ui/views/ib/ib_landing_view.dart +++ b/lib/ui/views/ib/ib_landing_view.dart @@ -30,6 +30,8 @@ class _IbLandingViewState extends State { ); IbChapter _selectedChapter; ValueNotifier _tocNotifier; + // ShowCaseState stores the information of whether the button which is to be + // showcased are clicked or not IBShowCase _showCaseState; final GlobalKey _menu = GlobalKey(); final LocalStorageService _localStorageService = diff --git a/lib/ui/views/ib/ib_page_view.dart b/lib/ui/views/ib/ib_page_view.dart index 36fa2b2f..63537d06 100644 --- a/lib/ui/views/ib/ib_page_view.dart +++ b/lib/ui/views/ib/ib_page_view.dart @@ -88,7 +88,7 @@ class _IbPageViewState extends State { if (!widget.showCase.nextButton) _list.add(_nextPage); if (!widget.showCase.prevButton) _list.add(_prevPage); if (_list.isNotEmpty) { - Future.delayed(const Duration(milliseconds: 500), () { + Future.delayed(const Duration(milliseconds: 300), () { WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { ShowCaseWidget.of(context).startShowCase(_list); diff --git a/test/ui_tests/ib/ib_page_view_test.dart b/test/ui_tests/ib/ib_page_view_test.dart index 40877bb8..e32c9b89 100644 --- a/test/ui_tests/ib/ib_page_view_test.dart +++ b/test/ui_tests/ib/ib_page_view_test.dart @@ -5,6 +5,7 @@ import 'package:mobile_app/locator.dart'; import 'package:mobile_app/models/ib/ib_chapter.dart'; import 'package:mobile_app/models/ib/ib_content.dart'; import 'package:mobile_app/models/ib/ib_page_data.dart'; +import 'package:mobile_app/models/ib/ib_showcase.dart'; import 'package:mobile_app/ui/views/ib/ib_page_view.dart'; import 'package:mobile_app/utils/router.dart'; import 'package:mobile_app/viewmodels/ib/ib_page_viewmodel.dart'; @@ -51,6 +52,13 @@ void main() { navOrder: '1', ); + //Mock ShowCase State + var showCase = IBShowCase( + nextButton: true, + prevButton: true, + contentButton: true, + ); + _chapter.prevPage = _chapter; _chapter.nextPage = _chapter; @@ -59,11 +67,12 @@ void main() { onGenerateRoute: CVRouter.generateRoute, navigatorObservers: [mockObserver], home: IbPageView( - key: UniqueKey(), - chapter: _chapter, - tocCallback: (val) {}, - setPage: (e) {}, - ), + key: UniqueKey(), + chapter: _chapter, + tocCallback: (val) {}, + setPage: (e) {}, + showCase: showCase, + setShowCase: (e) {}), ), ); From 288b933b3375b95961928d762340b0466c647328 Mon Sep 17 00:00:00 2001 From: Aman Date: Sun, 21 Nov 2021 17:19:22 +0530 Subject: [PATCH 03/14] feat: wrap drawer with showcase --- lib/models/ib/ib_showcase.dart | 5 ++++ lib/ui/views/ib/ib_landing_view.dart | 40 ++++++++++++++++++++----- lib/ui/views/ib/ib_page_view.dart | 1 + test/ui_tests/ib/ib_page_view_test.dart | 1 + 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/lib/models/ib/ib_showcase.dart b/lib/models/ib/ib_showcase.dart index ecf32e3c..891a3d14 100644 --- a/lib/models/ib/ib_showcase.dart +++ b/lib/models/ib/ib_showcase.dart @@ -9,6 +9,7 @@ class IBShowCase { @required this.nextButton, @required this.prevButton, @required this.contentButton, + @required this.drawerButton, }); factory IBShowCase.fromJson(Map json) { @@ -16,6 +17,7 @@ class IBShowCase { nextButton: json['next'] ?? false, prevButton: json['prev'] ?? false, contentButton: json['content'] ?? false, + drawerButton: json['drawer'] ?? false, ); } @@ -23,11 +25,13 @@ class IBShowCase { bool nextButton, bool prevButton, bool contentButton, + bool drawerButton, }) { return IBShowCase( nextButton: nextButton ?? this.nextButton, prevButton: prevButton ?? this.prevButton, contentButton: contentButton ?? this.contentButton, + drawerButton: drawerButton ?? this.drawerButton, ); } @@ -37,6 +41,7 @@ class IBShowCase { 'next': nextButton, 'prev': prevButton, 'content': contentButton, + 'drawer': drawerButton, }); } } diff --git a/lib/ui/views/ib/ib_landing_view.dart b/lib/ui/views/ib/ib_landing_view.dart index 2700ff8a..83a0f58e 100644 --- a/lib/ui/views/ib/ib_landing_view.dart +++ b/lib/ui/views/ib/ib_landing_view.dart @@ -33,7 +33,10 @@ class _IbLandingViewState extends State { // ShowCaseState stores the information of whether the button which is to be // showcased are clicked or not IBShowCase _showCaseState; + List _list; final GlobalKey _menu = GlobalKey(); + final GlobalKey _drawer = GlobalKey(); + final GlobalKey _key = GlobalKey(); final LocalStorageService _localStorageService = locator(); @@ -60,6 +63,22 @@ class _IbLandingViewState extends State { Widget _buildAppBar() { return AppBar( + leading: IconButton( + onPressed: () => _key.currentState.openDrawer(), + icon: Showcase( + key: _drawer, + description: 'Navigate to different chapters', + overlayPadding: const EdgeInsets.all(12.0), + onTargetClick: () { + setState(() { + _showCaseState = _showCaseState.copyWith(drawerButton: true); + }); + _key.currentState.openDrawer(); + }, + disposeOnTap: true, + child: const Icon(Icons.menu), + ), + ), title: Text( _selectedChapter.id == _homeChapter.id ? 'CircuitVerse' @@ -69,12 +88,16 @@ class _IbLandingViewState extends State { ValueListenableBuilder( valueListenable: _tocNotifier, builder: (context, value, child) { - if (!_showCaseState.contentButton && - value != null && + if (value != null && ShowCaseWidget.of(context).activeWidgetId == null) { - WidgetsBinding.instance.addPostFrameCallback((_) { - ShowCaseWidget.of(context).startShowCase([_menu]); - }); + _list = []; + if (!_showCaseState.drawerButton) _list.add(_drawer); + if (!_showCaseState.contentButton) _list.add(_menu); + if (_list.isNotEmpty) { + WidgetsBinding.instance.addPostFrameCallback((_) { + ShowCaseWidget.of(context).startShowCase(_list); + }); + } } return value != null @@ -91,7 +114,10 @@ class _IbLandingViewState extends State { contentButton: true, ); }); - value(); + Get.back(); + Future.delayed(const Duration(milliseconds: 200), () { + value(); + }); }, disposeOnTap: true, ) @@ -246,7 +272,7 @@ class _IbLandingViewState extends State { child: Theme( data: IbTheme.getThemeData(context), child: Scaffold( - key: const Key('IbLandingScaffold'), + key: _key, appBar: _buildAppBar(), drawer: _buildDrawer(model), body: PageTransitionSwitcher( diff --git a/lib/ui/views/ib/ib_page_view.dart b/lib/ui/views/ib/ib_page_view.dart index 63537d06..f59b259b 100644 --- a/lib/ui/views/ib/ib_page_view.dart +++ b/lib/ui/views/ib/ib_page_view.dart @@ -87,6 +87,7 @@ class _IbPageViewState extends State { }); if (!widget.showCase.nextButton) _list.add(_nextPage); if (!widget.showCase.prevButton) _list.add(_prevPage); + print(ShowCaseWidget.of(context).activeWidgetId); if (_list.isNotEmpty) { Future.delayed(const Duration(milliseconds: 300), () { WidgetsBinding.instance.addPostFrameCallback((_) { diff --git a/test/ui_tests/ib/ib_page_view_test.dart b/test/ui_tests/ib/ib_page_view_test.dart index e32c9b89..627bd577 100644 --- a/test/ui_tests/ib/ib_page_view_test.dart +++ b/test/ui_tests/ib/ib_page_view_test.dart @@ -57,6 +57,7 @@ void main() { nextButton: true, prevButton: true, contentButton: true, + drawerButton: true, ); _chapter.prevPage = _chapter; From c3d94720e874a4b9bf64d955f13af7d521ff8198 Mon Sep 17 00:00:00 2001 From: Aman Date: Mon, 22 Nov 2021 21:28:40 +0530 Subject: [PATCH 04/14] rfrac: swap `showcase` and `fab` --- lib/ui/views/ib/ib_page_view.dart | 66 ++++++++++++++++--------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/lib/ui/views/ib/ib_page_view.dart b/lib/ui/views/ib/ib_page_view.dart index f59b259b..c54e8a4d 100644 --- a/lib/ui/views/ib/ib_page_view.dart +++ b/lib/ui/views/ib/ib_page_view.dart @@ -381,26 +381,27 @@ class _IbPageViewState extends State { AnimatedOpacity( duration: const Duration(milliseconds: 500), opacity: _isFabsVisible ? 1.0 : 0.0, - child: Showcase( - key: _prevPage, - description: 'Tap to navigate to previous page', - shapeBorder: const CircleBorder(), - onTargetClick: () { - widget.setShowCase(widget.showCase.copyWith(prevButton: true)); + child: FloatingActionButton( + heroTag: 'previousPage', + mini: true, + backgroundColor: Theme.of(context).primaryIconTheme.color, + onPressed: () { + //If FAB are not visible do not do anything. + if (!_isFabsVisible) { + return; + } widget.setPage(widget.chapter.prev); }, - disposeOnTap: true, - child: FloatingActionButton( - heroTag: 'previousPage', - mini: true, - backgroundColor: Theme.of(context).primaryIconTheme.color, - onPressed: () { - //If FAB are not visible do not do anything. - if (!_isFabsVisible) { - return; - } + child: Showcase( + key: _prevPage, + description: 'Tap to navigate to previous page', + overlayPadding: const EdgeInsets.all(12.0), + shapeBorder: const CircleBorder(), + onTargetClick: () { + widget.setShowCase(widget.showCase.copyWith(prevButton: true)); widget.setPage(widget.chapter.prev); }, + disposeOnTap: true, child: const Icon( Icons.arrow_back_rounded, color: IbTheme.primaryColor, @@ -420,26 +421,27 @@ class _IbPageViewState extends State { AnimatedOpacity( duration: const Duration(milliseconds: 500), opacity: _isFabsVisible ? 1.0 : 0.0, - child: Showcase( - key: _nextPage, - description: 'Tap to navigate to next page', - shapeBorder: const CircleBorder(), - onTargetClick: () { - widget.setShowCase(widget.showCase.copyWith(nextButton: true)); + child: FloatingActionButton( + heroTag: 'nextPage', + mini: true, + backgroundColor: Theme.of(context).primaryIconTheme.color, + onPressed: () { + //If FAB are not visible do not do anything. + if (!_isFabsVisible) { + return; + } widget.setPage(widget.chapter.next); }, - disposeOnTap: false, - child: FloatingActionButton( - heroTag: 'nextPage', - mini: true, - backgroundColor: Theme.of(context).primaryIconTheme.color, - onPressed: () { - //If FAB are not visible do not do anything. - if (!_isFabsVisible) { - return; - } + child: Showcase( + key: _nextPage, + description: 'Tap to navigate to next page', + overlayPadding: const EdgeInsets.all(12.0), + shapeBorder: const CircleBorder(), + onTargetClick: () { + widget.setShowCase(widget.showCase.copyWith(nextButton: true)); widget.setPage(widget.chapter.next); }, + disposeOnTap: false, child: const Icon( Icons.arrow_forward_rounded, color: IbTheme.primaryColor, From 58be33ae48f21da52989d84c465486d7ba61f640 Mon Sep 17 00:00:00 2001 From: Aman Date: Tue, 23 Nov 2021 14:20:55 +0530 Subject: [PATCH 05/14] rfrac: update interactive book test --- test/ui_tests/ib/ib_landing_view_test.dart | 7 ++++++- test/ui_tests/ib/ib_page_view_test.dart | 20 +++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/test/ui_tests/ib/ib_landing_view_test.dart b/test/ui_tests/ib/ib_landing_view_test.dart index 6881fe1a..371d6a88 100644 --- a/test/ui_tests/ib/ib_landing_view_test.dart +++ b/test/ui_tests/ib/ib_landing_view_test.dart @@ -10,6 +10,7 @@ import 'package:mobile_app/viewmodels/ib/ib_landing_viewmodel.dart'; import 'package:mobile_app/viewmodels/ib/ib_page_viewmodel.dart'; import 'package:mockito/mockito.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:showcaseview/showcaseview.dart'; import '../../setup/test_helpers.dart'; @@ -62,7 +63,11 @@ void main() { GetMaterialApp( onGenerateRoute: CVRouter.generateRoute, navigatorObservers: [mockObserver], - home: const IbLandingView(), + home: ShowCaseWidget( + builder: Builder(builder: (context) { + return const IbLandingView(); + }), + ), ), ); diff --git a/test/ui_tests/ib/ib_page_view_test.dart b/test/ui_tests/ib/ib_page_view_test.dart index 627bd577..76ae58f6 100644 --- a/test/ui_tests/ib/ib_page_view_test.dart +++ b/test/ui_tests/ib/ib_page_view_test.dart @@ -11,6 +11,7 @@ import 'package:mobile_app/utils/router.dart'; import 'package:mobile_app/viewmodels/ib/ib_page_viewmodel.dart'; import 'package:mockito/mockito.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:showcaseview/showcaseview.dart'; import '../../setup/test_data/mock_ib_raw_page_data.dart'; import '../../setup/test_helpers.dart'; @@ -67,13 +68,18 @@ void main() { GetMaterialApp( onGenerateRoute: CVRouter.generateRoute, navigatorObservers: [mockObserver], - home: IbPageView( - key: UniqueKey(), - chapter: _chapter, - tocCallback: (val) {}, - setPage: (e) {}, - showCase: showCase, - setShowCase: (e) {}), + home: ShowCaseWidget( + builder: Builder(builder: (context) { + return IbPageView( + key: UniqueKey(), + chapter: _chapter, + tocCallback: (val) {}, + setPage: (e) {}, + showCase: showCase, + setShowCase: (e) {}, + ); + }), + ), ), ); From c721fe26afccc0dbad91f1503d06422121a3281d Mon Sep 17 00:00:00 2001 From: Aman Date: Fri, 26 Nov 2021 15:13:39 +0530 Subject: [PATCH 06/14] chor: cleanup --- lib/ui/views/ib/ib_page_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/views/ib/ib_page_view.dart b/lib/ui/views/ib/ib_page_view.dart index c54e8a4d..56df6d53 100644 --- a/lib/ui/views/ib/ib_page_view.dart +++ b/lib/ui/views/ib/ib_page_view.dart @@ -87,7 +87,7 @@ class _IbPageViewState extends State { }); if (!widget.showCase.nextButton) _list.add(_nextPage); if (!widget.showCase.prevButton) _list.add(_prevPage); - print(ShowCaseWidget.of(context).activeWidgetId); + if (_list.isNotEmpty) { Future.delayed(const Duration(milliseconds: 300), () { WidgetsBinding.instance.addPostFrameCallback((_) { From 80eecb9fca21593f2f34c1c0035cdcefed145442 Mon Sep 17 00:00:00 2001 From: Aman Date: Fri, 3 Dec 2021 00:13:04 +0530 Subject: [PATCH 07/14] rfrac: move showcase logic to respective viewmodel --- lib/ui/views/ib/ib_landing_view.dart | 27 +++++------------ lib/ui/views/ib/ib_page_view.dart | 26 ++++------------ lib/viewmodels/ib/ib_landing_viewmodel.dart | 15 ++++++++++ lib/viewmodels/ib/ib_page_viewmodel.dart | 33 +++++++++++++++++++++ test/ui_tests/ib/ib_page_view_test.dart | 1 + 5 files changed, 62 insertions(+), 40 deletions(-) diff --git a/lib/ui/views/ib/ib_landing_view.dart b/lib/ui/views/ib/ib_landing_view.dart index 83a0f58e..f6de22ec 100644 --- a/lib/ui/views/ib/ib_landing_view.dart +++ b/lib/ui/views/ib/ib_landing_view.dart @@ -33,9 +33,7 @@ class _IbLandingViewState extends State { // ShowCaseState stores the information of whether the button which is to be // showcased are clicked or not IBShowCase _showCaseState; - List _list; - final GlobalKey _menu = GlobalKey(); - final GlobalKey _drawer = GlobalKey(); + final GlobalKey _key = GlobalKey(); final LocalStorageService _localStorageService = locator(); @@ -61,12 +59,12 @@ class _IbLandingViewState extends State { } } - Widget _buildAppBar() { + Widget _buildAppBar(IbLandingViewModel _model) { return AppBar( leading: IconButton( onPressed: () => _key.currentState.openDrawer(), icon: Showcase( - key: _drawer, + key: _model.drawer, description: 'Navigate to different chapters', overlayPadding: const EdgeInsets.all(12.0), onTargetClick: () { @@ -88,21 +86,9 @@ class _IbLandingViewState extends State { ValueListenableBuilder( valueListenable: _tocNotifier, builder: (context, value, child) { - if (value != null && - ShowCaseWidget.of(context).activeWidgetId == null) { - _list = []; - if (!_showCaseState.drawerButton) _list.add(_drawer); - if (!_showCaseState.contentButton) _list.add(_menu); - if (_list.isNotEmpty) { - WidgetsBinding.instance.addPostFrameCallback((_) { - ShowCaseWidget.of(context).startShowCase(_list); - }); - } - } - return value != null ? Showcase( - key: _menu, + key: _model.menu, description: 'Show Table of Contents', child: IconButton( icon: const Icon(Icons.menu_book_rounded), @@ -114,7 +100,7 @@ class _IbLandingViewState extends State { contentButton: true, ); }); - Get.back(); + if (_key.currentState.isDrawerOpen) Get.back(); Future.delayed(const Duration(milliseconds: 200), () { value(); }); @@ -273,7 +259,7 @@ class _IbLandingViewState extends State { data: IbTheme.getThemeData(context), child: Scaffold( key: _key, - appBar: _buildAppBar(), + appBar: _buildAppBar(model), drawer: _buildDrawer(model), body: PageTransitionSwitcher( transitionBuilder: ( @@ -304,6 +290,7 @@ class _IbLandingViewState extends State { setState(() => _showCaseState = updatedState); }, showCase: _showCaseState, + globalKeysMap: model.keyMap, ), ), ), diff --git a/lib/ui/views/ib/ib_page_view.dart b/lib/ui/views/ib/ib_page_view.dart index 56df6d53..2c8aa78a 100644 --- a/lib/ui/views/ib/ib_page_view.dart +++ b/lib/ui/views/ib/ib_page_view.dart @@ -43,6 +43,7 @@ class IbPageView extends StatefulWidget { @required this.setPage, @required this.showCase, @required this.setShowCase, + @required this.globalKeysMap, }) : super(key: key); static const String id = 'ib_page_view'; @@ -51,6 +52,7 @@ class IbPageView extends StatefulWidget { final IbChapter chapter; final IBShowCase showCase; final SetShowCaseStateCallback setShowCase; + final Map globalKeysMap; @override _IbPageViewState createState() => _IbPageViewState(); @@ -60,18 +62,13 @@ class _IbPageViewState extends State { IbPageViewModel _model; AutoScrollController _hideButtonController; bool _isFabsVisible = true; - List _list; /// To track index through slug for scroll_to_index final Map _slugMap = {}; - //Global Keys - final GlobalKey _nextPage = GlobalKey(); - final GlobalKey _prevPage = GlobalKey(); - @override void initState() { - _list = []; + // _list = []; super.initState(); _isFabsVisible = true; _hideButtonController = AutoScrollController(axis: Axis.vertical); @@ -85,18 +82,6 @@ class _IbPageViewState extends State { setState(() => _isFabsVisible = true); } }); - if (!widget.showCase.nextButton) _list.add(_nextPage); - if (!widget.showCase.prevButton) _list.add(_prevPage); - - if (_list.isNotEmpty) { - Future.delayed(const Duration(milliseconds: 300), () { - WidgetsBinding.instance.addPostFrameCallback((_) { - if (mounted) { - ShowCaseWidget.of(context).startShowCase(_list); - } - }); - }); - } } Widget _buildDivider() { @@ -393,7 +378,7 @@ class _IbPageViewState extends State { widget.setPage(widget.chapter.prev); }, child: Showcase( - key: _prevPage, + key: _model.prevPage, description: 'Tap to navigate to previous page', overlayPadding: const EdgeInsets.all(12.0), shapeBorder: const CircleBorder(), @@ -433,7 +418,7 @@ class _IbPageViewState extends State { widget.setPage(widget.chapter.next); }, child: Showcase( - key: _nextPage, + key: _model.nextPage, description: 'Tap to navigate to next page', overlayPadding: const EdgeInsets.all(12.0), shapeBorder: const CircleBorder(), @@ -501,6 +486,7 @@ class _IbPageViewState extends State { onModelReady: (model) { _model = model; model.fetchPageData(id: widget.chapter.id); + model.showCase(context, widget.showCase, widget.globalKeysMap); }, builder: (context, model, child) { // Set the callback to show bottom sheet for Table of Contents diff --git a/lib/viewmodels/ib/ib_landing_viewmodel.dart b/lib/viewmodels/ib/ib_landing_viewmodel.dart index 6beb0a40..39d748bd 100644 --- a/lib/viewmodels/ib/ib_landing_viewmodel.dart +++ b/lib/viewmodels/ib/ib_landing_viewmodel.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:mobile_app/enums/view_state.dart'; import 'package:mobile_app/locator.dart'; import 'package:mobile_app/models/failure_model.dart'; @@ -11,6 +12,20 @@ class IbLandingViewModel extends BaseModel { final IbEngineService _ibEngineService = locator(); + // Global Keys + final GlobalKey _menu = GlobalKey(); + final GlobalKey _drawer = GlobalKey(); + + // Getter for Global Keys + GlobalKey get menu => _menu; + GlobalKey get drawer => _drawer; + + // Getter for Global Keys Map + Map get keyMap => { + 'menu': _menu, + 'drawer': _drawer, + }; + List _chapters = []; List get chapters => _chapters; diff --git a/lib/viewmodels/ib/ib_page_viewmodel.dart b/lib/viewmodels/ib/ib_page_viewmodel.dart index da231cf8..e5b179db 100644 --- a/lib/viewmodels/ib/ib_page_viewmodel.dart +++ b/lib/viewmodels/ib/ib_page_viewmodel.dart @@ -1,10 +1,13 @@ +import 'package:flutter/material.dart'; import 'package:mobile_app/enums/view_state.dart'; import 'package:mobile_app/locator.dart'; import 'package:mobile_app/models/failure_model.dart'; import 'package:mobile_app/models/ib/ib_page_data.dart'; import 'package:mobile_app/models/ib/ib_pop_quiz_question.dart'; +import 'package:mobile_app/models/ib/ib_showcase.dart'; import 'package:mobile_app/services/ib_engine_service.dart'; import 'package:mobile_app/viewmodels/base_viewmodel.dart'; +import 'package:showcaseview/showcaseview.dart'; class IbPageViewModel extends BaseModel { // ViewState Keys @@ -12,6 +15,17 @@ class IbPageViewModel extends BaseModel { String IB_FETCH_INTERACTION_DATA = 'ib_fetch_interaction_data'; String IB_FETCH_POP_QUIZ = 'ib_fetch_pop_quiz'; + // List of Global Keys to be Showcase + List _list; + + // Global Keys + final GlobalKey _nextPage = GlobalKey(); + final GlobalKey _prevPage = GlobalKey(); + + // Getter for Global Keys + GlobalKey get nextPage => _nextPage; + GlobalKey get prevPage => _prevPage; + final IbEngineService _ibEngineService = locator(); IbPageData _pageData; @@ -37,6 +51,25 @@ class IbPageViewModel extends BaseModel { } } + void showCase( + BuildContext context, + IBShowCase state, + Map keysMap, + ) { + _list = []; + + if (!state.nextButton) _list.add(_nextPage); + if (!state.prevButton) _list.add(_prevPage); + if (!state.drawerButton) _list.add(keysMap['drawer']); + if (!state.contentButton) _list.add(keysMap['menu']); + + if (_list.isNotEmpty) { + WidgetsBinding.instance.addPostFrameCallback((_) { + ShowCaseWidget.of(context).startShowCase(_list); + }); + } + } + List fetchPopQuiz(String rawContent) { try { var result = _ibEngineService.getPopQuiz(rawContent); diff --git a/test/ui_tests/ib/ib_page_view_test.dart b/test/ui_tests/ib/ib_page_view_test.dart index 76ae58f6..d048f460 100644 --- a/test/ui_tests/ib/ib_page_view_test.dart +++ b/test/ui_tests/ib/ib_page_view_test.dart @@ -77,6 +77,7 @@ void main() { setPage: (e) {}, showCase: showCase, setShowCase: (e) {}, + globalKeysMap: const {}, ); }), ), From 2741444cf70d8d872b21af424bcf8fa12cde3b11 Mon Sep 17 00:00:00 2001 From: Aman Date: Fri, 3 Dec 2021 00:34:08 +0530 Subject: [PATCH 08/14] chor: update contentButton -> tocButton --- lib/models/ib/ib_showcase.dart | 12 ++++++------ lib/ui/views/ib/ib_landing_view.dart | 6 +++--- lib/viewmodels/ib/ib_landing_viewmodel.dart | 6 +++--- lib/viewmodels/ib/ib_page_viewmodel.dart | 2 +- test/ui_tests/ib/ib_page_view_test.dart | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/models/ib/ib_showcase.dart b/lib/models/ib/ib_showcase.dart index 891a3d14..5aa7563c 100644 --- a/lib/models/ib/ib_showcase.dart +++ b/lib/models/ib/ib_showcase.dart @@ -3,12 +3,12 @@ import 'dart:convert'; import 'package:flutter/material.dart'; class IBShowCase { - bool nextButton, prevButton, contentButton, drawerButton; + bool nextButton, prevButton, tocButton, drawerButton; IBShowCase({ @required this.nextButton, @required this.prevButton, - @required this.contentButton, + @required this.tocButton, @required this.drawerButton, }); @@ -16,7 +16,7 @@ class IBShowCase { return IBShowCase( nextButton: json['next'] ?? false, prevButton: json['prev'] ?? false, - contentButton: json['content'] ?? false, + tocButton: json['content'] ?? false, drawerButton: json['drawer'] ?? false, ); } @@ -24,13 +24,13 @@ class IBShowCase { IBShowCase copyWith({ bool nextButton, bool prevButton, - bool contentButton, + bool tocButton, bool drawerButton, }) { return IBShowCase( nextButton: nextButton ?? this.nextButton, prevButton: prevButton ?? this.prevButton, - contentButton: contentButton ?? this.contentButton, + tocButton: tocButton ?? this.tocButton, drawerButton: drawerButton ?? this.drawerButton, ); } @@ -40,7 +40,7 @@ class IBShowCase { return json.encode({ 'next': nextButton, 'prev': prevButton, - 'content': contentButton, + 'toc': tocButton, 'drawer': drawerButton, }); } diff --git a/lib/ui/views/ib/ib_landing_view.dart b/lib/ui/views/ib/ib_landing_view.dart index f6de22ec..a3b19552 100644 --- a/lib/ui/views/ib/ib_landing_view.dart +++ b/lib/ui/views/ib/ib_landing_view.dart @@ -33,7 +33,7 @@ class _IbLandingViewState extends State { // ShowCaseState stores the information of whether the button which is to be // showcased are clicked or not IBShowCase _showCaseState; - + final GlobalKey _key = GlobalKey(); final LocalStorageService _localStorageService = locator(); @@ -88,7 +88,7 @@ class _IbLandingViewState extends State { builder: (context, value, child) { return value != null ? Showcase( - key: _model.menu, + key: _model.toc, description: 'Show Table of Contents', child: IconButton( icon: const Icon(Icons.menu_book_rounded), @@ -97,7 +97,7 @@ class _IbLandingViewState extends State { onTargetClick: () { setState(() { _showCaseState = _showCaseState.copyWith( - contentButton: true, + tocButton: true, ); }); if (_key.currentState.isDrawerOpen) Get.back(); diff --git a/lib/viewmodels/ib/ib_landing_viewmodel.dart b/lib/viewmodels/ib/ib_landing_viewmodel.dart index 39d748bd..96532a91 100644 --- a/lib/viewmodels/ib/ib_landing_viewmodel.dart +++ b/lib/viewmodels/ib/ib_landing_viewmodel.dart @@ -13,16 +13,16 @@ class IbLandingViewModel extends BaseModel { final IbEngineService _ibEngineService = locator(); // Global Keys - final GlobalKey _menu = GlobalKey(); + final GlobalKey _toc = GlobalKey(); final GlobalKey _drawer = GlobalKey(); // Getter for Global Keys - GlobalKey get menu => _menu; + GlobalKey get toc => _toc; GlobalKey get drawer => _drawer; // Getter for Global Keys Map Map get keyMap => { - 'menu': _menu, + 'toc': _toc, 'drawer': _drawer, }; diff --git a/lib/viewmodels/ib/ib_page_viewmodel.dart b/lib/viewmodels/ib/ib_page_viewmodel.dart index e5b179db..dadedf54 100644 --- a/lib/viewmodels/ib/ib_page_viewmodel.dart +++ b/lib/viewmodels/ib/ib_page_viewmodel.dart @@ -61,7 +61,7 @@ class IbPageViewModel extends BaseModel { if (!state.nextButton) _list.add(_nextPage); if (!state.prevButton) _list.add(_prevPage); if (!state.drawerButton) _list.add(keysMap['drawer']); - if (!state.contentButton) _list.add(keysMap['menu']); + if (!state.tocButton) _list.add(keysMap['toc']); if (_list.isNotEmpty) { WidgetsBinding.instance.addPostFrameCallback((_) { diff --git a/test/ui_tests/ib/ib_page_view_test.dart b/test/ui_tests/ib/ib_page_view_test.dart index d048f460..c6488cb3 100644 --- a/test/ui_tests/ib/ib_page_view_test.dart +++ b/test/ui_tests/ib/ib_page_view_test.dart @@ -57,7 +57,7 @@ void main() { var showCase = IBShowCase( nextButton: true, prevButton: true, - contentButton: true, + tocButton: true, drawerButton: true, ); From d4c455f6a791ee579f6cd099286688c8bdd2391d Mon Sep 17 00:00:00 2001 From: Aman Date: Thu, 9 Dec 2021 01:18:37 +0530 Subject: [PATCH 09/14] bfix,ptch: fix test, rename content button -> toc button --- lib/models/ib/ib_showcase.dart | 2 +- lib/ui/views/ib/ib_landing_view.dart | 14 ++++++---- lib/ui/views/ib/ib_page_view.dart | 1 - pubspec.lock | 2 +- pubspec.yaml | 2 +- test/ui_tests/ib/ib_landing_view_test.dart | 4 +++ test/ui_tests/ib/ib_page_view_test.dart | 32 ++++++++++++++++------ 7 files changed, 39 insertions(+), 18 deletions(-) diff --git a/lib/models/ib/ib_showcase.dart b/lib/models/ib/ib_showcase.dart index 5aa7563c..e3995aa4 100644 --- a/lib/models/ib/ib_showcase.dart +++ b/lib/models/ib/ib_showcase.dart @@ -16,7 +16,7 @@ class IBShowCase { return IBShowCase( nextButton: json['next'] ?? false, prevButton: json['prev'] ?? false, - tocButton: json['content'] ?? false, + tocButton: json['toc'] ?? false, drawerButton: json['drawer'] ?? false, ); } diff --git a/lib/ui/views/ib/ib_landing_view.dart b/lib/ui/views/ib/ib_landing_view.dart index a3b19552..9f2e2b71 100644 --- a/lib/ui/views/ib/ib_landing_view.dart +++ b/lib/ui/views/ib/ib_landing_view.dart @@ -30,6 +30,7 @@ class _IbLandingViewState extends State { ); IbChapter _selectedChapter; ValueNotifier _tocNotifier; + IbLandingViewModel _model; // ShowCaseState stores the information of whether the button which is to be // showcased are clicked or not IBShowCase _showCaseState; @@ -59,7 +60,7 @@ class _IbLandingViewState extends State { } } - Widget _buildAppBar(IbLandingViewModel _model) { + Widget _buildAppBar() { return AppBar( leading: IconButton( onPressed: () => _key.currentState.openDrawer(), @@ -178,7 +179,7 @@ class _IbLandingViewState extends State { return Column(children: _chapters); } - Widget _buildDrawer(IbLandingViewModel _model) { + Widget _buildDrawer() { return Drawer( child: Stack( children: [ @@ -237,7 +238,10 @@ class _IbLandingViewState extends State { @override Widget build(BuildContext context) { return BaseView( - onModelReady: (model) => model.fetchChapters(), + onModelReady: (model) { + _model = model; + model.fetchChapters(); + }, builder: (context, model, child) { // Set next page for home page if (model.isSuccess(model.IB_FETCH_CHAPTERS) && @@ -259,8 +263,8 @@ class _IbLandingViewState extends State { data: IbTheme.getThemeData(context), child: Scaffold( key: _key, - appBar: _buildAppBar(model), - drawer: _buildDrawer(model), + appBar: _buildAppBar(), + drawer: _buildDrawer(), body: PageTransitionSwitcher( transitionBuilder: ( Widget child, diff --git a/lib/ui/views/ib/ib_page_view.dart b/lib/ui/views/ib/ib_page_view.dart index 2c8aa78a..89b40196 100644 --- a/lib/ui/views/ib/ib_page_view.dart +++ b/lib/ui/views/ib/ib_page_view.dart @@ -68,7 +68,6 @@ class _IbPageViewState extends State { @override void initState() { - // _list = []; super.initState(); _isFabsVisible = true; _hideButtonController = AutoScrollController(axis: Axis.vertical); diff --git a/pubspec.lock b/pubspec.lock index d1bb90c1..989f42a5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -841,7 +841,7 @@ packages: name: showcaseview url: "https://pub.dartlang.org" source: hosted - version: "1.1.3" + version: "1.1.4" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 93b39e79..43afd125 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,7 +57,7 @@ dependencies: hive_flutter: ^1.1.0 flutter_math_fork: ^0.5.0 scroll_to_index: ^2.0.0 - showcaseview: ^1.1.3 + showcaseview: ^1.1.4 dev_dependencies: flutter_test: diff --git a/test/ui_tests/ib/ib_landing_view_test.dart b/test/ui_tests/ib/ib_landing_view_test.dart index 371d6a88..f183a653 100644 --- a/test/ui_tests/ib/ib_landing_view_test.dart +++ b/test/ui_tests/ib/ib_landing_view_test.dart @@ -58,6 +58,10 @@ void main() { ], ) ]); + when(model.drawer).thenAnswer((_) => GlobalKey()); + when(model.toc).thenAnswer((_) => GlobalKey()); + when(pageViewModel.nextPage).thenAnswer((_) => GlobalKey()); + when(pageViewModel.prevPage).thenAnswer((_) => GlobalKey()); await tester.pumpWidget( GetMaterialApp( diff --git a/test/ui_tests/ib/ib_page_view_test.dart b/test/ui_tests/ib/ib_page_view_test.dart index c6488cb3..602c3d13 100644 --- a/test/ui_tests/ib/ib_page_view_test.dart +++ b/test/ui_tests/ib/ib_page_view_test.dart @@ -16,13 +16,17 @@ import 'package:showcaseview/showcaseview.dart'; import '../../setup/test_data/mock_ib_raw_page_data.dart'; import '../../setup/test_helpers.dart'; +class MockBuildContext extends Mock implements BuildContext {} + void main() { group('IbPageViewTest -', () { NavigatorObserver mockObserver; + MockBuildContext _mockContext; setUpAll(() async { SharedPreferences.setMockInitialValues({}); await setupLocator(); + _mockContext = MockBuildContext(); locator.allowReassignment = true; }); @@ -33,6 +37,17 @@ void main() { var model = MockIbPageViewModel(); locator.registerSingleton(model); + // Mock ShowCase State + var showCase = IBShowCase( + nextButton: true, + prevButton: true, + tocButton: true, + drawerButton: true, + ); + + // Mock Global Key Map + const Map globalKeyMap = {}; + // Mock Page Data when(model.fetchPageData()).thenReturn(null); when(model.isSuccess(model.IB_FETCH_PAGE_DATA)).thenAnswer((_) => true); @@ -45,6 +60,13 @@ void main() { tableOfContents: [], ), ); + when(model.nextPage).thenAnswer((_) => GlobalKey()); + when(model.prevPage).thenAnswer((_) => GlobalKey()); + when(model.showCase( + _mockContext, + showCase, + globalKeyMap, + )).thenReturn(null); // Mock Page Data var _chapter = IbChapter( @@ -53,14 +75,6 @@ void main() { navOrder: '1', ); - //Mock ShowCase State - var showCase = IBShowCase( - nextButton: true, - prevButton: true, - tocButton: true, - drawerButton: true, - ); - _chapter.prevPage = _chapter; _chapter.nextPage = _chapter; @@ -77,7 +91,7 @@ void main() { setPage: (e) {}, showCase: showCase, setShowCase: (e) {}, - globalKeysMap: const {}, + globalKeysMap: globalKeyMap, ); }), ), From 1aa47b300e13dfcf305587ae8f48177c65cb2f4a Mon Sep 17 00:00:00 2001 From: Aman Date: Thu, 9 Dec 2021 01:45:14 +0530 Subject: [PATCH 10/14] chor: remove whitespace --- test/ui_tests/ib/ib_page_view_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ui_tests/ib/ib_page_view_test.dart b/test/ui_tests/ib/ib_page_view_test.dart index 602c3d13..1c525886 100644 --- a/test/ui_tests/ib/ib_page_view_test.dart +++ b/test/ui_tests/ib/ib_page_view_test.dart @@ -66,7 +66,7 @@ void main() { _mockContext, showCase, globalKeyMap, - )).thenReturn(null); + )).thenReturn(null); // Mock Page Data var _chapter = IbChapter( From e788e4c4e00b804721c664f5a1db39368a2084be Mon Sep 17 00:00:00 2001 From: Aman Date: Sun, 12 Dec 2021 18:24:48 +0530 Subject: [PATCH 11/14] bfix: not showcase button mutiple time --- lib/ui/views/ib/ib_landing_view.dart | 117 ++++++++++++++------ lib/utils/router.dart | 9 +- lib/viewmodels/ib/ib_landing_viewmodel.dart | 4 +- lib/viewmodels/ib/ib_page_viewmodel.dart | 4 +- 4 files changed, 86 insertions(+), 48 deletions(-) diff --git a/lib/ui/views/ib/ib_landing_view.dart b/lib/ui/views/ib/ib_landing_view.dart index 9f2e2b71..bc6a9028 100644 --- a/lib/ui/views/ib/ib_landing_view.dart +++ b/lib/ui/views/ib/ib_landing_view.dart @@ -235,6 +235,39 @@ class _IbLandingViewState extends State { ); } + void onShowCased(String key) { + switch (key) { + case 'next': + if (!_showCaseState.nextButton) { + setState(() { + _showCaseState = _showCaseState.copyWith(nextButton: true); + }); + } + break; + case 'prev': + if (!_showCaseState.prevButton) { + setState(() { + _showCaseState = _showCaseState.copyWith(prevButton: true); + }); + } + break; + case 'toc': + if (!_showCaseState.tocButton) { + setState(() { + _showCaseState = _showCaseState.copyWith(tocButton: true); + }); + } + break; + case 'drawer': + if (!_showCaseState.drawerButton) { + setState(() { + _showCaseState = _showCaseState.copyWith(drawerButton: true); + }); + } + break; + } + } + @override Widget build(BuildContext context) { return BaseView( @@ -261,42 +294,54 @@ class _IbLandingViewState extends State { }, child: Theme( data: IbTheme.getThemeData(context), - child: Scaffold( - key: _key, - appBar: _buildAppBar(), - drawer: _buildDrawer(), - body: PageTransitionSwitcher( - transitionBuilder: ( - Widget child, - Animation animation, - Animation secondaryAnimation, - ) { - return FadeThroughTransition( - animation: animation, - secondaryAnimation: secondaryAnimation, - child: child, - ); - }, - child: IbPageView( - key: Key(_selectedChapter.toString()), - tocCallback: (val) { - Future.delayed(Duration.zero, () async { - if (mounted) { - _tocNotifier.value = val; - } - }); - }, - setPage: (chapter) { - setState(() => _selectedChapter = chapter); - }, - chapter: _selectedChapter, - setShowCase: (updatedState) { - setState(() => _showCaseState = updatedState); - }, - showCase: _showCaseState, - globalKeysMap: model.keyMap, - ), - ), + child: ShowCaseWidget( + onComplete: (index, globalKey) { + final String key = globalKey + .toString() + .substring(1, globalKey.toString().length - 1) + .split(" ") + .last; + onShowCased(key); + }, + builder: Builder(builder: (context) { + return Scaffold( + key: _key, + appBar: _buildAppBar(), + drawer: _buildDrawer(), + body: PageTransitionSwitcher( + transitionBuilder: ( + Widget child, + Animation animation, + Animation secondaryAnimation, + ) { + return FadeThroughTransition( + animation: animation, + secondaryAnimation: secondaryAnimation, + child: child, + ); + }, + child: IbPageView( + key: Key(_selectedChapter.toString()), + tocCallback: (val) { + Future.delayed(Duration.zero, () async { + if (mounted) { + _tocNotifier.value = val; + } + }); + }, + setPage: (chapter) { + setState(() => _selectedChapter = chapter); + }, + chapter: _selectedChapter, + setShowCase: (updatedState) { + setState(() => _showCaseState = updatedState); + }, + showCase: _showCaseState, + globalKeysMap: model.keyMap, + ), + ), + ); + }), ), ), ); diff --git a/lib/utils/router.dart b/lib/utils/router.dart index 5030c256..f5b3eb8c 100644 --- a/lib/utils/router.dart +++ b/lib/utils/router.dart @@ -24,7 +24,6 @@ import 'package:mobile_app/ui/views/projects/featured_projects_view.dart'; import 'package:mobile_app/ui/views/projects/project_details_view.dart'; import 'package:mobile_app/ui/views/projects/project_preview_fullscreen_view.dart'; import 'package:mobile_app/ui/views/teachers/teachers_view.dart'; -import 'package:showcaseview/showcaseview.dart'; class CVRouter { static Route generateRoute(RouteSettings settings) { @@ -112,13 +111,7 @@ class CVRouter { ); case IbLandingView.id: return MaterialPageRoute( - builder: (_) => ShowCaseWidget( - builder: Builder( - builder: (context) { - return const IbLandingView(); - }, - ), - ), + builder: (_) => const IbLandingView(), ); case ProjectPreviewFullScreen.id: var _project = settings.arguments as Project; diff --git a/lib/viewmodels/ib/ib_landing_viewmodel.dart b/lib/viewmodels/ib/ib_landing_viewmodel.dart index 96532a91..ea79f0ff 100644 --- a/lib/viewmodels/ib/ib_landing_viewmodel.dart +++ b/lib/viewmodels/ib/ib_landing_viewmodel.dart @@ -13,8 +13,8 @@ class IbLandingViewModel extends BaseModel { final IbEngineService _ibEngineService = locator(); // Global Keys - final GlobalKey _toc = GlobalKey(); - final GlobalKey _drawer = GlobalKey(); + final GlobalKey _toc = GlobalKey(debugLabel: 'toc'); + final GlobalKey _drawer = GlobalKey(debugLabel: 'drawer'); // Getter for Global Keys GlobalKey get toc => _toc; diff --git a/lib/viewmodels/ib/ib_page_viewmodel.dart b/lib/viewmodels/ib/ib_page_viewmodel.dart index dadedf54..733f7ea7 100644 --- a/lib/viewmodels/ib/ib_page_viewmodel.dart +++ b/lib/viewmodels/ib/ib_page_viewmodel.dart @@ -19,8 +19,8 @@ class IbPageViewModel extends BaseModel { List _list; // Global Keys - final GlobalKey _nextPage = GlobalKey(); - final GlobalKey _prevPage = GlobalKey(); + final GlobalKey _nextPage = GlobalKey(debugLabel: 'next'); + final GlobalKey _prevPage = GlobalKey(debugLabel: 'prev'); // Getter for Global Keys GlobalKey get nextPage => _nextPage; From 53454df9cb63afdc50ce07ecb9bd78f77d76e2c4 Mon Sep 17 00:00:00 2001 From: Aman Date: Tue, 21 Dec 2021 03:07:07 +0530 Subject: [PATCH 12/14] rfac: move showcase state logic in viewmodel --- lib/ui/views/ib/ib_landing_view.dart | 69 ++++----------------- lib/viewmodels/ib/ib_landing_viewmodel.dart | 50 +++++++++++++++ 2 files changed, 63 insertions(+), 56 deletions(-) diff --git a/lib/ui/views/ib/ib_landing_view.dart b/lib/ui/views/ib/ib_landing_view.dart index bc6a9028..19b358cf 100644 --- a/lib/ui/views/ib/ib_landing_view.dart +++ b/lib/ui/views/ib/ib_landing_view.dart @@ -2,10 +2,7 @@ import 'package:animations/animations.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:mobile_app/ib_theme.dart'; -import 'package:mobile_app/locator.dart'; import 'package:mobile_app/models/ib/ib_chapter.dart'; -import 'package:mobile_app/models/ib/ib_showcase.dart'; -import 'package:mobile_app/services/local_storage_service.dart'; import 'package:mobile_app/ui/components/cv_drawer_tile.dart'; import 'package:mobile_app/ui/views/base_view.dart'; import 'package:mobile_app/ui/views/ib/ib_page_view.dart'; @@ -31,19 +28,13 @@ class _IbLandingViewState extends State { IbChapter _selectedChapter; ValueNotifier _tocNotifier; IbLandingViewModel _model; - // ShowCaseState stores the information of whether the button which is to be - // showcased are clicked or not - IBShowCase _showCaseState; final GlobalKey _key = GlobalKey(); - final LocalStorageService _localStorageService = - locator(); @override void initState() { _tocNotifier = ValueNotifier(null); _selectedChapter = _homeChapter; - _showCaseState = IBShowCase.fromJson(_localStorageService.getShowcaseState); super.initState(); } @@ -63,15 +54,18 @@ class _IbLandingViewState extends State { Widget _buildAppBar() { return AppBar( leading: IconButton( - onPressed: () => _key.currentState.openDrawer(), + onPressed: () { + if (!_model.showCaseState.drawerButton) { + _model.onShowCased('drawer'); + } + _key.currentState.openDrawer(); + }, icon: Showcase( key: _model.drawer, description: 'Navigate to different chapters', overlayPadding: const EdgeInsets.all(12.0), onTargetClick: () { - setState(() { - _showCaseState = _showCaseState.copyWith(drawerButton: true); - }); + _model.onShowCased('drawer'); _key.currentState.openDrawer(); }, disposeOnTap: true, @@ -96,11 +90,7 @@ class _IbLandingViewState extends State { onPressed: value, ), onTargetClick: () { - setState(() { - _showCaseState = _showCaseState.copyWith( - tocButton: true, - ); - }); + _model.onShowCased('toc'); if (_key.currentState.isDrawerOpen) Get.back(); Future.delayed(const Duration(milliseconds: 200), () { value(); @@ -235,45 +225,12 @@ class _IbLandingViewState extends State { ); } - void onShowCased(String key) { - switch (key) { - case 'next': - if (!_showCaseState.nextButton) { - setState(() { - _showCaseState = _showCaseState.copyWith(nextButton: true); - }); - } - break; - case 'prev': - if (!_showCaseState.prevButton) { - setState(() { - _showCaseState = _showCaseState.copyWith(prevButton: true); - }); - } - break; - case 'toc': - if (!_showCaseState.tocButton) { - setState(() { - _showCaseState = _showCaseState.copyWith(tocButton: true); - }); - } - break; - case 'drawer': - if (!_showCaseState.drawerButton) { - setState(() { - _showCaseState = _showCaseState.copyWith(drawerButton: true); - }); - } - break; - } - } - @override Widget build(BuildContext context) { return BaseView( onModelReady: (model) { _model = model; - model.fetchChapters(); + model.init(); }, builder: (context, model, child) { // Set next page for home page @@ -289,7 +246,7 @@ class _IbLandingViewState extends State { setState(() => _selectedChapter = _homeChapter); return Future.value(false); } - _localStorageService.setShowcaseState = _showCaseState.toString(); + _model.saveShowcaseState(); return Future.value(true); }, child: Theme( @@ -301,7 +258,7 @@ class _IbLandingViewState extends State { .substring(1, globalKey.toString().length - 1) .split(" ") .last; - onShowCased(key); + model.onShowCased(key); }, builder: Builder(builder: (context) { return Scaffold( @@ -334,9 +291,9 @@ class _IbLandingViewState extends State { }, chapter: _selectedChapter, setShowCase: (updatedState) { - setState(() => _showCaseState = updatedState); + model.showCaseState = updatedState; }, - showCase: _showCaseState, + showCase: model.showCaseState, globalKeysMap: model.keyMap, ), ), diff --git a/lib/viewmodels/ib/ib_landing_viewmodel.dart b/lib/viewmodels/ib/ib_landing_viewmodel.dart index ea79f0ff..9496d425 100644 --- a/lib/viewmodels/ib/ib_landing_viewmodel.dart +++ b/lib/viewmodels/ib/ib_landing_viewmodel.dart @@ -3,7 +3,9 @@ import 'package:mobile_app/enums/view_state.dart'; import 'package:mobile_app/locator.dart'; import 'package:mobile_app/models/failure_model.dart'; import 'package:mobile_app/models/ib/ib_chapter.dart'; +import 'package:mobile_app/models/ib/ib_showcase.dart'; import 'package:mobile_app/services/ib_engine_service.dart'; +import 'package:mobile_app/services/local_storage_service.dart'; import 'package:mobile_app/viewmodels/base_viewmodel.dart'; class IbLandingViewModel extends BaseModel { @@ -11,6 +13,8 @@ class IbLandingViewModel extends BaseModel { String IB_FETCH_CHAPTERS = 'ib_fetch_chapters'; final IbEngineService _ibEngineService = locator(); + final LocalStorageService _localStorageService = + locator(); // Global Keys final GlobalKey _toc = GlobalKey(debugLabel: 'toc'); @@ -30,6 +34,52 @@ class IbLandingViewModel extends BaseModel { List get chapters => _chapters; + // ShowCaseState stores the information of whether the button which is to be + // showcased are clicked or not + IBShowCase _showCaseState; + + IBShowCase get showCaseState => _showCaseState; + + set showCaseState(IBShowCase updatedState) { + _showCaseState = updatedState; + notifyListeners(); + } + + void onShowCased(String key) { + switch (key) { + case 'next': + if (!_showCaseState.nextButton) { + _showCaseState = _showCaseState.copyWith(nextButton: true); + } + break; + case 'prev': + if (!_showCaseState.prevButton) { + _showCaseState = _showCaseState.copyWith(prevButton: true); + } + break; + case 'toc': + if (!_showCaseState.tocButton) { + _showCaseState = _showCaseState.copyWith(tocButton: true); + } + break; + case 'drawer': + if (!_showCaseState.drawerButton) { + _showCaseState = _showCaseState.copyWith(drawerButton: true); + } + break; + } + notifyListeners(); + } + + void saveShowcaseState() { + _localStorageService.setShowcaseState = _showCaseState.toString(); + } + + void init() { + _showCaseState = IBShowCase.fromJson(_localStorageService.getShowcaseState); + fetchChapters(); + } + Future fetchChapters() async { try { _chapters = await _ibEngineService.getChapters(); From a0dc5738e95ed3a23d2f01082a4bd79114341a7d Mon Sep 17 00:00:00 2001 From: Aman Date: Wed, 22 Dec 2021 01:27:19 +0530 Subject: [PATCH 13/14] bfix: delay showcase --- lib/ui/views/ib/ib_page_view.dart | 14 +++++++++++++- lib/viewmodels/ib/ib_page_viewmodel.dart | 6 ++++-- test/ui_tests/ib/ib_page_view_test.dart | 9 --------- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/ui/views/ib/ib_page_view.dart b/lib/ui/views/ib/ib_page_view.dart index 89b40196..a92b15ac 100644 --- a/lib/ui/views/ib/ib_page_view.dart +++ b/lib/ui/views/ib/ib_page_view.dart @@ -62,6 +62,7 @@ class _IbPageViewState extends State { IbPageViewModel _model; AutoScrollController _hideButtonController; bool _isFabsVisible = true; + ShowCaseWidgetState _showCaseWidgetState; /// To track index through slug for scroll_to_index final Map _slugMap = {}; @@ -69,6 +70,7 @@ class _IbPageViewState extends State { @override void initState() { super.initState(); + _showCaseWidgetState = ShowCaseWidget.of(context); _isFabsVisible = true; _hideButtonController = AutoScrollController(axis: Axis.vertical); _hideButtonController.addListener(() { @@ -83,6 +85,12 @@ class _IbPageViewState extends State { }); } + @override + void didChangeDependencies() { + _showCaseWidgetState = ShowCaseWidget.of(context); + super.didChangeDependencies(); + } + Widget _buildDivider() { return const Padding( padding: EdgeInsets.symmetric(vertical: 10), @@ -485,7 +493,11 @@ class _IbPageViewState extends State { onModelReady: (model) { _model = model; model.fetchPageData(id: widget.chapter.id); - model.showCase(context, widget.showCase, widget.globalKeysMap); + model.showCase( + _showCaseWidgetState, + widget.showCase, + widget.globalKeysMap, + ); }, builder: (context, model, child) { // Set the callback to show bottom sheet for Table of Contents diff --git a/lib/viewmodels/ib/ib_page_viewmodel.dart b/lib/viewmodels/ib/ib_page_viewmodel.dart index 733f7ea7..49ecb575 100644 --- a/lib/viewmodels/ib/ib_page_viewmodel.dart +++ b/lib/viewmodels/ib/ib_page_viewmodel.dart @@ -52,7 +52,7 @@ class IbPageViewModel extends BaseModel { } void showCase( - BuildContext context, + ShowCaseWidgetState showCaseWidgetState, IBShowCase state, Map keysMap, ) { @@ -65,7 +65,9 @@ class IbPageViewModel extends BaseModel { if (_list.isNotEmpty) { WidgetsBinding.instance.addPostFrameCallback((_) { - ShowCaseWidget.of(context).startShowCase(_list); + Future.delayed(const Duration(milliseconds: 800), () { + showCaseWidgetState.startShowCase(_list); + }); }); } } diff --git a/test/ui_tests/ib/ib_page_view_test.dart b/test/ui_tests/ib/ib_page_view_test.dart index 1c525886..3f69361a 100644 --- a/test/ui_tests/ib/ib_page_view_test.dart +++ b/test/ui_tests/ib/ib_page_view_test.dart @@ -16,17 +16,13 @@ import 'package:showcaseview/showcaseview.dart'; import '../../setup/test_data/mock_ib_raw_page_data.dart'; import '../../setup/test_helpers.dart'; -class MockBuildContext extends Mock implements BuildContext {} - void main() { group('IbPageViewTest -', () { NavigatorObserver mockObserver; - MockBuildContext _mockContext; setUpAll(() async { SharedPreferences.setMockInitialValues({}); await setupLocator(); - _mockContext = MockBuildContext(); locator.allowReassignment = true; }); @@ -62,11 +58,6 @@ void main() { ); when(model.nextPage).thenAnswer((_) => GlobalKey()); when(model.prevPage).thenAnswer((_) => GlobalKey()); - when(model.showCase( - _mockContext, - showCase, - globalKeyMap, - )).thenReturn(null); // Mock Page Data var _chapter = IbChapter( From e01e4e7274578882035bca3a5f4fab1b62913b93 Mon Sep 17 00:00:00 2001 From: Aman Date: Wed, 22 Dec 2021 12:33:37 +0530 Subject: [PATCH 14/14] bfix: save the state after every showcase --- lib/viewmodels/ib/ib_landing_viewmodel.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/viewmodels/ib/ib_landing_viewmodel.dart b/lib/viewmodels/ib/ib_landing_viewmodel.dart index 9496d425..a9f873b2 100644 --- a/lib/viewmodels/ib/ib_landing_viewmodel.dart +++ b/lib/viewmodels/ib/ib_landing_viewmodel.dart @@ -50,21 +50,25 @@ class IbLandingViewModel extends BaseModel { case 'next': if (!_showCaseState.nextButton) { _showCaseState = _showCaseState.copyWith(nextButton: true); + saveShowcaseState(); } break; case 'prev': if (!_showCaseState.prevButton) { _showCaseState = _showCaseState.copyWith(prevButton: true); + saveShowcaseState(); } break; case 'toc': if (!_showCaseState.tocButton) { _showCaseState = _showCaseState.copyWith(tocButton: true); + saveShowcaseState(); } break; case 'drawer': if (!_showCaseState.drawerButton) { _showCaseState = _showCaseState.copyWith(drawerButton: true); + saveShowcaseState(); } break; }