简体   繁体   中英

How to create a vertical list fully horizontally scrollable in flutter

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;
}

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
)

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"),
                          ]),
                    );
                  }),
            ),
          ),
        ),
      ),
    );
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM