title: How to create a horizontally scrolling table with fixed column in Flutter?
body: <p>I would like to create a series of tables that you can scroll through vertically, each of which may have a different number of rows/columns from each other.  </p>
  
  <p>Within each table, I would like to have the leftmost column frozen in place, and the remaining columns in that table to be horizontally scrollable, in case there are a number of columns that do not fit in the width of the screen.  See screenshot:</p>
  
  <p><a href="https://i.stack.imgur.com/WnP9I.png" rel="noreferrer"><img src="https://i.stack.imgur.com/WnP9I.png" alt="example"></a></p>
  
  <p>My initial plan was to use a ListView for the page-level vertical scrolling between tables, and within each table, there is a Row of Columns, where the first column is a static width, and the remaining columns are enclosed within a horizontally scrolling ListView.  The error I'm getting from Flutter is not helping me determine what I need to do, but it clearly has to do with having to set bounds on child Widgets.</p>
  
  <p>Error: (Fixed 7/9/19 by wrapping horizontal ListView with a fixed height container and shrinkwrapping the ListView)</p>
  
  <blockquote>
    <p>The following assertion was thrown during performResize():
    Horizontal viewport was given unbounded width.
    Viewports expand in the scrolling direction to fill their container.In this case, a horizontal
    viewport was given an unlimited amount of horizontal space in which to expand. This situation
    typically happens when a scrollable widget is nested inside another scrollable widget.
    If this widget is always nested in a scrollable widget there is no need to use a viewport because
    there will always be enough horizontal space for the children. In this case, consider using a Row
    instead. Otherwise, consider using the "shrinkWrap" property (or a ShrinkWrappingViewport) to size
    the width of the viewport to the sum of the widths of its children.</p>
  </blockquote>
  
  <p>New Error 7/9/19:</p>
  
  <blockquote>
    <p>The following message was thrown during layout:
    A RenderFlex overflowed by 74 pixels on the right.
    The overflowing RenderFlex has an orientation of Axis.horizontal.
    The edge of the RenderFlex that is overflowing has been marked in the rendering with a yellow and
    black striped pattern. This is usually caused by the contents being too big for the RenderFlex.
    Consider applying a flex factor (e.g. using an Expanded widget) to force the children of the
    RenderFlex to fit within the available space instead of being sized to their natural size.
    This is considered an error condition because it indicates that there is content that cannot be
    seen. If the content is legitimately bigger than the available space, consider clipping it with a
    ClipRect widget before putting it in the flex, or using a scrollable container rather than a Flex,
    like a ListView.
    The specific RenderFlex in question is:<br>
    RenderFlex#9bf67 relayoutBoundary=up5 OVERFLOWING<br>
      creator: Row ← RepaintBoundary-[&lt;0>] ← IndexedSemantics ←<br>
      NotificationListener ← KeepAlive ←   AutomaticKeepAlive ← SliverList ←<br>
      SliverPadding ← Viewport ← IgnorePointer-[GlobalKey#74513] ← Semantics ← Listener ← ⋯<br>
      parentData:  (can use size)<br>
      constraints: BoxConstraints(w=404.0, 0.0&lt;=h&lt;=Infinity)<br>
      size: Size(404.0, 300.0)<br>
      direction: horizontal<br>
      mainAxisAlignment: start<br>
      mainAxisSize: max<br>
      crossAxisAlignment: center<br>
      textDirection: ltr  </p>
  </blockquote>
  
  <p>This was the issue I ran into originally before getting side-tracked with the first issue reported; I can't understand why my ListView is not creating a scrollable container.</p>
  
  <p>Code:</p>
  
  <pre><code>import 'package:flutter/material.dart';
  
  void main() =&gt; runApp(MyApp());
  
  class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'My App',
        home: Scaffold(
          appBar: AppBar(
            title: Text('My App'),
            backgroundColor: Colors.teal[400],
          ),
          body: MyClass(),
        ),
      );
    }
  }
  
  const double headerCellWidth = 108.0;
  const double cellPadding = 8.0;
  const double focusedColumnWidth = 185.0;
  const double rowHeight = 36.0;
  
  class MyClass extends StatefulWidget {
    @override
    _MyClassState createState() =&gt; _MyClassState();
  }
  
  class _MyClassState extends State&lt;MyClass&gt; {
    @override
    void initState() {
      super.initState();
    }
  
    @override
    Widget build(BuildContext context) {
      return ListView(
        padding: EdgeInsets.all(5.0),
        children: &lt;Widget&gt;[
          Row(
            children: &lt;Widget&gt;[
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: &lt;Widget&gt;[
                  Container(
                    color: Colors.grey,
                    padding: EdgeInsets.all(cellPadding),
                    width: headerCellWidth,
                  ),
                  HeaderCell('ABC'),
                  HeaderCell('123'),
                  HeaderCell('XYZ'),
                ],
              ),
              Container(
                height: 300.0,  // Could compute height with fixed rows and known number of rows in advance
                child: ListView(
                  shrinkWrap: true,
                  scrollDirection: Axis.horizontal,
                  children: &lt;Widget&gt;[
                    Column(
                      children: &lt;Widget&gt;[
                        Container(
                          color: Colors.grey[300],
                          padding: EdgeInsets.all(cellPadding),
                          height: rowHeight,
                          width: focusedColumnWidth,
                        ),
                        NumberCell('89'),
                        NumberCell('92'),
                        NumberCell('91'),
                        NumberCell('90'),
                        NumberCell('91'),
                        NumberCell('89'),
                      ],
                    ),
                    Column(
                      children: &lt;Widget&gt;[
                        Container(
                          color: Colors.grey[300],
                          padding: EdgeInsets.all(cellPadding),
                          height: rowHeight,
                          width: focusedColumnWidth,
                        ),
                        NumberCell('89'),
                        NumberCell('92'),
                        NumberCell('91'),
                        NumberCell('90'),
                        NumberCell('91'),
                        NumberCell('89'),
                      ],
                    ),
                  ],
                ),
              ),
            ],
          ),
        ],
      );
    }
  }
  
  class HeaderCell extends StatelessWidget {
    HeaderCell(this.text);
  
    final String text;
  
    @override
    Widget build(BuildContext context) {
      return Container(
        height: rowHeight,
        padding: EdgeInsets.all(cellPadding),
        width: headerCellWidth,
        child: Text(
          text,
          textAlign: TextAlign.left,
          overflow: TextOverflow.ellipsis,
          maxLines: 1,
          style: TextStyle(
            fontWeight: FontWeight.bold,
          ),
        ),
      );
    }
  }
  
  class NumberCell extends StatelessWidget {
    NumberCell(this.text);
  
    final String text;
  
    @override
    Widget build(BuildContext context) {
      return Container(
        height: rowHeight,
        width: focusedColumnWidth,
        padding: EdgeInsets.all(cellPadding),
        child: Text(
          text,
        ),
      );
    }
  }
  </code></pre>