简体   繁体   English

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

[英]how to create horizontal and vertical scrollable widgets in flutter

sample Layout image示例布局图像

在此处输入图片说明

I am trying to create attached layout.我正在尝试创建附加布局。 It has two containers.它有两个容器。

  • First is a fixed size box that scrolls horizontally.首先是一个固定大小的水平滚动框。
  • Second is a card that takes up remaining space with list view inside it.其次是一张卡片,它占用剩余空间,里面有列表视图。

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

tl;dr: the following code does what you want =D 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),
        ));
  }
}

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.在 taglist 小部件中,请注意您有一行作为列中唯一的小部件。 In the boardView, LisView is also the only element in a column;在boardView中,LisView也是一列唯一的元素;

Then I added more items to make sure both scrolls would work.然后我添加了更多项目以确保两个卷轴都能正常工作。 Adding scrollDirection: Axis.horizontal int the tagList made sure of that.添加scrollDirection: Axis.horizontal int tagList 确保了这一点。

At last, time to put it all together and display both elements.最后,是时候将它们放在一起并显示这两个元素了。 Remove the top Container as Scaffold is enough.移除顶部的 Container,因为Scaffold就足够了。 Then is was inly a matter of placing the boardView in the Expanded widget.然后只是将 boardView 放在Expanded小部件中的问题。

This was a fun exercise.这是一个有趣的练习。 =D =D

Use this trick:使用这个技巧:

  1. Wrap your Row in a SingleChildScrollView .将您的Row包裹在SingleChildScrollView
  2. Use scrollDirection: Axis.horizontal in the SingleChildScrollView .SingleChildScrollView使用scrollDirection: Axis.horizontal
  3. Wrap SingleChildScrollView in the your ListView .将 SingleChildScrollView 包装在您的ListView

Use CustomScrollView with SliverListview then you can achieve any kind of views with vertical scrolling horizontal scrolling.将 CustomScrollView 与 SliverListview 一起使用,然后您可以通过垂直滚动水平滚动实现任何类型的视图。

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

在此处输入图片说明

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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