Code Monkey home page Code Monkey logo

story's Introduction

version likes popularity License: MIT all contributors

Instagram stories like UI with rich animations and customizability.

final 2

Usage

StoryPageView requires at least three arguments: itemBuilder, pageLength, and storyLength.

/// Minimum example to explain the usage.
return Scaffold(
  body: StoryPageView(
    itemBuilder: (context, pageIndex, storyIndex) {
      return Center(
        child: Text("Index of PageView: $pageIndex Index of story on each page: $storyIndex"),
      );
    },
    storyLength: (pageIndex) {
      return 3;
    },
    pageLength: 4,
  );
  • itemBuilder builds the content of each story and is called with the index of the pageView and the index of the story on the page.

  • storyLength decides the length of story for each page. The example above always returns 3, but it should depend on pageIndex.

  • pageLength is just the length of StoryPageView

The example above just shows 12 stories by 4 pages, which is not practical.

This one is the proper usage, extracted from example.

return Scaffold(
  body: StoryPageView(
    itemBuilder: (context, pageIndex, storyIndex) {
      final user = sampleUsers[pageIndex];
      final story = user.stories[storyIndex];
      return Stack(
        children: [
          Positioned.fill(
            child: Container(color: Colors.black),
          ),
          Positioned.fill(
            child: Image.network(
              story.imageUrl,
              fit: BoxFit.cover,
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(top: 44, left: 8),
            child: Row(
              children: [
                Container(
                  height: 32,
                  width: 32,
                  decoration: BoxDecoration(
                    image: DecorationImage(
                      image: NetworkImage(user.imageUrl),
                      fit: BoxFit.cover,
                    ),
                    shape: BoxShape.circle,
                  ),
                ),
                const SizedBox(
                  width: 8,
                ),
                Text(
                  user.userName,
                  style: TextStyle(
                    fontSize: 17,
                    color: Colors.white,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ],
            ),
          ),
        ],
      );
    },
    gestureItemBuilder: (context, pageIndex, storyIndex) {
      return Align(
        alignment: Alignment.topRight,
        child: Padding(
          padding: const EdgeInsets.only(top: 32),
          child: IconButton(
            padding: EdgeInsets.zero,
            color: Colors.white,
            icon: Icon(Icons.close),
            onPressed: () {
              Navigator.pop(context);
            },
          ),
        ),
      );
    },
    pageLength: sampleUsers.length,
    storyLength: (int pageIndex) {
      return sampleUsers[pageIndex].stories.length;
    },
    onPageLimitReached: () {
      Navigator.pop(context);
    },
  ),
);
  • gestureItemBuilder builds widgets that need gesture actions

In this case, IconButton to close the page is in the callback.

You CANNOT place the gesture widgets in itemBuilder as they are covered and disabled by the default story gestures.

  • onPageLimitReached is called when the very last story is finished.

  • It is recommended to use data model with two layers. In this case, UserModel which has the list of StoryModel

/// Example Data Model
class UserModel {
  UserModel(this.stories, this.userName, this.imageUrl);

  final List<StoryModel> stories;
  final String userName;
  final String imageUrl;
}

class StoryModel {
  StoryModel(this.imageUrl);

  final String imageUrl;
}

StoryImage

If you show images in StoryPageView, use StoryImage. It can stop the indicator until the image is fully loaded.

StoryImage(
  /// key is required
  key: ValueKey(story.imageUrl),
  imageProvider: NetworkImage(
    story.imageUrl,
  ),
  fit: BoxFit.fitWidth,
)

Be sure to assign the unique key value for each image, otherwise the image loading will not be handled properly.

indicatorAnimationController

If you stop/start the animation of the story with your custom widgets, use indicatorAnimationController

class _StoryPageState extends State<StoryPage> {
  late ValueNotifier<IndicatorAnimationCommand> indicatorAnimationController;

  @override
  void initState() {
    super.initState();
    indicatorAnimationController = ValueNotifier<IndicatorAnimationCommand>(
        IndicatorAnimationCommand.resume);
  }

  @override
  void dispose() {
    indicatorAnimationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: StoryPageView(
        indicatorAnimationController: indicatorAnimationController,
        ...,
      ),
    );
  }
}

Once the instance is passed to StoryPageView, you can stop handle the indicator by the methods below.

/// To pause the indicator
indicatorAnimationController.value = IndicatorAnimationCommand.pause;

/// To resume the indicator
indicatorAnimationController.value = IndicatorAnimationCommand.resume;

Contributors

Santa Takahashi
Santa Takahashi

💻
Isaias Mejia de los Santos
Isaias Mejia de los Santos

💻
Медик
Медик

💻
Alperen Soysal
Alperen Soysal

💻
AtixD
AtixD

💻
harshitFinmapp
harshitFinmapp

💻
dmitry-kotorov
dmitry-kotorov

💻
sachin kumar rajput
sachin kumar rajput

💻
Mohamed Othman
Mohamed Othman

💻

story's People

Contributors

allcontributors[bot] avatar alperensoysall avatar atixd avatar dmitry-kotorov avatar harshitfinmapp avatar imejiasoft avatar mwothman avatar rajputsk avatar realmedik avatar santa112358 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

story's Issues

Video Player Screen

is there a way to mix multiple Video and Photo on the stories like IG stories or FB's?

Additional Gestures

Is there a way to modify the overall gesture controls so we can add our own gestures?

For example, what if I want to make a story with an image that can link to a website?

Option to start viewing stories from any story

I couldn't find any option to start in the middle. Let's say there are 10 users' stories, what if end user doesn't press the first story, and instead press second story? It should be start on second story but end user also should view first story without going back and clicking the first story; they should do that by the same way like going to previous story. Is there an option to do that? Maybe I just missed that

Progress bar don't apears on new phones

As you can see on Iphone 12 Pro Max emulator the progress bar is under the notch. On others phone it can be displayed under notification bar.

Capture d’écran, le 2021-09-07 à 19 25 47

There is any way to modify the top margin of the progress bar ?

Widget Placement Issues

What is the best approach for having widgets appear responsively across devices as well as having multiple widgets with gestures at once?

I'm trying something like this:

Currently, I am using this extension:

extension GlobalKeyExtension on GlobalKey {
  Rect? get globalPaintBounds {
    final renderObject = currentContext?.findRenderObject();
    final translation = renderObject?.getTransformTo(null).getTranslation();
    if (translation != null && renderObject?.paintBounds != null) {
      final offset = Offset(translation.x, translation.y);
      return renderObject!.paintBounds.shift(offset);
    } else {
      return null;
    }
  }
}

Then on the Container or SizedBox of the Custom Widget I am adding, I would set a key like so:

final myCustomContainerKey = GlobalKey();

Container(
    key: myCustomContainerKey,
    child: ...

Saving the positionings:

'left': myCustomContainerKey.globalPaintBounds!.left,
'top': myCustomContainerKey.globalPaintBounds!.top,
'right': myCustomContainerKey.globalPaintBounds!.right,
'bottom': myCustomContainerKey.globalPaintBounds!.bottom,

When I display the UI again:

itemBuilder: (context, pageIndex, storyIndex) {
    return Align(
          child: Padding(
            padding: EdgeInsets.only(
              left: (left is int) ? left.toDouble() : left,
              top: (top is int) ? top.toDouble() : top,
              right: (right is int) ? right.toDouble() : right,
              bottom: (bottom is int) ? bottom.toDouble() : bottom,
            ),
            child: Positioned.fromRect(
              rect: Rect.fromLTRB(
                (left is int) ? left.toDouble() : left,
                (top is int) ? top.toDouble() : top,
                (right is int) ? right.toDouble() : right,
                (bottom is int) ? bottom.toDouble() : bottom,
              ),
              child: Transform.rotate(
                angle: (rotation is int) ? rotation.toDouble() : rotation,
                child: Transform.scale(
                  scale: (scale is int) ? scale.toDouble() : scale,
                  child: ....

Some of the issues I am facing include:
• Removing Align causes overall issues with viewing any of the widgets.
• If I am trying to view multiple widgets at once, most of this logic is ignored & all the widgets are placed in the center of the screen.

Vertical Drag

Can we implement Vertical Drag like Swipe Down to PopUp Story and Swipe Up to Open any link like instagram?

Video stories

Please send the documentation for using video stories

Bug: minHeight of _Indicator is hard-coded to 2 instead of indicatorHeight parameter

class _Indicator extends StatelessWidget {
  const _Indicator({
    Key? key,
    required this.index,
    required this.value,
    required this.indicatorVisitedColor,
    required this.indicatorUnvisitedColor,
    required this.indicatorHeight,
  }) : super(key: key);
  final int index;
  final double value;
  final Color indicatorVisitedColor;
  final Color indicatorUnvisitedColor;
  final double indicatorHeight;

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Padding(
        padding: EdgeInsets.only(left: (index == 0) ? 0 : 4),
        child: LinearProgressIndicator(
          value: value,
          backgroundColor: indicatorUnvisitedColor,
          valueColor: AlwaysStoppedAnimation<Color>(indicatorVisitedColor),
          minHeight: 2 // Should be indicatorHeight
        ),
      ),
    );
  }
}

onPageLimitReached Double pop()

If there is pop() on onPageLimitReached, when the last story is finish, it closes normally as expected. But if end user tap the right side to the last story (like try to go to next story), pop() method is called twice and users find themselves on a page they don't want.

StoryPageView( onPageLimitReached: () { Navigator.pop(context); } )

Story Indicator border radius

Hello there, thanks for this library.

Could we add border radius to the story indicator ?

One way would be to wrap the LinearProgressIndicator with a ClipRRect widget.

Video Player Support

We need video player support here, this package should play videos in story view also.

onPageChanged callback NOT working

I have this piece of code :

StoryPageView(
                itemBuilder: (context, pageIndex, storyIndex) {
                  // We can use NULL checks as if .data does NOT have any value OR
                  // If storyData is empty, such cases are handled before the code
                  // written below is executed
                  final hashMap = zoopShots.data!.storyData;
                  final hashMapList = hashMap!.values.toList();

                  var imageSrc = ApiUrls.strapiBaseUrl +
                      hashMapList[storyIndex].url.substring(1);

                  return Stack(
                    children: [
                      //  Background Color
                      Positioned.fill(
                        child: Container(
                          color: const Color(0xFFF2F6FF),
                        ),
                      ),
                      //  Main Image
                      renderContent(context, imageSrc),
                    ],
                  );
                },
                gestureItemBuilder: (context, pageIndex, storyIndex) {
                  return const SizedBox.shrink();
                },
                indicatorAnimationController: indicatorAnimationController,
                initialStoryIndex: (pageIndex) {
                  final index = widget.model?.indexToVisit ?? 0;
                  return index;
                },
                pageLength: 1,
                storyLength: (int pageIndex) {
                  return zoopShots.data!.storyData!.length;
                },
                onPageChanged: (index) {
                  log('Currently at $index');
                },
                onPageLimitReached: () => Navigator.pop(context),
              );

Doesn't matter I manually change the story OR it gets changed automatically after the timer runs out, this callback (onPageChanged ) never gets called/executed.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.