Skip to content

Commit

Permalink
Merge pull request #170 from aman-singh7/ib_showcase
Browse files Browse the repository at this point in the history
ShowCaseView on Interactive Book
  • Loading branch information
manjotsidhu authored Dec 22, 2021
2 parents fb7cca5 + e01e4e7 commit 0970ada
Show file tree
Hide file tree
Showing 11 changed files with 354 additions and 50 deletions.
47 changes: 47 additions & 0 deletions lib/models/ib/ib_showcase.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import 'dart:convert';

import 'package:flutter/material.dart';

class IBShowCase {
bool nextButton, prevButton, tocButton, drawerButton;

IBShowCase({
@required this.nextButton,
@required this.prevButton,
@required this.tocButton,
@required this.drawerButton,
});

factory IBShowCase.fromJson(Map<String, dynamic> json) {
return IBShowCase(
nextButton: json['next'] ?? false,
prevButton: json['prev'] ?? false,
tocButton: json['toc'] ?? false,
drawerButton: json['drawer'] ?? false,
);
}

IBShowCase copyWith({
bool nextButton,
bool prevButton,
bool tocButton,
bool drawerButton,
}) {
return IBShowCase(
nextButton: nextButton ?? this.nextButton,
prevButton: prevButton ?? this.prevButton,
tocButton: tocButton ?? this.tocButton,
drawerButton: drawerButton ?? this.drawerButton,
);
}

@override
String toString() {
return json.encode({
'next': nextButton,
'prev': prevButton,
'toc': tocButton,
'drawer': drawerButton,
});
}
}
13 changes: 13 additions & 0 deletions lib/services/local_storage_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<LocalStorageService> getInstance() async {
_preferences ??= await SharedPreferences.getInstance();
Expand Down Expand Up @@ -86,4 +87,16 @@ class LocalStorageService {
set authType(AuthType authType) {
_saveToDisk(AUTH_TYPE, authTypeValues.reverse[authType]);
}

Map<String, dynamic> get getShowcaseState {
final Map<String, dynamic> result =
Map.castFrom<dynamic, dynamic, String, dynamic>(
json.decode(_getFromDisk(IB_SHOWCASE_STATE) ?? '{}')
as Map<dynamic, dynamic>);
return result;
}

set setShowcaseState(String state) {
_saveToDisk(IB_SHOWCASE_STATE, state);
}
}
129 changes: 92 additions & 37 deletions lib/ui/views/ib/ib_landing_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ 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 {
Expand All @@ -26,6 +27,9 @@ class _IbLandingViewState extends State<IbLandingView> {
);
IbChapter _selectedChapter;
ValueNotifier<Function> _tocNotifier;
IbLandingViewModel _model;

final GlobalKey<ScaffoldState> _key = GlobalKey();

@override
void initState() {
Expand All @@ -49,6 +53,25 @@ class _IbLandingViewState extends State<IbLandingView> {

Widget _buildAppBar() {
return AppBar(
leading: IconButton(
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: () {
_model.onShowCased('drawer');
_key.currentState.openDrawer();
},
disposeOnTap: true,
child: const Icon(Icons.menu),
),
),
title: Text(
_selectedChapter.id == _homeChapter.id
? 'CircuitVerse'
Expand All @@ -59,10 +82,21 @@ class _IbLandingViewState extends State<IbLandingView> {
valueListenable: _tocNotifier,
builder: (context, value, child) {
return value != null
? IconButton(
icon: const Icon(Icons.menu_book_rounded),
tooltip: 'Show Table of Contents',
onPressed: value,
? Showcase(
key: _model.toc,
description: 'Show Table of Contents',
child: IconButton(
icon: const Icon(Icons.menu_book_rounded),
onPressed: value,
),
onTargetClick: () {
_model.onShowCased('toc');
if (_key.currentState.isDrawerOpen) Get.back();
Future.delayed(const Duration(milliseconds: 200), () {
value();
});
},
disposeOnTap: true,
)
: Container();
},
Expand Down Expand Up @@ -135,7 +169,7 @@ class _IbLandingViewState extends State<IbLandingView> {
return Column(children: _chapters);
}

Widget _buildDrawer(IbLandingViewModel _model) {
Widget _buildDrawer() {
return Drawer(
child: Stack(
children: [
Expand Down Expand Up @@ -194,7 +228,10 @@ class _IbLandingViewState extends State<IbLandingView> {
@override
Widget build(BuildContext context) {
return BaseView<IbLandingViewModel>(
onModelReady: (model) => model.fetchChapters(),
onModelReady: (model) {
_model = model;
model.init();
},
builder: (context, model, child) {
// Set next page for home page
if (model.isSuccess(model.IB_FETCH_CHAPTERS) &&
Expand All @@ -209,41 +246,59 @@ class _IbLandingViewState extends State<IbLandingView> {
setState(() => _selectedChapter = _homeChapter);
return Future.value(false);
}
_model.saveShowcaseState();
return Future.value(true);
},
child: Theme(
data: IbTheme.getThemeData(context),
child: Scaffold(
key: const Key('IbLandingScaffold'),
appBar: _buildAppBar(),
drawer: _buildDrawer(model),
body: PageTransitionSwitcher(
transitionBuilder: (
Widget child,
Animation<double> animation,
Animation<double> 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,
),
),
child: ShowCaseWidget(
onComplete: (index, globalKey) {
final String key = globalKey
.toString()
.substring(1, globalKey.toString().length - 1)
.split(" ")
.last;
model.onShowCased(key);
},
builder: Builder(builder: (context) {
return Scaffold(
key: _key,
appBar: _buildAppBar(),
drawer: _buildDrawer(),
body: PageTransitionSwitcher(
transitionBuilder: (
Widget child,
Animation<double> animation,
Animation<double> 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) {
model.showCaseState = updatedState;
},
showCase: model.showCaseState,
globalKeysMap: model.keyMap,
),
),
);
}),
),
),
);
Expand Down
56 changes: 50 additions & 6 deletions lib/ui/views/ib/ib_page_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -27,23 +28,31 @@ 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({
@required Key key,
@required this.tocCallback,
@required this.chapter,
@required this.setPage,
@required this.showCase,
@required this.setShowCase,
@required this.globalKeysMap,
}) : 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;
final Map<String, dynamic> globalKeysMap;

@override
_IbPageViewState createState() => _IbPageViewState();
Expand All @@ -53,13 +62,15 @@ class _IbPageViewState extends State<IbPageView> {
IbPageViewModel _model;
AutoScrollController _hideButtonController;
bool _isFabsVisible = true;
ShowCaseWidgetState _showCaseWidgetState;

/// To track index through slug for scroll_to_index
final Map<String, int> _slugMap = {};

@override
void initState() {
super.initState();
_showCaseWidgetState = ShowCaseWidget.of(context);
_isFabsVisible = true;
_hideButtonController = AutoScrollController(axis: Axis.vertical);
_hideButtonController.addListener(() {
Expand All @@ -74,6 +85,12 @@ class _IbPageViewState extends State<IbPageView> {
});
}

@override
void didChangeDependencies() {
_showCaseWidgetState = ShowCaseWidget.of(context);
super.didChangeDependencies();
}

Widget _buildDivider() {
return const Padding(
padding: EdgeInsets.symmetric(vertical: 10),
Expand Down Expand Up @@ -367,9 +384,20 @@ class _IbPageViewState extends State<IbPageView> {
}
widget.setPage(widget.chapter.prev);
},
child: const Icon(
Icons.arrow_back_rounded,
color: IbTheme.primaryColor,
child: Showcase(
key: _model.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,
),
),
),
),
Expand All @@ -396,9 +424,20 @@ class _IbPageViewState extends State<IbPageView> {
}
widget.setPage(widget.chapter.next);
},
child: const Icon(
Icons.arrow_forward_rounded,
color: IbTheme.primaryColor,
child: Showcase(
key: _model.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,
),
),
),
),
Expand Down Expand Up @@ -454,6 +493,11 @@ class _IbPageViewState extends State<IbPageView> {
onModelReady: (model) {
_model = model;
model.fetchPageData(id: widget.chapter.id);
model.showCase(
_showCaseWidgetState,
widget.showCase,
widget.globalKeysMap,
);
},
builder: (context, model, child) {
// Set the callback to show bottom sheet for Table of Contents
Expand Down
4 changes: 3 additions & 1 deletion lib/utils/router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,9 @@ class CVRouter {
),
);
case IbLandingView.id:
return MaterialPageRoute(builder: (_) => const IbLandingView());
return MaterialPageRoute(
builder: (_) => const IbLandingView(),
);
case ProjectPreviewFullScreen.id:
var _project = settings.arguments as Project;
return MaterialPageRoute(
Expand Down
Loading

0 comments on commit 0970ada

Please sign in to comment.