-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(#259): add tutorial components and initial setup instruction
- Loading branch information
Showing
10 changed files
with
254 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import 'dart:async'; | ||
|
||
import '../module.dart'; | ||
|
||
enum ButtonDirection { forward, backward } | ||
|
||
class DirectionButton extends StatelessWidget { | ||
const DirectionButton({ | ||
super.key, | ||
required this.direction, | ||
required this.text, | ||
required this.onPressed, | ||
this.emphasize = false, | ||
this.onDarkBackground = false, | ||
}); | ||
|
||
final ButtonDirection direction; | ||
final String text; | ||
final FutureOr<void> Function() onPressed; | ||
final bool emphasize; | ||
final bool onDarkBackground; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
const lightColor = Colors.white; | ||
const darkColor = PharMeTheme.onSurfaceText; | ||
final buttonStyle = emphasize | ||
? ElevatedButton.styleFrom( | ||
backgroundColor: onDarkBackground ? lightColor : darkColor, | ||
) | ||
: null; | ||
final textColor = emphasize == onDarkBackground | ||
? PharMeTheme.onSurfaceText | ||
: Colors.white; | ||
final separator = SizedBox(width: 8); | ||
final iconData = direction == ButtonDirection.forward | ||
? Icons.arrow_forward_rounded | ||
: Icons.arrow_back_rounded; | ||
final icon = Icon( | ||
iconData, | ||
color: textColor, | ||
size: 32, | ||
); | ||
final buttonText = Text( | ||
text, | ||
style: PharMeTheme.textTheme.titleLarge!.copyWith(color: textColor), | ||
); | ||
final buttonContent = direction == ButtonDirection.forward | ||
? [ separator, buttonText, separator, icon ] | ||
: [ icon, separator, buttonText, separator ]; | ||
return TextButton( | ||
style: buttonStyle, | ||
onPressed: onPressed, | ||
child: Row( | ||
mainAxisAlignment: MainAxisAlignment.center, | ||
mainAxisSize: MainAxisSize.min, | ||
children: buttonContent, | ||
), | ||
); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import 'dart:async'; | ||
|
||
import '../../module.dart'; | ||
|
||
class TutorialContainer extends HookWidget { | ||
const TutorialContainer({ | ||
super.key, | ||
required this.pages, | ||
required this.finishTutorial, | ||
this.lastNextButtonText, | ||
}); | ||
|
||
final List<TutorialContent> pages; | ||
final Future<void> Function() finishTutorial; | ||
final String? lastNextButtonText; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
final currentPageIndex = useState(0); | ||
return Padding( | ||
padding: EdgeInsets.only( | ||
left: PharMeTheme.largeSpace, | ||
bottom: PharMeTheme.largeSpace, | ||
right: PharMeTheme.largeSpace, | ||
), | ||
child: Column( | ||
mainAxisSize: MainAxisSize.min, | ||
crossAxisAlignment: CrossAxisAlignment.start, | ||
children: _buildPageContent(context, currentPageIndex), | ||
), | ||
); | ||
} | ||
|
||
List<Widget> _buildPageContent( | ||
BuildContext context, | ||
ValueNotifier<int> currentPageIndex, | ||
) { | ||
final currentPage = pages[currentPageIndex.value]; | ||
final title = currentPage.title != null | ||
? currentPage.title!(context) | ||
: null; | ||
final content = currentPage.content != null | ||
? currentPage.content!(context) | ||
: null; | ||
final asset = currentPage.assetPath != null | ||
? Image.asset(currentPage.assetPath!) | ||
: null; | ||
return [ | ||
if (title != null) Text( | ||
title, | ||
style: PharMeTheme.textTheme.headlineMedium!.copyWith( | ||
fontSize: PharMeTheme.textTheme.headlineSmall!.fontSize, | ||
), | ||
), | ||
if (content != null || asset != null) Padding( | ||
padding: EdgeInsetsDirectional.only(top: PharMeTheme.mediumSpace), | ||
child: Expanded( | ||
child: Center(child: SingleChildScrollView( | ||
child: Column( | ||
mainAxisAlignment: MainAxisAlignment.center, | ||
mainAxisSize: MainAxisSize.max, | ||
children: [ | ||
if (content != null) | ||
Text.rich(content, style: PharMeTheme.textTheme.bodyLarge), | ||
if (asset != null) asset, | ||
], | ||
), | ||
)), | ||
), | ||
), | ||
Padding( | ||
padding: EdgeInsetsDirectional.only(top: PharMeTheme.mediumSpace), | ||
child: _buildActionBar(context, currentPageIndex), | ||
), | ||
]; | ||
} | ||
|
||
Widget _buildActionBar( | ||
BuildContext context, | ||
ValueNotifier<int> currentPageIndex, | ||
) { | ||
final isFirstPage = currentPageIndex.value == 0; | ||
final isLastPage = currentPageIndex.value == pages.length - 1; | ||
return Row( | ||
mainAxisAlignment: MainAxisAlignment.end, | ||
children: [ | ||
if (!isFirstPage) DirectionButton( | ||
direction: ButtonDirection.backward, | ||
onPressed: () => currentPageIndex.value = currentPageIndex.value - 1, | ||
text: context.l10n.onboarding_prev, | ||
), | ||
DirectionButton( | ||
direction: ButtonDirection.forward, | ||
onPressed: isLastPage | ||
? finishTutorial | ||
: () => currentPageIndex.value = currentPageIndex.value + 1, | ||
|
||
text: isLastPage && lastNextButtonText != null | ||
? lastNextButtonText! | ||
: context.l10n.action_continue, | ||
emphasize: isLastPage, | ||
), | ||
], | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import '../../module.dart'; | ||
|
||
class TutorialContent { | ||
TutorialContent({ | ||
this.title, | ||
this.content, | ||
this.assetPath, | ||
}); | ||
|
||
final String Function(BuildContext)? title; | ||
final TextSpan Function(BuildContext)? content; | ||
final String? assetPath; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import '../../module.dart'; | ||
import 'container.dart'; | ||
|
||
Future<void> showTutorial({ | ||
required BuildContext context, | ||
required List<TutorialContent> pages, | ||
required bool concludesWholeTutorial, | ||
}) => showModalBottomSheet( | ||
context: context, | ||
enableDrag: true, | ||
showDragHandle: true, | ||
isDismissible: false, | ||
isScrollControlled: true, | ||
useSafeArea: true, | ||
elevation: 0, | ||
builder: (context) => TutorialContainer( | ||
pages: pages, | ||
lastNextButtonText: concludesWholeTutorial | ||
? context.l10n.tutorial_to_the_app | ||
: null, | ||
finishTutorial: () async { | ||
final closeTutorial = Navigator.of(context).pop; | ||
if (concludesWholeTutorial) { | ||
MetaData.instance.tutorialDone = true; | ||
await MetaData.save(); | ||
} | ||
closeTutorial(); | ||
}, | ||
), | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters