How to make PhotoView's image appear over other widgets when zoomed in flutter

534 Views Asked by At

I'm trying to make an Instagram clone app but I can't zoom into the app without the like, comment and share icon appearing over the zoomed-in image example

I want the zoomed-in image to come above every widget on screen (even the bottomNavigationBar and appBar), as in the real Instagram.

The code:

class PostCard extends StatefulWidget {
  final Map<String, dynamic> snap;
  const PostCard({super.key, required this.snap});
    
  @override
  State<PostCard> createState() => _PostCardState();
  }
    
class _PostCardState extends State<PostCard> {
  bool isbookmarked = false;
  String? desc;
  bool hide = true;
  bool isAnimating = false;
  double oldscale = 1;
  double newscale = 1;

  @override
  void initState() {...


  Future<int> getNumberOfComments() async {...

  String timeDifference(Timestamp uploadDate) {...
   

  Future<void> tapMore(BuildContext context) {...

  @override
  Widget build(BuildContext context) {
    model.User? user = Provider.of<UserProvider>(context).getuser;
    bool isliked = widget.snap['likes'].contains(user!.username);
    return Container(
      padding: const EdgeInsets.symmetric(
        vertical: 2,
      ),
      child: Column(
        children: [
          Container(
            padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 18),
            child: Row(
              children: [
                CircleAvatar(
                  backgroundImage: NetworkImage(widget.snap['profileURL']),
                ),
                const SizedBox(
                  width: 14,
                ),
                Text(widget.snap['username']),
                Expanded(
                  child: Align(
                      alignment: Alignment.bottomRight,
                      child: GestureDetector(
                          onTap: () => tapMore(context),
                          child: const Icon(Icons.more_vert_outlined))),
                )
              ],
            ),
          ),
          GestureDetector(
            onDoubleTap: () {
              Firestore().updateLikesPost(
                  removeFunction: false,
                  usernameOfPostee: widget.snap['username'],
                  postID: widget.snap['postID'],
                  likesList: widget.snap['likes'],
                  signedInUsername: user.username!);
              setState(() {
                isliked = true;
                isAnimating = true;
              });
            },
            child: Stack(
              alignment: Alignment.center,
              children: [
                Container(
                    height: MediaQuery.of(context).size.height * 0.3,
                    child: PhotoView(
                        minScale: PhotoViewComputedScale.contained * 1.0,
                        imageProvider: NetworkImage(widget.snap['postURL']))),
                // Container(
                //   height: MediaQuery.of(context).size.height * 0.3,
                //   decoration: BoxDecoration(
                //     image: DecorationImage(
                //         image: NetworkImage(widget.snap['postURL']),
                //         fit: BoxFit.contain),
                //   ),
                // ),
                AnimatedOpacity(
                  opacity: isAnimating ? 0.9 : 0,
                  duration: const Duration(milliseconds: 200),
                  child: LikeAnimation(
                    onEnd: () {
                      setState(() {
                        isAnimating = false;
                      });
                    },
                    isAnimating: isAnimating,
                    child: Icon(
                      Icons.favorite,
                      color: Colors.white,
                      size: 100,
                    ),
                  ),
                ),
              ],
            ),
          ),
          Row(
            mainAxisSize: MainAxisSize.min,
            // mainAxisAlignment: MainAxisAlignment.start,
            children: [
              IconButton(
                  onPressed: () {
                    if (isliked) {
                      Firestore().updateLikesPost(
                          removeFunction: true,
                          usernameOfPostee: widget.snap['username'],
                          postID: widget.snap['postID'],
                          likesList: widget.snap['likes'],
                          signedInUsername: user.username!);
                      setState(() {
                        isliked = widget.snap['likes'].contains(user.username);
                      });
                    } else {
                      Firestore().updateLikesPost(
                          removeFunction: true,
                          usernameOfPostee: widget.snap['username'],
                          postID: widget.snap['postID'],
                          likesList: widget.snap['likes'],
                          signedInUsername: user.username!);
                      setState(() {
                        isliked = widget.snap['likes'].contains(user.username);
                      });
                    }
                  },
                  icon: isliked
                      ? Icon(
                          Icons.favorite,
                          color: Colors.red[500],
                        )
                      : const Icon(Icons.favorite_outline)),
              IconButton(
                  onPressed: () {
                    Navigator.of(context).push(MaterialPageRoute(
                      builder: (context) => BeechWalaWidget(
                        snap: widget.snap,
                      ),
                    ));
                  },
                  icon: const Icon(Icons.mode_comment_outlined)),
              IconButton(
                  onPressed: () {}, icon: const Icon(Icons.send_rounded)),
              Expanded(
                  child: Align(
                alignment: Alignment.bottomRight,
                child: IconButton(
                  onPressed: () {
                    setState(() {
                      isbookmarked = !isbookmarked;
                    });
                  },
                  icon: isbookmarked
                      ? const Icon(Icons.bookmark)
                      : const Icon(Icons.bookmark_border),
                ),
              ))
            ],
          ),
          Align(
            alignment: Alignment.centerLeft,
            child: Container(
              padding: const EdgeInsets.symmetric(horizontal: 10),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'Liked by ${widget.snap['likes'].length} ${widget.snap['likes'].length == 1 ? 'other' : 'others'}',
                  ),
                  const SizedBox(
                    height: 5,
                  ),
                  ExpandableText(
                    '${widget.snap['description']}',
                    maxLines: 2,
                    onPrefixTap: () {
                      // Go to the user profile
                    },
                    expandText: '\n...more',
                    prefixText: '${widget.snap['username']}',
                    animation: true,
                    linkColor: secondaryColor,
                    prefixStyle: const TextStyle(fontWeight: FontWeight.w700),
                  ),
                  const SizedBox(
                    height: 5,
                  ),
                  GestureDetector(
                      onTap: () {
                        Navigator.of(context).push(MaterialPageRoute(
                          builder: (context) => BeechWalaWidget(
                            snap: widget.snap,
                          ),
                        ));
                      },
                      child: FutureBuilder(
                        future: getNumberOfComments(),
                        builder: (context, snapshot) {
                          return Text(
                            'View all ${snapshot.data} comments',
                            style: const TextStyle(color: secondaryColor),
                          );
                        },
                      )),
                  const SizedBox(
                    height: 3,
                  ),
                  Text(
                    timeDifference(widget.snap['datePublished'] as Timestamp),
                    style: const TextStyle(color: secondaryColor, fontSize: 10),
                  )
                ],
              ),
            ),
          )
        ],
      ),
    );
  }
}

TLDR code: (the structure of build method)

@override
Widget build(BuildContext context){
  return Container(
    child: Column(
      children: [
        // Container with padding (
            Row: [CircleAvatar(profile photo), text(username), IconButton(More Options)]
          ),
        // Stack(
            children: [Container(child: PhotoView()), LikeAnimation()]
          ),
        // Row(
            children: [likebutton, commentbutton, sendbutton, bookmark button]
           ),
        // Column(
            children: [text("Liked by _ others"), ExpandedText(external package for bio), text(View all comments), text(_ days ago)]
           ),
         ]
       )
     );
}

The widget reproduced by the code above looks like this

PS: I'm using this widget in a ListView (not lazily built) to display multiple posts like these so the size of all widgets must be described.

2

There are 2 best solutions below

1
On BEST ANSWER

Give this package a try, it might suit your use case well.

https://pub.dev/packages/zoom_pinch_overlay

https://pub.dev/packages/zoom_pinch_overlay

0
On

It doesn't work when the widget is in a ListView. The previous is behind, and the next is in front of it. I would expect the zoomed widget to be on top of both and not just the previous widget in the ListView.

Secondly - but not least - in a ListView it does only zoom if PhotoView is used as Imagebuilder on the CachedNetworkImage.

ListView
- children:
    Stack
    - children: 
          Container - ZoomOverlay - CachedNetworkImage,
          Visibility - Padding - Icon
          Visibility - Padding - Icon
          Visibility - Padding - Buttonbar ...

    Text