skip to Main Content

I’m making a flutter web application and want to restrict screen’s width but at the same time keep the whole screen scrollable like it’s done in twitter website.
twitter has whole page scrollable

I use primary Scaffold to determine whether to show bottomAppBar or navigation rail. Inside body of that Scaffold I put inner Scaffolds wrapped iniside ContrainedBox(maxWidth: 650) so I restrict the width of the content. The problem is that scroll bar is attached to inner Scaffolds but not to the screen. Below is the minimum representation code.

class MinimalHomeScreen extends StatelessWidget {
  const MinimalHomeScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final bool isMobile = constraints.maxWidth <= 600;

        return Scaffold(
          // show bottom bar if width is small
          bottomNavigationBar: isMobile
              ? BottomNavigationBar(
                  items: [
                    BottomNavigationBarItem(icon: Icon(Icons.home)),
                    BottomNavigationBarItem(icon: Icon(Icons.search)),
                  ],
                )
              : null,
          // The content is centered and its maxWidth is 650
          body: Center(
            child: ConstrainedBox(
              constraints: BoxConstraints(maxWidth: 650),
              child: Row(
                children: [
                  // show navigation rail if width is not small
                  if (!isMobile)
                    NavigationRail(
                        destinations: [
                          NavigationRailDestination(icon: Icon(Icons.home), label: Text('Home')),
                          NavigationRailDestination(icon: Icon(Icons.search), label: Text('Search')),
                        ],
                        selectedIndex: 0,
                    ),

                  // show content
                  Expanded(
                    child: Scaffold(
                      appBar: AppBar(title: Text('Title')),
                      body: ListView.separated(
                        itemCount: 100,
                        itemBuilder: (_, __) {
                          return Container(
                            height: 50,
                            width: double.infinity,
                            color: Colors.green,
                          );
                        },
                        separatorBuilder: (_, __) {
                          return Divider();
                        },
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    );
  }
}

flutter app has scrolling on only scrollable widgets

I need to scroll my content from any part of screen but I don’t really know how to do that. I also need to use inner scaffolds to have logic inside them like back button on AppBar.
the routing logic inside content remains (back button)

I think I have to use NestedScrollView or CustomScrollView but I didn’t come up with solution after hours of attemps. I also looked into ‘responsive framework’ package but it also remains scrolling for only scrollable part of content but not the whole screen. If someone knows how to solve this typical issue, please, help!

2

Answers


  1. Chosen as BEST ANSWER

    It's possible to listen the whole screen scrolling in web like this:

    document.onMouseWheel.listen((event) {
      event.deltaY; // scroll offset
    });
    

    So we can get scroll offset, put it in some stream, get it from widget which contains ListView/GridView etc, calculate offset inside ScrollController and use jumpTo() method of ScrollController. But the problem is that the given trackpad offset from onMouseWheel is little. So after hours of trying different ways like Listener widget and some libraries I decided to stop slaming my head against the wall and redisign my application. I hope there'll come a day when such a task is solved easily


  2. To achieve a scrollable screen (no matter where the user scrolls) with a restricted width, you can use a combination of SingleChildScrollView, Row and Expanded.

    In the example below, SingleChildScrollView is used as the main container, ensuring the entire screen is scrollable. The ConstrainedBox is used to restrict the minimum width of the content to 650 pixels. The content is wrapped in a Column, which includes the AppBar at the top and the Row below it. The NavigationRail is placed on the left side, and the main content is placed in an Expanded widget to take up the remaining width.

    class MinimalHomeScreen extends StatelessWidget {
      const MinimalHomeScreen({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: SingleChildScrollView(
            child: ConstrainedBox(
              constraints: BoxConstraints(minWidth: 650),
              child: Column(
                children: [
                  AppBar(title: Text('Title')),
                  Row(
                    children: [
                      NavigationRail(
                        destinations: [
                          NavigationRailDestination(icon: Icon(Icons.home), label: Text('Home')),
                          NavigationRailDestination(icon: Icon(Icons.search), label: Text('Search')),],
                        selectedIndex: 0,),
                      Expanded(
                        child: ListView.separated(
                          itemCount: 100,
                          itemBuilder: (_, __) {
                            return Container(
                              height: 50,
                              width: double.infinity,
                              color: Colors.green,);},
                          separatorBuilder: (_, __) {
                            return Divider();
                          },),),],),],),),),
          bottomNavigationBar: BottomNavigationBar(
            items: [
              BottomNavigationBarItem(icon: Icon(Icons.home)),
              BottomNavigationBarItem(icon: Icon(Icons.search)),
            ],),);}}
    

    Note: Formatting of close brackets for easier viewing. 🙂


    EDIT 1

    Given your comment, you can use a combination of NestedScrollView and SilverList.

    NestedScrollView is used as the main container, with a SliverAppBar as the header (remaining pinned at the top and can float when scrolling). Inside the NestedScrollView, the SingleChildScrollView is used to wrap the restricted content. The Column contains the NavigationRail on the left side and the ListView.builder as the main content. The ListView.builder has shrinkWrap set to true and physics set to NeverScrollableScrollPhysics() to ensure that it only takes up the necessary space and doesn’t scroll independently.

    class MinimalHomeScreen extends StatelessWidget {
      const MinimalHomeScreen({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: NestedScrollView(
            headerSliverBuilder: (context, innerBoxIsScrolled) {
              return [
                SliverAppBar(
                  title: Text('Title'),
                  pinned: true,
                  floating: true,),];},
            body: SingleChildScrollView(
              child: ConstrainedBox(
                constraints: BoxConstraints(minWidth: 650),
                child: Column(
                  children: [
                    NavigationRail(
                      destinations: [
                        NavigationRailDestination(icon: Icon(Icons.home), label: Text('Home')),
                        NavigationRailDestination(icon: Icon(Icons.search), label: Text('Search')),],
                      selectedIndex: 0,),
                    ListView.builder(
                      shrinkWrap: true,
                      physics: NeverScrollableScrollPhysics(),
                      itemCount: 100,
                      itemBuilder: (_, index) {
                        return Container(
                          height: 50,
                          width: double.infinity,
                          color: Colors.green,
                          child: Center(child: Text('Item $index')),
                        );},),],),),),),
          bottomNavigationBar: BottomNavigationBar(
            items: [
              BottomNavigationBarItem(icon: Icon(Icons.home)),
              BottomNavigationBarItem(icon: Icon(Icons.search)),
            ],),);}}
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search