How to make a screen with Tabs and TabBarViews both scrollable in flutter?

Viewed 52

I've been trying to add SingleChildScrollView to my code so that the whole page including Tabs and TabBarViews are scrolled together.

RenderFlex children have non-zero flex but incoming height constraints are unbounded Horizontal viewport was given unbounded height.

These are the errors I encountered to make the whole page scrollable.

import 'package:buttons_tabbar/buttons_tabbar.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';

import '../widgets/style_grid.dart';

class StyleScreen extends StatefulWidget {
  const StyleScreen({super.key});

  @override
  State<StyleScreen> createState() => _StyleScreenState();
}

class _StyleScreenState extends State<StyleScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Screen')),
      body: SafeArea(
        child: DefaultTabController(
          length: 5,
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              ButtonsTabBar(
                backgroundColor: Colors.grey[300],
                radius: 16,
                unselectedLabelStyle: TextStyle(color: Colors.black),
                labelStyle: TextStyle(
                    color: Colors.orange, fontWeight: FontWeight.bold),
                tabs: [
                  Tab(
                    icon: Icon(Icons.directions_car),
                    text: "All",
                  ),
                  Tab(
                    icon: Icon(Icons.directions_transit),
                    text: "Cat1",
                  ),
                  Tab(
                    icon: Icon(Icons.directions_transit),
                    text: "Cat2",
                  ),
                  Tab(
                    icon: Icon(Icons.directions_transit),
                    text: "Cat3",
                  ),
                  Tab(
                    icon: Icon(Icons.directions_transit),
                    text: "Cat4",
                  ),
                ],
              ),
              Expanded(
                child: TabBarView(
                  physics: NeverScrollableScrollPhysics(),
                  children: <Widget>[
                    StyleGrid(),
                    StyleGrid(),
                    StyleGrid(),
                    StyleGrid(),
                    StyleGrid(),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

I want to make the red area scrollable.

2 Answers

Try wrapping it in the following class. This provides both vertical and horizontal scrolling.

class ScrollableWidget extends StatelessWidget {
      final Widget child;
    
      const ScrollableWidget({Key? key, required this.child}) : super(key: key);
    
      @override
      Widget build(BuildContext context) => SingleChildScrollView(
            physics: const BouncingScrollPhysics(),
            scrollDirection: Axis.horizontal,
            child: SingleChildScrollView(
              physics: const BouncingScrollPhysics(),
              scrollDirection: Axis.vertical,
              child: child,
            ),
          );
    }

You can use NestedScrollView. It is better to use ListView.builder or CustomScrollView instead of using SingleChildScrollView on StyleGrid

class StyleScreen extends StatefulWidget {
  const StyleScreen({super.key});

  @override
  State<StyleScreen> createState() => _StyleScreenState();
}

class _StyleScreenState extends State<StyleScreen>
    with SingleTickerProviderStateMixin {
  late final TabController controller = TabController(length: 5, vsync: this);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Screen')),
      body: NestedScrollView(
        headerSliverBuilder: (context, innerBoxIsScrolled) => [
          SliverToBoxAdapter(
            child: tabBar(),
          )
        ],
        body: TabBarView(
          controller: controller,
          children: List.generate(
              5, (index) => SingleChildScrollView(child: StyleGrid())),
        ),
      ),
    );
  }

  ButtonsTabBar tabBar() {
    return ButtonsTabBar(
      controller: controller,
      backgroundColor: Colors.grey[300],
      radius: 16,
      unselectedLabelStyle: TextStyle(color: Colors.black),
      labelStyle: TextStyle(color: Colors.orange, fontWeight: FontWeight.bold),
      tabs: [
        Tab(
          icon: Icon(Icons.directions_car),
          text: "All",
        ),
        Tab(
          icon: Icon(Icons.directions_transit),
          text: "Cat1",
        ),
        Tab(
          icon: Icon(Icons.directions_transit),
          text: "Cat2",
        ),
        Tab(
          icon: Icon(Icons.directions_transit),
          text: "Cat3",
        ),
        Tab(
          icon: Icon(Icons.directions_transit),
          text: "Cat4",
        ),
      ],
    );
  }
}

Related