2

I have a need for vertical list who's elements exceed the width of the display (similar to a wide Flutter Datatable). So I'd like to make the vertically scrollable list also scrollable horizontally. It seems the way to do this would be to put the list in a SingleChildScrollView

   SingleChildScrollView(child: listview, scrollDirection: Axis.horizontal);

... which works, but apparently not if the listview contains a row as an element:

 Row(
    mainAxisAlignment: MainAxisAlignment.start,
    crossAxisAlignment: CrossAxisAlignment.start,
    mainAxisSize: MainAxisSize.min,
    children: children.toList());

I don't understand why this is a problem, since this kind of row should have a finite width (due to MainAxisSize.min).

It's possible to wrap each list element in a SingleChildScrollView, but this leads only to the single element being scrollable.

I can't use Datatable in a SingleChildScrollView for this because it doesn't support variably sized cell heights. how to create horizontally scrollable vertical listview in Flutter?


Widget test() {
  Widget child = ListView.builder(
      itemCount: 2,
      shrinkWrap: true,
      itemBuilder: (context, i) {
        Widget child = rowSSMin_([
          Text(
              "AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA"),
          Text(
              "BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB"),
          Text(
              "XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX"),
        ]);
        return child;
      });

  child = scrollH(child);  // < breaks

  return child;
}

//////////////////////////////////////////////////////////////////////////////
//

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            body: Center(
                child: Padding(padding: EdgeInsets.all(20), child: test()))));
  }
}


Widget scrollH(Widget child, {ScrollController controller}) =>
    SingleChildScrollView(child: child, scrollDirection: Axis.horizontal, controller: controller);

Widget columnSSMin_(List<Widget> children) => Column(
    mainAxisAlignment: MainAxisAlignment.start,
    crossAxisAlignment: CrossAxisAlignment.start,
    mainAxisSize: MainAxisSize.min,
    children: children);

Widget rowSSMin_(Iterable<Widget> children) => Row(
    mainAxisAlignment: MainAxisAlignment.start,
    crossAxisAlignment: CrossAxisAlignment.start,
    mainAxisSize: MainAxisSize.min,
    children: children.toList());

gets...

════════ Exception caught by rendering library ═════════════════════════════════════════════════════
The following assertion was thrown during performLayout():
RenderBox was not laid out: RenderSemanticsAnnotations#fe879 relayoutBoundary=up10 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
'package:flutter/src/rendering/box.dart':
Failed assertion: line 1785 pos 12: 'hasSize'


Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=BUG.md

The relevant error-causing widget was: 
  SingleChildScrollView file:///home/sir/ailive/livebetter/flutterui/lib/scratch2.dart:41:31
When the exception was thrown, this was the stack: 
#2      RenderBox.size (package:flutter/src/rendering/box.dart:1785:12)
#3      RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:114:21)
#4      RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
#5      RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:113:14)
#6      RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
...
The following RenderObject was being processed when the exception was fired: RenderPointerListener#ea643 relayoutBoundary=up9 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
...  parentData: <none> (can use size)
...  constraints: BoxConstraints(0.0<=w<=921.5, 0.0<=h<=512.9)
...  size: MISSING
...  behavior: opaque
...  listeners: down
RenderObject: RenderPointerListener#ea643 relayoutBoundary=up9 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
  parentData: <none> (can use size)
  constraints: BoxConstraints(0.0<=w<=921.5, 0.0<=h<=512.9)
  size: MISSING
  behavior: opaque
  listeners: down
...  child: RenderSemanticsAnnotations#fe879 relayoutBoundary=up10 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
...    parentData: <none> (can use size)
...    constraints: BoxConstraints(0.0<=w<=921.5, 0.0<=h<=512.9)
...    size: MISSING
...    child: RenderIgnorePointer#74492 relayoutBoundary=up11 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
...      parentData: <none> (can use size)
...      constraints: BoxConstraints(0.0<=w<=921.5, 0.0<=h<=512.9)
...      size: MISSING
...      ignoring: false
...      ignoringSemantics: false
...      child: _RenderSingleChildViewport#3aec9 relayoutBoundary=up12 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
...        needs compositing
...        parentData: <none> (can use size)
...        constraints: BoxConstraints(0.0<=w<=921.5, 0.0<=h<=512.9)
...        size: MISSING
...        child: RenderRepaintBoundary#4986d relayoutBoundary=up13 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
...          needs compositing
...          parentData: <none> (can use size)
...          constraints: BoxConstraints(0.0<=w<=Infinity, 0.0<=h<=512.9)
...          size: MISSING
...          usefulness ratio: no metrics collected yet (never painted)

Tying each row to a ScrollController also doesn't seem to get the whole list to scroll horizontally in unison:

Widget test() {
  ScrollController controller = ScrollController();

  Widget child = ListView.builder(
      itemCount: 2,
      shrinkWrap: true,
      itemBuilder: (context, i) {
        Widget child = rowSSMin_([
          Text(
              "AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA"),
          Text(
              "BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB"),
          Text(
              "XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX"),
        ]);
        child = scrollH(child, controller:controller);  // Added
        return child;
      });

  // child = scrollH(child, controller:controller);  // Removed

  return child;
}

2 Answers 2

2

You can try wrapping your scrollable widgets with a Container and setting its height and width to the device's size:

Container(
  height: MediaQuery.of(context).size.height,
  width: MediaQuery.of(context).size.width,
  child: yourVerticallyOrHorizontallyScrollableWidget
)
Sign up to request clarification or add additional context in comments.

Comments

2

Yes you can, but with 2 SingleChildScrollView and make sure that it has no ListView in it.

     SingleChildScrollView(
        scrollDirection: Axis.vertical,
        child: SingleChildScrollView(
          scrollDirection: Axis.horizontal,
          child: Column(
            children: [ 
            \\ Your widgets
            ],
          ),
        ),
      ),

update

If you want to use ListView.builder as you said at the comments, You should do 2 things:

1- add this attribute for the listView :

physics: NeverScrollableScrollPhysics()

2- add the listview inside any widget that should have width and height. and this width should be more than the width of the screen.

This is the code

@override
  Widget build(BuildContext context) {
    return SafeArea(
      child: new Scaffold(
        body: SingleChildScrollView(
          scrollDirection: Axis.horizontal,
          child: SingleChildScrollView(
            scrollDirection: Axis.vertical,
            child: Container(
              width: MediaQuery.of(context).size.width * 1.8,
              height: MediaQuery.of(context).size.height,
              child: ListView.builder(
                  physics: NeverScrollableScrollPhysics(),
                  scrollDirection: Axis.vertical,
                  itemCount: 24,
                  shrinkWrap: false,
                  itemBuilder: (context, i) {
                    return Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text(
                                "AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA"),
                            Text(
                                "BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB BBB"),
                            Text(
                                "XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX"),
                          ]),
                    );
                  }),
            ),
          ),
        ),
      ),
    );
  }

4 Comments

Can you share what's wrong with the long list with code?
It requires that all the items in the list are rendered at once. This great benefit of ListView is that is can handle hundreds of thousands of elements and add visible elements to the render tree.
See ListView.builder
What's the deal with "width * 1.8"? It causes the length of the list to limites -- you can never scroll far down a large list. I don't think this problem is solvable (for ListView) by wrapping the list in a vertical scrollable, because your removes the ability of the list to manages the visibility of offscreen widgets.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.