CybeX
CybeX

Reputation: 2406

(Flutter) Align horizontal listview at bottom of screen (and related issues)

My application requires a horizontal listview where items can be tapped on.

Rough Layout description below

Container
|_ Stack
    |_ (background content)
    |_ Column (overlay, align end vertically)
          |_ Expand (fill up space above)
          |_ ListView (horizontal)
          |_ SizedBox (padding)

Problem

When inserting this ListView (or uncommenting), Flutter throws a fit with the following error:

======== Exception caught by rendering library =====================================================
The following assertion was thrown during performLayout():
'package:flutter/src/rendering/viewport.dart': Failed assertion: line 1852 pos 16: 'constraints.hasBoundedHeight': is not true.


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: 
  ListView file:///C:/Users/CybeX/CozyUp/cozyup-mobile-flutter/lib/ui/components/ui_component_partner_selector.dart:76:13
When the exception was thrown, this was the stack: 
#2      RenderShrinkWrappingViewport.performLayout (package:flutter/src/rendering/viewport.dart:1852:16)
#3      RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
#4      RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:113:14)
#5      RenderObject.layout (package:flutter/src/rendering/object.dart:1777:7)
#6      RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:113:14)
...
The following RenderObject was being processed when the exception was fired: RenderShrinkWrappingViewport#400d9 relayoutBoundary=up16 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
...  needs compositing
...  parentData: <none> (can use size)
...  constraints: BoxConstraints(0.0<=w<=395.4, 0.0<=h<=Infinity)
...  size: MISSING
...  axisDirection: right
...  crossAxisDirection: down
...  offset: ScrollPositionWithSingleContext#72596(offset: 0.0, range: null..null, viewport: null, ScrollableState, ClampingScrollPhysics -> RangeMaintainingScrollPhysics, IdleScrollActivity#601d0, ScrollDirection.idle)
RenderObject: RenderShrinkWrappingViewport#400d9 relayoutBoundary=up16 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE

I tried wrapping the ListView in an Expanded but this recentered it in the middle of the screen as suggested here. I tried adding an Expanded top as suggested here, but Flutter doesn't like this.

fwiw, tried ShrinkWrap too (wasn't really expecting much here - didn't help either)

MVCE

import 'package:flutter/material.dart';

void main() async {
  runApp(MaterialApp(home: MyApp()));
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    List<String> imgSrc = [
      "https://randomuser.me/api/portraits/women/84.jpg",
          "https://randomuser.me/api/portraits/men/82.jpg",
          "https://randomuser.me/api/portraits/women/11.jpg",
          "https://randomuser.me/api/portraits/women/61.jpg",
          "https://randomuser.me/api/portraits/men/1.jpg",
    ];

    List<Widget> _listviewItems() {
      return imgSrc
          .map((e) => Image.network(e))
          .map((e) => Image(
                image: e.image,
                fit: BoxFit.scaleDown,
                width: 64,
              ))
          .toList();
    }

    Widget w = Container(
        child: Stack(
      children: [
        // Center(
        //   child: cards.first,
        // ),
        Column(
          mainAxisAlignment: MainAxisAlignment.end,
          children: [
            Expanded(
                child: ListView(
              padding: const EdgeInsets.all(0.0),
              scrollDirection: Axis.horizontal,
              children: _listviewItems(),
            )),
            SizedBox(
              height: 8,
            )
          ],
        )
      ],
    ));

    return Scaffold(
      body: Container(width: double.infinity, child: w),
    );
  }
}

enter image description here

Image above is with ListView wrapped in Expanded, i.e.

   Column(
      mainAxisAlignment: MainAxisAlignment.end,
      children: [
        Expanded(
            child: Container()
        ),
        ListView(
          padding: const EdgeInsets.all(0.0),
          scrollDirection: Axis.horizontal,
          children: _listviewItems(),
        ),
        SizedBox(
          height: 8,
        )
      ],
    )

Question:

How can I align a horizontal listview to the bottom of the screen allowing for horizontal scrolling (as shown in the image above)?

fyi: I added shrinkWrap: true and wrapped the Column (inside the Stack) inside of an Extended (as suggested here) resulting in an error that leads me here which suggests the opposite - quite frustrating!

Upvotes: 0

Views: 2251

Answers (2)

chunhunghan
chunhunghan

Reputation: 54407

You can copy paste run 2 full code below
Solution 1: You can use bottomSheet
code snippet

return Scaffold(
      appBar: ...
      body: ...
      bottomSheet: SizedBox(height: 64, child: listview()),
    );

Solution 2: Wrap ListView with Align and SizedBox
code snippet

Expanded(
        child: Align(
      alignment: Alignment.bottomCenter,
      child: SizedBox(
        height: 64,
        child: ListView(
          padding: const EdgeInsets.all(0.0),
          scrollDirection: Axis.horizontal,
          children: _listviewItems(),
        ),

working demo

enter image description here

full code of solution 1

import 'package:flutter/material.dart';

void main() async {
  runApp(MaterialApp(home: MyApp()));
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    List<String> imgSrc = [
      "https://randomuser.me/api/portraits/women/84.jpg",
      "https://randomuser.me/api/portraits/men/82.jpg",
      "https://randomuser.me/api/portraits/women/11.jpg",
      "https://randomuser.me/api/portraits/women/61.jpg",
      "https://randomuser.me/api/portraits/men/1.jpg",
      "https://randomuser.me/api/portraits/men/2.jpg",
      "https://randomuser.me/api/portraits/men/3.jpg",
      "https://randomuser.me/api/portraits/men/4.jpg",
      "https://randomuser.me/api/portraits/men/5.jpg",
      "https://randomuser.me/api/portraits/men/6.jpg",
      "https://randomuser.me/api/portraits/men/7.jpg",
    ];

    Widget listview() {
      List<Widget> imgList = imgSrc
          .map((e) => Image.network(e))
          .map((e) => Image(
                image: e.image,
                fit: BoxFit.scaleDown,
                width: 64,
              ))
          .toList();

      return ListView(
        scrollDirection: Axis.horizontal,
        children: imgList,
      );
    }

    return Scaffold(
      appBar: AppBar(title: const Text('AppBar Demo')),
      body: Center(
        child: Container(
            //width: double.infinity,
            child: Text("other content")),
      ),
      bottomSheet: SizedBox(height: 64, child: listview()),
    );
  }
}

full code of solution 2

import 'package:flutter/material.dart';

void main() async {
  runApp(MaterialApp(home: MyApp()));
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    List<String> imgSrc = [
      "https://randomuser.me/api/portraits/women/84.jpg",
      "https://randomuser.me/api/portraits/men/82.jpg",
      "https://randomuser.me/api/portraits/women/11.jpg",
      "https://randomuser.me/api/portraits/women/61.jpg",
      "https://randomuser.me/api/portraits/men/1.jpg",
      "https://randomuser.me/api/portraits/men/2.jpg",
      "https://randomuser.me/api/portraits/men/3.jpg",
      "https://randomuser.me/api/portraits/men/4.jpg",
      "https://randomuser.me/api/portraits/men/5.jpg",
      "https://randomuser.me/api/portraits/men/6.jpg",
      "https://randomuser.me/api/portraits/men/7.jpg",
    ];

    List<Widget> _listviewItems() {
      return imgSrc
          .map((e) => Image.network(e))
          .map((e) => Image(
                image: e.image,
                fit: BoxFit.scaleDown,
                width: 64,
              ))
          .toList();
    }

    Widget w = Container(
        child: Stack(
      children: [
        // Center(
        //   child: cards.first,
        // ),
        Column(
          mainAxisAlignment: MainAxisAlignment.end,
          children: [
            Expanded(
                child: Align(
              alignment: Alignment.bottomCenter,
              child: SizedBox(
                height: 64,
                child: ListView(
                  padding: const EdgeInsets.all(0.0),
                  scrollDirection: Axis.horizontal,
                  children: _listviewItems(),
                ),
              ),
            )),
            SizedBox(
              height: 8,
            )
          ],
        )
      ],
    ));

    return Scaffold(
      body: Container(width: double.infinity, child: w),
    );
  }
}

Upvotes: 0

Hamza Siddiqui
Hamza Siddiqui

Reputation: 716

wrap your list view with container and give height to container like this

Container(
 height:50,
  ListView(
      padding: const EdgeInsets.all(0.0),
      scrollDirection: Axis.horizontal,
      children: _listviewItems(),
    ),
),

Upvotes: 0

Related Questions