'How can i do Navigator.push in a modal bottom sheet only (not the parent page)

In my app, i am trying to create a comments section. In this section, i want a user to be able to reply to another user's comment in a fashion akin to the YouTube app. I have managed to create a modal bottom sheet showing the top level comments and it is working quite well, withthe aid of several stream builders. However, the problem im facing is the second level comments (replies to people's comments.). I want to make it so that user taps on a comment and is taken to a page (still in the modal bottom sheet) where he sees all replies to the comment and can also comment on his own, and if he presses back, he goes right back to where he was and can continue scrolling through the top level comments.

Akin to how the YouTube app looks. I had the idea of using Navigator.push inside the bottom sheet, to overlay a new page on top of the currently existing page in the modal bottom sheet and in this new page, the second level comments (replies) for a particular comment are to be displayed. But, when i call Navigator.push, the entire page changes. The whole parent page shifts to the new page, instead of just the stuff in the bottom sheet. So, that's my problem. I'm open to ideas.

the code behind my comment bottom sheet:

  showModalBottomSheet(
  context: context,
  builder: (context) {
    return 
    Container(
      child: StatefulBuilder(
        builder: (BuildContext context, setTheModalState) {
          setModalState = setTheModalState;
          return Container(
            child: Column(
              children: <Widget>[
                Expanded(
                  child: StreamBuilder(listview))])}))}

This is what i have designed so far. The tricky bit now is making a new page in the modal sheet on press of the reply button (the message icon)

This is what i have designed so far. The tricky bit now is making a new page in the modal sheet on press of the reply button (the message icon)

This is what a YouTube comment system looks like. A YouTube comment system

The code for the comment section:

showModalBottomSheet(
  context: context,
  builder: (context) {
    return 
    Container(
      child: StatefulBuilder(
        builder: (BuildContext context, setTheModalState) {
          setModalState = setTheModalState;
          return Container(
            child: Column(
              children: <Widget>[
                Expanded(
                  child: StreamBuilder(
                    stream: Firestore.instance
                        .collection("Replies")
                        .document(dream.documentID)
                        .collection(dream.documentID)
                        .orderBy('time', descending: true)
                        .snapshots(),
                    builder: (context, snapshot) {
                      if (!snapshot.hasData) {
                        return Center(
                          child: SpinKitDoubleBounce(
                            color: Colors.blue,
                          ),
                        );
                      } else {
                        dynamic replies = snapshot.data.documents;

                        return ListView.builder(
                          itemCount: replies.length,
                          reverse: true,
                          itemBuilder: (context, index) {
                            if (replies[index]["text"] != "" &&
                                replies[index]["images"] == null) {
                              return _buildStringCommment(
                                  replies[index], true);
                            } else {
                              if (replies[index]["images"].length == 1) {
                                return _buildCommentFromOneImage(
                                    replies[index], true);
                              } else {
                                if (replies[index]["images"].length == 2) {
                                  return _buildCommentFromTwoImages(
                                      replies[index], true);
                                } else {
                                  if (replies[index]["images"].length ==
                                      3) {
                                    return _buildCommentFromThreeImages(
                                        replies[index], true);
                                  } else {
                                    if (replies[index]["images"].length ==
                                        4) {
                                      return _buildCommentFromFourImages(
                                          replies[index], true);
                                    } else {
                                      if (replies[index]["images"].length ==
                                          5) {
                                        return _buildCommmentFromFiveImages(
                                            replies[index], true);
                                      } else {
                                        return _buildMessageFromMoreThanFiveImages(
                                            replies[index], true);
                                      }
                                    }
                                  }
                                }
                              }
                            }
                          },
                        );
                      }
                    },
                  ),
                ),
                theTypeReplyThingie(dream, context, true)
              ],
            ),
          );
        },
      ),
    );
  },
);

"the type reply thingie" is the widget that has the text input form and the send button.



Solution 1:[1]

There are two ways you can achieve this.

  1. If you are using modalSheet, then why do you need to use Navigator.push? Lets say, you have a comment text field/button. To the onPressed callback, you need to call showModalSheet. You don't need to routing.

    `Inkwell(child: Text("comment"),
            onTap: () => showModalSheet() //your sheet here
         ) ` 
    
  2. You can use fullScreenDailog property in MaterialPageRoute. This would suit your requirements better.

       Navigator.of(context).push(
          MaterialPageRoute(
                fullscreenDialog: true,
                builder: (context) => XYZ(),)
    

Solution 2:[2]

Soooo, for the benefit of anyone else who may have this same query, i have managed to do this using a modified version of Mr Azad Prajapat's answer. I am attaching the relevant code below.

Essentially, i used a stack to either show sized box of height 0 or show a container that has the stream builder, text form and other relevant widgets of the reply_to_a_comment page. Using a boolean, i am able to easily switch between what the user sees, without the entire comment page having to reload, or the user losing his current scroll position. PS: I am relatively a beginner in flutter, soo, some of the code may look too boilerplate. Just bear with me.

The new commment section code:

 wereOnTopLevel = true;
showModalBottomSheet(
  context: context,
  builder: (context) {
    return Container(
      child: StatefulBuilder(
        builder: (BuildContext context, setTheModalState) {
          setModalState = setTheModalState;
          return Stack(
            children: <Widget>[
              Container(
                child: Column(
                  children: <Widget>[
                    Expanded(
                      child: StreamBuilder(
                        stream: Firestore.instance
                            .collection("Replies")
                            .document(post.documentID)
                            .collection(post.documentID)
                            .orderBy('time', descending: true)
                            .snapshots(),
                        builder: (context, snapshot) {
                          if (!snapshot.hasData) {
                            return Center(
                              child: SpinKitDoubleBounce(
                                color: Colors.blue,
                              ),
                            );
                          } else {
                            dynamic replies = snapshot.data.documents;

                            return ListView.builder(
                              itemCount: replies.length,
                              reverse: true,
                              itemBuilder: (context, index) {
                                if (replies[index]["text"] != "" &&
                                    replies[index]["images"] == null) {
                                  return _buildStringReply(
                                      replies[index], true, true);
                                } else {
                                  if (replies[index]["images"].length ==
                                      1) {
                                    return _buildMessageFromOneImage(
                                        replies[index], true, true);
                                  } else {
                                    if (replies[index]["images"].length ==
                                        2) {
                                      return _buildMessageFromTwoImages(
                                          replies[index], true, true);
                                    } else {
                                      if (replies[index]["images"].length ==
                                          3) {
                                        return _buildMessageFromThreeImages(
                                            replies[index], true, true);
                                      } else {
                                        if (replies[index]["images"]
                                                .length ==
                                            4) {
                                          return _buildMessageFromFourImages(
                                              replies[index], true, true);
                                        } else {
                                          if (replies[index]["images"]
                                                  .length ==
                                              5) {
                                            return _buildMessageFromFiveImages(
                                                replies[index], true, true);
                                          } else {
                                            return _buildMessageFromMoreThanFiveImages(
                                                replies[index], true, true);
                                          }
                                        }
                                      }
                                    }
                                  }
                                }
                              },
                            );
                          }
                        },
                      ),
                    ),
                    theTypeReplyThingie(dream, context, true)
                  ],
                ),
              ),
              wereOnTopLevel
                  ? SizedBox(
                      height: 0,
                    )
                  : showTheRepliesToCommentsThing(
                      dream, context, idYaComment, commentInfo)
            ],
          );
        },
      ),
    );
  },
);

wereOnTopLevel is a boolean that tells the bottom sheet whether or not the user has tapped the "replyToAComment" button on a certain comment.

theTypeReplyThing is the widget that contains the textFormField, the emoji grid, the container for displaying the grid of selected images , the send button and the button for attaching images to a reply / comment.

showTheRepliesToTheCommentsThing is the widget that contains the replies. It is more or less the same as the comment page, except that for it, i put a small preview of the comment being replied to at the top, and i had to make the color of the container white, because it is otherwise transparent and this looks confusing.

Let me attach an image of the reply_to_a_comment page to show the difference. (i added a divider below the top bar later on. To make it look nicer) Contrast it with the image of the comment section i attached to the question. Picture of the reply to a comment page

Solution 3:[3]

Just show another ModalBottomSheet inside the main one Here's an example,

ElevatedButton(
          onPressed: () {
            showModalBottomSheet(
                context: context,
                builder: (context) {
                  return ElevatedButton(
                      onPressed: () {
                        showModalBottomSheet(
                            context: context,
                            builder: (context) {
                              return Container(
                                child: Center(
                                  child: ElevatedButton(
                                    child: Text('back'),
                                    onPressed: () =>
                                        Navigator.pop(context),
                                  ),
                                ),
                              );
                            });
                      },
                      child: Text('Next Bottom'));
                });
          },
          child: Text('press Me'),
        )

Solution 4:[4]

You have to use showModalBottomSheet() method, which would return your desired comment section.

A sample code is:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
  static const String _title = 'Flutter Code Sample';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: _title,
      home: Scaffold(
        appBar: AppBar(title: const Text(_title)),
        body: MyStatelessWidget(),
      ),
    );
  }
}

/// This is the stateless widget that the main application instantiates.
class MyStatelessWidget extends StatelessWidget {
  MyStatelessWidget({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: RaisedButton(
        child: const Text('showModalBottomSheet'),
        onPressed: () {
          showModalBottomSheet<void>(
            context: context,
            builder: (BuildContext context) {
              return Container(
                height: 200,
                color: Colors.amber,
                child: Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    mainAxisSize: MainAxisSize.min,
                    children: <Widget>[
                      const Text('Modal BottomSheet'),
                      RaisedButton(
                        child: const Text('Close BottomSheet'),
                        onPressed: () => Navigator.pop(context),
                      )
                    ],
                  ),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

For further information, visit: https://api.flutter.dev/flutter/material/showModalBottomSheet.html

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 theCaptainXgod
Solution 2 Simeon
Solution 3 Khaled_Alshafeey
Solution 4 spycbanda