QuiteListener
QuiteListener

Reputation: 125

how to create horizontal and vertical scrollable widgets in flutter

sample Layout image

enter image description here

I am trying to create attached layout. It has two containers.

How can I achieve this layout ?

As you can see, the scroll direction is different for both containers.

The code is working till the Tags view (first box) but as soon as I am adding a second box i.e card, it is not showing anything and getting errors at console as below..

I/flutter ( 9412):   AnimatedDefaultTextStyle ← _InkFeatures-[GlobalKey#85877 ink renderer] ←
I/flutter ( 9412):   NotificationListener<LayoutChangedNotification> ← ⋯
I/flutter ( 9412):   parentData: offset=Offset(0.0, 0.0); flex=null; fit=null (can use size)
I/flutter ( 9412):   constraints: BoxConstraints(0.0<=w<=340.0, 0.0<=h<=Infinity)
I/flutter ( 9412):   size: MISSING
I/flutter ( 9412):   additionalConstraints: BoxConstraints(biggest)
I/flutter ( 9412): This RenderObject had the following descendants (showing up to depth 5):
I/flutter ( 9412):   RenderFlex#93e12 NEEDS-LAYOUT NEEDS-PAINT
I/flutter ( 9412):     RenderRepaintBoundary#977a7 NEEDS-LAYOUT NEEDS-PAINT
I/flutter ( 9412):       RenderCustomPaint#b6be8 NEEDS-LAYOUT NEEDS-PAINT
I/flutter ( 9412):         RenderRepaintBoundary#e449b NEEDS-LAYOUT NEEDS-PAINT
I/flutter ( 9412):           _RenderExcludableScrollSemantics#293fd NEEDS-LAYOUT NEEDS-PAINT



class _KanbanState extends State<Kanban> {
  @override
  Widget build(BuildContext context) {

    Widget tagList = 
    new SizedBox(
      height: 100.0,
      child: 
    new Column(
      children: <Widget>[
        new Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: <Widget>[
            new ActionChip(
                backgroundColor: Colors.yellow,
                label: new Text('Tag1'),
                onPressed: () {
                  // update board with selection
                }),
            new ActionChip(
                //backgroundColor: Colors.transparent,
                label: new Text('Tag2'),
                onPressed: () {
                  // update board with selection
                }),
            new ActionChip(
                label: new Text('Tag3'),
                onPressed: () {
                  // update board with selection
                }),
            new ActionChip(
                label: new Text('Tag4'),
                onPressed: () {
                  // update board with selection
                }),
          ],
        )
      ],
    ),);

    Widget boardView = new Flexible(
      // margin: new EdgeInsets.symmetric(vertical: 15.0),
      child: new Column(
        children: <Widget>[
          new ListView.builder(
            scrollDirection: Axis.vertical,
            itemCount: 5,
            itemBuilder: (BuildContext context, int index) {
              return new ListTile(
                onTap: () {
                },
                title: new Row(
                  children: <Widget>[
                    new Expanded(child: new Text("This is item name")),
                    new Text("12 Dec 18"),
                  ],
                ),
              );
            },
          ),
        ],
      ),
    );

    //  int _value=0;
    return new Container(
        child: new Scaffold(
          appBar: new AppBar(
            elevation: 1.0,
            title: new Text("Test title"),
          ),
          body: new Container(
              margin: new EdgeInsets.all(10.0),
              child: Column(
                children: <Widget>[
                  tagList,
                  boardView,
                ],
              )),
        ));
  }
}

Upvotes: 10

Views: 28829

Answers (4)

Mojtaba Hoseinpour
Mojtaba Hoseinpour

Reputation: 567

Use this trick:

  1. Wrap your Row in a SingleChildScrollView.
  2. Use scrollDirection: Axis.horizontal in the SingleChildScrollView.
  3. Wrap SingleChildScrollView in the your ListView.

Upvotes: 10

Mehran Ullah
Mehran Ullah

Reputation: 792

enter image description here

100% custom

 import 'package:flutter/material.dart';
    
    import 'dart:math';
    
    
    class NestedScrolls extends StatefulWidget {
    static const   listHeader = ['Pakistan', 'China','Iran','Turkey'];
      @override
      _NestedScrollsState createState() => _NestedScrollsState();
    }
    
    class _NestedScrollsState extends State<NestedScrolls> {
     
    
    var position=0;
      var topHeader;
     Widget? applyWidget() {
    
       switch(position){
         case 0:
           setState(() {
             topHeader = NestedScrolls.listHeader[0];
           });
           // return widget if user click over pakistan in tab bar
           return grid();
         case 1:
           setState(() {
             topHeader = NestedScrolls.listHeader[1];
           });
           return   list();
         case 2:
           setState(() {
             topHeader = NestedScrolls.listHeader[2];
           });
           return Container(color: Colors.blue,
             child: Center(child: Text(topHeader),),);
         case 3:
           setState(() {
             topHeader = NestedScrolls.listHeader[3];
           });
           return Container(color: Colors.orange,
             child: Center(child: Text(topHeader),),);
    
    
       }
    
    
    
      }
    @override
      void initState() {
        // TODO: implement initState
        super.initState();
        //initial header name when activity start first time
         topHeader = NestedScrolls.listHeader[0];
      }
      @override
      Widget build(BuildContext context) {
        topHeader = topHeader;
        return Scaffold(
          // Persistent AppBar that never scrolls
          appBar: AppBar(
            title: Text('AppBar'),
            elevation: 0.0,
          ),
          body:
               Column(
                children: <Widget>[
                  ///header
                  Container(
                    alignment: Alignment.center,
                    color: Colors.blueGrey,
                    height: 90,
                    child: Text(NestedScrolls.listHeader[position]),
                  ),
                  /// tabBar
                  Container(
                    height: 60,
                    width: MediaQuery.of(context).size.width,
                    child: ListView.builder(
                        scrollDirection: Axis.horizontal,
                        itemCount: NestedScrolls.listHeader.length,
                        itemBuilder: (con, index) {
                          return GestureDetector(
                            onTap: () => setState(() {
                              position=index;
                              topHeader = NestedScrolls.listHeader[index];
    
                            }),
                            child: Padding(
                              padding: const EdgeInsets.symmetric(
                                  vertical: 2.0, horizontal: 10),
                              child: Container(alignment: Alignment.center,
                                  width: 100,
                                  color: topHeader==NestedScrolls.listHeader[index]?Colors.black26:Colors.transparent,
                                  child: Text(NestedScrolls.listHeader[index])),
                            ),
                          );
                        }),
                  ),
    
                ///Widget
                Expanded(
                      child: GestureDetector(
    
                          // onHorizontalDragStart: (left){
                          //    print('left : ${left.localPosition.direction}');
                          // // left.globalPosition.dx
                          //
                          // },
                          onHorizontalDragEnd: (start){
                            print('start : ${start.velocity.pixelsPerSecond.dx}');
                            if((start.velocity.pixelsPerSecond.dx)<-700){
                              if(position<NestedScrolls.listHeader.length-1 && position>=0)
                                setState(() {
                                  position=position+1;
                                });
    
    
                            }else{}
    
                          if((start.velocity.pixelsPerSecond.dx)>900){
                            if(position<=NestedScrolls.listHeader.length-1 && position>0)
                              setState(() {
                                position=position-1;
                              });
    
                            }
                            print(position);
                          },
                          child: applyWidget()),
                    ),
    
                ],
              ),
        );
      }
    
      list() {
        return SingleChildScrollView(scrollDirection: Axis.vertical,
          child: Container(
    
    
    
                  child: Column(children: [
                    for(var color in Colors.primaries)
                    Container(color: color, height: 100.0)
    
                  ],),
    
    
    
    
    
    
          ),
        );
      }
    
      grid() {
        return  GridView.count(
            padding: EdgeInsets.zero,
            crossAxisCount: 3,
            children: Colors.primaries.map((color) {
              return Container(color: color, height: 100.0);
            }).toList(),
          );
      }
    }

Upvotes: 1

Sibin
Sibin

Reputation: 561

Use CustomScrollView with SliverListview then you can achieve any kind of views with vertical scrolling horizontal scrolling.

CustomScrollView(
    slivers: <Widget>[
     SliverList(
  delegate: new SliverChildBuilderDelegate(
    (context, index) {
      return Container(
        child: Row(
          children: <Widget>[
            buildTitle(),
            Expanded(
              child: _buildList(),
            ),
          ],
        ),
      );
    },
    childCount: array.length,
  ),
);,
    ],
  );

Upvotes: 2

vbandrade
vbandrade

Reputation: 2662

tl;dr: the following code does what you want =D

import 'package:flutter/material.dart';

void main() async {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'stack overflow',
      theme: ThemeData(
        primarySwatch: Colors.pink,
      ),
      routes: {},
      home: KanbanState(),
    );
  }
}

class KanbanState extends StatefulWidget {
  @override
  KanbanStateState createState() {
    return KanbanStateState();
  }
}

class KanbanStateState extends State<KanbanState> {
  @override
  Widget build(BuildContext context) {
    Widget tagList = Container(
      color: Colors.green,
      height: 100.0,
      child: ListView(
        scrollDirection: Axis.horizontal,
        children: <Widget>[
          Row(
            children: <Widget>[
              ActionChip(
                  backgroundColor: Colors.yellow,
                  label: Text('Tag1'),
                  onPressed: () {
                    // update board with selection
                  }),
              ActionChip(
                  //backgroundColor: Colors.transparent,
                  label: Text('Tag2'),
                  onPressed: () {
                    // update board with selection
                  }),
              ActionChip(
                  label: Text('Tag3'),
                  onPressed: () {
                    // update board with selection
                  }),
              ActionChip(
                  label: Text('Tag4'),
                  onPressed: () {
                    // update board with selection
                  }),
              ActionChip(
                  backgroundColor: Colors.yellow,
                  label: Text('Tag1'),
                  onPressed: () {
                    // update board with selection
                  }),
              ActionChip(
                  //backgroundColor: Colors.transparent,
                  label: Text('Tag2'),
                  onPressed: () {
                    // update board with selection
                  }),
              ActionChip(
                  label: Text('Tag3'),
                  onPressed: () {
                    // update board with selection
                  }),
              ActionChip(
                  label: Text('Tag4'),
                  onPressed: () {
                    // update board with selection
                  }),
            ],
          )
        ],
      ),
    );

    Widget boardView = Container(
      color: Colors.blue,
      child: ListView.builder(
        scrollDirection: Axis.vertical,
        itemCount: 15,
        itemBuilder: (BuildContext context, int index) {
          return ListTile(
            onTap: () {},
            title: Row(
              children: <Widget>[
                Expanded(child: Text("This is item name")),
                Text("12 Dec 18"),
              ],
            ),
          );
        },
      ),
    );

    //  int _value=0;
    return Scaffold(
        appBar: AppBar(
          elevation: 1.0,
          title: Text("Test title"),
        ),
        body: Container(
          color: Colors.amber,
          child: new Column(
            children: <Widget>[
              tagList,
              Expanded(
                child: boardView,
              )
            ],
          ),
          margin: EdgeInsets.all(10.0),
        ));
  }
}

Heres the thought process: I started cleaning up each widget and making sure they would be properly displayed. In the taglist widget, notice you have a row as the only widget in a column. In the boardView, LisView is also the only element in a column;

Then I added more items to make sure both scrolls would work. Adding scrollDirection: Axis.horizontal int the tagList made sure of that.

At last, time to put it all together and display both elements. Remove the top Container as Scaffold is enough. Then is was inly a matter of placing the boardView in the Expanded widget.

This was a fun exercise. =D

Upvotes: 6

Related Questions