Comment on page
Creating Custom Contents
AdvStory
exports StoryContent
class which is also superclass of ImageContent
and VideoContent
. This means you can use any AdvStory
method to create your own story content, just like predefined contents. AdvStory
gives you all its power through StoryContent
.- Extend
StoryContent
class,StoryContent
is aStatefulWidget
.
class MyCustomContent extends StoryContent {
...
@override
StoryContentState<MyStoryContent> createState() => MyStoryContentState();
}
- For state class, extend
StoyContentState.
class MyCustomContentState extends StoryContentState<MyCustomContent> {
...
}
StoryContentState
provides methods for handling events, loading files, starting story and handling errors.
You have direct access following methods and variables inside of state class:
Provided or default
AdvStoryController
. Provided or default loading screen that used in predefined contents.
Content and story position of this story content.
Fetchs a file from the given url and caches it to local storage.
Marks the story content as ready to start. Call this method when content is ready to be display. When you call this method, if story views position is the position of this content, story flow starts immediately for the given duration, otherwise this content will be marked as ready and
AdvStory
will start the flow for this content when necessary.If an
AnimatedTray
provided to AdvStory
and the content is the first item in the story of tray, tray animation continues to play until this method is called. If the tray of this content is an
AnimatedTray
and this content is the first content in story, AdvStory
builds content but keeps it off screen until you call markReady
. Building an animated loading screen is completely unnecessary in this case. You can check this to not build a loading screen when the content is not ready. This might increase opening performance significantly.
Widget build(BuildContext context) {
if(isMyResourcesLoaded) {
return buildMyContent();
}
return shouldShowLoading ? loadingScreen : const SizedBox();
}
Returns true if this content is the first item in the tapped tray. This is different from 0th position.
For example, when user tap to second tray, if this contents position is
StoryPosition(content: 0, story: 2)
this method will return true
, but if this contents position is StoryPosition(content: 0, story: 3)
this method will return false
.Sets a timeout to call
markReady
. Use this method to set a time limit to take action when your content isn't ready at the requested time. Create your action in the onTimeout
method.Called when
StoryContent
class finished its initialization. You can call methods provided by StoryContentState
inside of this method. This method is the first place you can use AdvStory
methods and works only once. Execution order of state class methods is as follows:void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallbakc((_) {
print('postFrameCallback');
});
// Accessing AdvStory methods here throws exception
print('initState');
}
void didChangeDependencies() {
// Accessing AdvStory methods here throws exception
print('before didChangeDependencies.super()');
super.didChangeDependencies();
// AdvStory methods available here, but might work multiple times.
print('after didChangeDependencies.super()');
}
void initContent() {
print('initContent');
}
@override
Widget build(context) {
print('build');
...
}
This code will produce this output:
initStatebefore didChangeDependencies.super()initContentafter didChangeDependencies.super()buildpostFrameCallback
Best place for fetching your resources and setting content is
initContent
and its recommended. But if you need to do it in another method, be sure didChangeDependencies.super()
called. @override
void initContent async () {
// Fetch and cache your video file
final videoFile = await loadFile(url: 'https://my.video.url');
// Create and initialize video player controller
videoController = VideoPlayerController.file(videoFile);
await videoController.initialize();
// Fetch and cache image file.
final imageFile = await loadFile(url: 'https://my.video.url');
_myImage = imageFile;
// Notify AdvStory when your content is ready to display.
markReady(const Duration(seconds: 10));
}
Called when content is on screen and should start. Start any process that needs to start in this method.
@override
void onStart() {
videoController.play();
}
Called when story is paused. Pause any ongoing process in this method.
This method works on every tap.
AdvStory
calls this method very often, don't do expensive operations to avoid performance issues.@override
void onPause() {
videoController.pause();
}
Called when story is resumed. Resume any paused process in this method.
This method works on every tap up except of story/content skip.
AdvStory
calls this method very often, don't do expensive operations to avoid performance issues.@override
void onResume() {
videoController.play();
}
Called when content is not visible on screen and should stop. Stop any ongoing process and reset its progress in this method, but don't dispose anything.
@override
void onStop() {
videoController.stop();
videoController.seekTo(Duration.zero);
}
Called when the end of the set timeout is reached. Use this method to handling content preparing errors.
@override
void onTimeout() {
setState(() {
_activeView = Center(
child: Text('Error loading story!'),
);
});
Future.delayed(const Duration(seconds: 2), () => controller.toNextContent());
}
class MyCustomContent extends StoryContent {
const MyCustomContent({Key? key}) : super(key: key);
@override
StoryContentState<MyCustomContent> createState() => MyCustomStoryContentState();
}
class MyCustomContentState extends StoryContentState<MyCustomContent> {
VideoPlayerController? _videoController;
bool _hasError = false;
@override
void initContent() async {
// Set a timeout to make this content ready.
setTimeout(const Duration(seconds: 3));
// Fetch file and initialize video player
final videoFile = await loadFile(url: 'my.video.url');
_videoController = VideoPlayerController.file(videoFile);
await _videoController.initialize();
// Notify AdvStory
markReady(duration: _videoController.value.duration);
}
@override
Widget build(BuildContext context) {
// Check if timeout has reached to the end, show an error
// message.
if(_hasError) return Text('Error');
if(_videoController?.value.isInitialized) {
return Center(
child: AspectRatio(
aspectRatio: _videoController!.value.aspectRatio,
child: VideoPlayer(_videoController!),
),
);
}
// Check if showing a loading screen is necessary for this content.
return shouldShowLoading ? loadingScreen : const SizedBox();
}
@override
void onStart() {
_videoController?.play();
}
@override
onResume() {
_videoController?.play();
}
@override
void onPause() {
_videoController?.pause();
}
@override
void onStop() {
_videoController?.pause();
_videoController?.seekTo(Duration.zero);
}
@override
void onTimeout() {
setState(() {
_hasError = true;
});
}
@override
void dispose() {
_videoController?.dispose();
super.dispose();
}
}
Last modified 1yr ago