Search code examples
flutterflutter-listview

A ListView of two ListView builders


I'm cloning a social media app design where there are two ListView: an horizontal one for the user stories, and a vertical one for user posts. I've already built them and they work, but I would like to wrap them in a unique ListView with stories at the top, and posts under the stories. If I scroll, the stories should disappear after a while and I should see only posts. How can I do?

UserStory widget:


class UserStory extends StatelessWidget {
  const UserStory({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        SizedBox(
          height: 100,
          child: ListView.builder(
            physics: const BouncingScrollPhysics(),
            padding: const EdgeInsets.symmetric(horizontal: 10.0),
            scrollDirection: Axis.horizontal,
            itemCount: 10,
            itemBuilder: (context, i) {
              return Container(
                width: 60,
                height: 60,
                margin: const EdgeInsets.symmetric(horizontal: 5.0),
                decoration: BoxDecoration(
                  shape: BoxShape.circle,
                  color: Colors.grey.shade200,
                ),
              );
            },
          ),
        )
      ],
    );
  }
}

UserPost widget:


class UserPost extends StatelessWidget {
  const UserPost({super.key});

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: ListView.builder(
        physics: const BouncingScrollPhysics(),
        itemCount: 10,
        itemBuilder: (context, index) {
          return Padding(
            padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
            child: Column(
              children: [
                ListTile(
                  leading: Container(
                    width: 40,
                    height: 40,
                    decoration: BoxDecoration(
                        shape: BoxShape.circle,
                        color: Colors.grey.shade200
                    ),
                  ),
                  title: const Text("Ava Sadie"),
                  subtitle: Text("Sylhet, Bangladesh", style: Theme.of(context).textTheme.labelSmall,),
                  trailing: IconButton(splashRadius: 20.0, onPressed: () {}, icon: const Icon(Icons.more_horiz_outlined),),
                ),
                Container(
                  width: double.infinity,
                  height: 250,
                  margin: const EdgeInsets.only(top: 5.0),
                  decoration: BoxDecoration(
                    color: Colors.grey.shade200,
                    borderRadius: BorderRadius.circular(25.0),
                  ),
                ),
                const SizedBox(height: 5),
                Row(
                  mainAxisAlignment: MainAxisAlignment.start,
                  children: [
                    Row(
                      children: [
                        IconButton(splashRadius: 20.0, onPressed: () {}, icon: const Icon(Icons.favorite_outline,),),
                        Text("6.2k", style: Theme.of(context).textTheme.displaySmall,),
                      ],
                    ),

                    const SizedBox(width: 20,),

                    Row(
                      children: [
                        IconButton(splashRadius: 20.0, onPressed: () {}, icon: const Icon(Icons.messenger_outline,),),
                        Text("2.1k", style: Theme.of(context).textTheme.displaySmall,),
                      ],
                    ),

                    const SizedBox(width: 20,),

                    Row(
                      children: [
                        IconButton(splashRadius: 20.0, onPressed: () {}, icon: const Icon(Icons.bookmark_border_outlined),),
                        Text("3.5k", style: Theme.of(context).textTheme.displaySmall,),
                      ],
                    ),
                  ],
                )
              ],
            ),
          );
        }
      ),
    );
  }
}

HomePage:

import 'package:atlas/pages/widgets/user_post.dart';
import 'package:atlas/pages/widgets/user_story.dart';
import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        forceMaterialTransparency: true,
        leading: IconButton(
          padding: const EdgeInsets.symmetric(horizontal: 30.0),
          onPressed: () {},
          icon: const Icon(Icons.camera_alt_outlined),
        ),
        actions: [
          IconButton(
            padding: const EdgeInsets.symmetric(horizontal: 30.0),
            onPressed: () {},
            icon: const Icon(Icons.email_outlined),
          ),
        ],
      ),
      body: const Column(
        children: <Widget>[
          UserStory(),
          UserPost(),
        ],
      ),
    );
  }
}

I tried to wrap the body in a ListView, but it spams some errors and I see neither Stories nor posts

You find two photos of the actual screen, When opening homepage:

here it's all ok, but when I want to scroll stories should disappear and I should see only posts. Instead, as you can see in the second photo, stories stay at the top.

Second photo while scrolling:


Solution

  • You could replace Column to ListView at HomePage.

    body: ListView(
      children: <Widget>[
        UserStory(),
        UserPost(),
      ],
    ),
    

    And add shrinkWrap: true to ListView at UserPost widget.

    return ListView.builder(
      shrinkWrap: true,
      ...,
    );