如何在flutter中创建水平和垂直可滚动的小部件

时间:2018-06-08 18:32:58

标签: flutter flutter-layout

sample Layout image

我正在尝试创建附加布局。它有两个容器。 - 首先是一个水平滚动的固定尺寸的盒子 - 第二张卡片占用剩余空间,其中包含列表视图。

如何实现此布局?

如您所见,两个容器的滚动方向不同。 代码工作到标签视图(第一个框)但是一旦我添加第二个盒子,即卡片,它没有显示任何内容并在控制台上收到错误,如下所示..

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

4 个答案:

答案 0 :(得分:3)

使用此技巧:

  1. Row包裹在SingleChildScrollView中。
  2. scrollDirection: Axis.horizontal中使用SingleChildScrollView
  3. 在您的ListView中包装SingleChildScrollView。

答案 1 :(得分:1)

tl; dr:以下代码执行您想要的操作= 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),
        ));
  }
}

继承思想过程: 我开始清理每个小部件并确保它们正确显示。在taglist小部件中,请注意您有一行作为列中的唯一小部件。在boardView中,LisView也是列中唯一的元素;

然后我添加了更多项目以确保两个卷轴都能正常工作。在tagList中添加scrollDirection: Axis.horizontal确保了这一点。

最后,时间将它们放在一起并显示两个元素。删除顶部容器Scaffold就足够了。然后就是将boardView放在Expanded小部件中。

这是一项有趣的练习。 = d

答案 2 :(得分:0)

将CustomScrollView与SliverListview结合使用,则可以通过垂直滚动和水平滚动来实现任何类型的视图。

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

答案 3 :(得分:0)

enter image description here

100% 定制

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