A Flutter package for creating highly customized in-app tutorials.
- Add dependency to
pubspec.yaml
dependencies:
tutorial_stage: <latest-version>- Import the package
import'package:tutorial_stage/tutorial_stage.dart';- Add
TutorialStageto your widget tree
TutorialStage(
child:SomeWidget(),
)- Adding
TutorialContents
enumTutorialIdentifier {
button,
title,
counter,
}
classTutorialContentExampleextendsStatelessWidget {
constTutorialContentExample({
super.key,
requiredthis.key,
requiredthis.text,
});
finalGlobalKey key;
finalString text;
@overrideWidgetbuild(BuildContext context) {
finalRect rect = key.boxPosition!.rect.withPadding(constEdgeInsets.all(4));
returnSpotlightStage(
rect: rect,
borderRadius:constBorderRadius.all(Radius.circular(4)),
children:<Widget>[
AlignRect(
rect: rect,
alignment:constAlignment(0.0, 2.0),
child:Padding(
padding:constEdgeInsets.only(top:8),
child:ElevatedButton(
onPressed: () =>TutorialStage.of(context).next(),
child:Text(text),
),
),
),
],
);
}
}
classButtonTutorialContentextendsAnimatedTutorialContent {
ButtonTutorialContent(this._buttonKey)
:super(identifier:TutorialIdentifier.button);
finalGlobalKey _buttonKey;
@overrideWidgetbuildContent(BuildContext context) {
returnTutorialContentExample(
key: _buttonKey,
text:'Button',
);
}
}
classTitleTutorialContentextendsAnimatedTutorialContent {
TitleTutorialContent(this._titleKey)
:super(identifier:TutorialIdentifier.title);
finalGlobalKey _titleKey;
@overrideWidgetbuildContent(BuildContext context) {
returnTutorialContentExample(
key: _titleKey,
text:'Title',
);
}
}
classCounterTutorialContentextendsAnimatedTutorialContent {
CounterTutorialContent(this._counterKey)
:super(identifier:TutorialIdentifier.counter);
finalGlobalKey _counterKey;
@overrideFuture<void> start() async {
awaitScrollable.ensureVisible(
_counterKey.currentContext!,
duration:constDuration(milliseconds:300),
);
}
@overrideWidgetbuildContent(BuildContext context) {
returnTutorialContentExample(
key: _counterKey,
text:'Counter',
);
}
}- Starting the tutorial
finalGlobalKey _buttonKey =GlobalKey();
finalGlobalKey _titleKey =GlobalKey();
finalGlobalKey _counterKey =GlobalKey();
@overrideWidgetbuild(BuildContext context) {
returnTutorialStage(
child:Scaffold(
appBar:AppBar(
title:Text(
widget.title,
key: _titleKey,
),
),
body:SingleChildScrollView(
child:Column(
mainAxisAlignment:MainAxisAlignment.center,
children:<Widget>[
constText('This is text'),
Padding(
padding:EdgeInsets.only(
top:MediaQuery.of(context).size.height,
),
),
Text(
'This the target text',
key: _counterKey,
style:Theme.of(context).textTheme.headline4,
),
constSizedBox(height:200),
],
),
),
floatingActionButton:FloatingActionButton(
key: _buttonKey,
onPressed: _startTutorial,
child:constText('Start'),
),
),
);
}
void_startTutorial() {
TutorialStage.build(
context: context,
contents:<TutorialContent>[
_ButtonTutorialContent(_buttonKey),
_TitleTutorialContent(_titleKey),
_CounterTutorialContent(_counterKey),
],
).start();
}