Flutter Stacked Item ListView | Scroll Animation

Last updated Feb 02, 2021


Hello Guys, In this post we are going to cover create a stacked Listview Items with Aniimations.

This will give the good animation on scrolling the list items from top to bottom and viceversa

For making this animations we have used

AnimatedOpacity,

AnimatedContainer and

Transform widget.

By doing this you learn controll the listview in new way.

 

For this example we are showing the list of restaurants with their image,address...

 

Let's get started

Step 1: Create a Flutter project

Step 2: Create a Data class which contains the Restaurant details

The constants.dart file contains below data

const RESTAURANTS = [
  {
    "name":"Home Cravings",
    "brand":"Healthy Food",
    "location":"Miyapur, Hyderabad",
    "image":"https://b.zmtcdn.com/data/pictures/chains/9/19360109/e413ff9d1ca752e291a0f97a739873d5_featured_v2.jpg?output-format=webp"
  },{
    "name":"Shahi Schezwan",
    "brand":"North Indian",
    "location":"Hitech City, Hyderabad",
    "image":"https://b.zmtcdn.com/data/pictures/chains/6/19336726/e1b8d3814aca73d501d2a40bde81991d_featured_v2.jpg?output-format=webp"
  },
  {
    "name":"Healthy Shakes",
    "brand":"Beverages, Fast Food",
    "location":"Gachibowli, Hyderabad",
    "image":"https://b.zmtcdn.com/data/pictures/chains/5/19035705/e2db91bfb71093002ad4b179e4908cf8_featured_v2.jpeg?output-format=webp"
  },
  {
    "name":"PourHouse7",
    "brand":"Casual Dining, Bar - North Indian",
    "location":"Gachibowli",
    "image":"https://b.zmtcdn.com/data/pictures/4/18661594/0a37f7f12faf9295afce797564790182.jpg?output-format=webp&fit=around|771.75:416.25&crop=771.75:416.25;*,*"
  },
  {
    "name":"Bistro Milano",
    "brand":"Casual Dining - Continental,",
    "location":"Jubilee Hills, Hyderabad",
    "image":"https://b.zmtcdn.com/data/pictures/4/19331884/ab1dd2a50883329db53c1f7c1248c80a.jpg?output-format=webp&fit=around|771.75:416.25&crop=771.75:416.25;*,*"
  },
  {
    "name":"Drunkyard",
    "brand":"Casual Dining, Bar ",
    "location":"Gachibowli",
    "image":"https://b.zmtcdn.com/data/pictures/5/18265585/8f613fae7df645e4771ca774d33047f2_featured_v2.jpg?output-format=webp"
  }
  ,{
    "name":"Shahi Schezwan",
    "brand":"North Indian",
    "location":"Hitech City, Hyderabad",
    "image":"https://b.zmtcdn.com/data/pictures/chains/6/19336726/e1b8d3814aca73d501d2a40bde81991d_featured_v2.jpg?output-format=webp"
  },
  {
    "name":"Healthy Shakes",
    "brand":"Beverages, Fast Food",
    "location":"Gachibowli, Hyderabad",
    "image":"https://b.zmtcdn.com/data/pictures/chains/5/19035705/e2db91bfb71093002ad4b179e4908cf8_featured_v2.jpeg?output-format=webp"
  },
  {
    "name":"PourHouse7",
    "brand":"Casual Dining, Bar - North Indian",
    "location":"Gachibowli",
    "image":"https://b.zmtcdn.com/data/pictures/4/18661594/0a37f7f12faf9295afce797564790182.jpg?output-format=webp&fit=around|771.75:416.25&crop=771.75:416.25;*,*"
  }
];

 

Step 3: Update main.dart file

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final CategoriesScroller categoriesScroller = CategoriesScroller();
  ScrollController controller = ScrollController();
  bool closeTopContainer = false;
  double topContainer = 0;

  List<Widget> itemsData = [];

  void getPostsData() {
    List<dynamic> responseList = RESTAURANTS;
    List<Widget> listItems = [];
    responseList.forEach((post) {
      listItems.add(Container(
          height: 150,
            margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
          decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(20.0)), color: Colors.white, boxShadow: [
            BoxShadow(color: Colors.black.withAlpha(100), blurRadius: 10.0),
          ]),
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    Text(
                      post["name"],
                      style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                    ),
                    Container(
                      width: 100,
                      child: Text(
                        post["brand"],
                        style: const TextStyle(fontSize: 17, color: Colors.grey),
                      ),
                    ),
                    SizedBox(
                      height: 10,
                    ),
                    Text(
                      " ${post["location"]}",
                      style: const TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.bold),
                    )
                  ],
                ),
                Image.network(
                  "${post["image"]}",
                  height: 100,
                  width: 100,
                )
              ],
            ),
          )));
    });
    setState(() {
      itemsData = listItems;
    });
  }

  @override
  void initState() {
    super.initState();
    getPostsData();
    controller.addListener(() {

      double value = controller.offset/119;

      setState(() {
        topContainer = value;
        closeTopContainer = controller.offset > 50;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    final Size size = MediaQuery.of(context).size;
    final double categoryHeight = size.height*0.30;
    return SafeArea(
      child: Scaffold(
        backgroundColor: Colors.blueGrey,
        appBar: AppBar(
          elevation: 0,
          backgroundColor: Colors.pinkAccent,
          leading: Icon(
            Icons.menu,
            color: Colors.white,
          ),
          actions: <Widget>[
            IconButton(
              icon: Icon(Icons.search, color: Colors.white),
              onPressed: () {},
            ),
            IconButton(
              icon: Icon(Icons.person, color: Colors.white),
              onPressed: () {},
            )
          ],
        ),
        body: Container(
          height: size.height,
          margin: EdgeInsets.only(top: 10),
          child: Column(
            children: <Widget>[
              Card(
                color: Colors.lightGreen,
                child: Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceAround,
                    children: <Widget>[
                      Text(
                        "Popular Restaurants",
                        style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 20),
                      ),
                      Text(
                        "Menu",
                        style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 20),
                      ),
                    ],
                  ),
                ),
              ),
              const SizedBox(
                height: 10,
              ),
              AnimatedOpacity(
                duration: const Duration(milliseconds: 200),
                opacity: closeTopContainer?0:1,
                child: AnimatedContainer(
                    duration: const Duration(milliseconds: 200),
                    width: size.width,
                    alignment: Alignment.topCenter,
                    height: closeTopContainer?0:categoryHeight,
                    child: categoriesScroller),
              ),
              Expanded(
                  child: ListView.builder(
                      controller: controller,
                      itemCount: itemsData.length,
                      physics: BouncingScrollPhysics(),
                      itemBuilder: (context, index) {
                        double scale = 1.0;
                        if (topContainer > 0.5) {
                          scale = index + 0.5 - topContainer;
                          if (scale < 0) {
                            scale = 0;
                          } else if (scale > 1) {
                            scale = 1;
                          }
                        }
                        return Opacity(
                          opacity: scale,
                          child: Transform(
                            transform:  Matrix4.identity()..scale(scale,scale),
                            alignment: Alignment.bottomCenter,
                            child: Align(
                                heightFactor: 0.7,
                                alignment: Alignment.topCenter,
                                child: itemsData[index]),
                          ),
                        );
                      })),
            ],
          ),
        ),
      ),
    );
  }
}

class CategoriesScroller extends StatelessWidget {
  const CategoriesScroller();

  @override
  Widget build(BuildContext context) {
    final double categoryHeight = MediaQuery.of(context).size.height * 0.30 - 50;
    return SingleChildScrollView(
      physics: BouncingScrollPhysics(),
      scrollDirection: Axis.horizontal,
      child: Container(
        margin: const EdgeInsets.symmetric(vertical: 20, horizontal: 20),
        child: FittedBox(
          fit: BoxFit.fill,
          alignment: Alignment.topCenter,
          child: Row(
            children: <Widget>[
              Container(
                width: 150,
                margin: EdgeInsets.only(right: 20),
                height: categoryHeight,
                child: Card(
                  elevation: 10,
                  borderOnForeground: true,
                  child: Padding(
                    padding: const EdgeInsets.all(2.0),
                    child: Stack(
                      children: [
                        Image.network("https://b.zmtcdn.com/data/pictures/chains/6/19336726/e1b8d3814aca73d501d2a40bde81991d_featured_v2.jpg",width: 150,height: 150,),
                        Positioned(
                          top: 0,
                          left: 10,
                          child: Text(
                            "Famous",
                            style: TextStyle(fontSize: 20, color: Colors.red, fontWeight: FontWeight.bold),
                          ),),
                        Positioned(
                          bottom: 0,
                          left: 10,
                          child: Text(
                            "20 Items",
                            style: TextStyle(fontSize: 16, color: Colors.red),
                          ),),

                      ],

                    ),
                  ),
                ),
              ),
              Container(
                width: 150,
                margin: EdgeInsets.only(right: 20),
                height: categoryHeight,
                child: Card(
                  elevation: 10,
                  borderOnForeground: true,
                  child: Padding(
                    padding: const EdgeInsets.all(2.0),
                    child: Stack(
                      children: [
                        Image.network("https://b.zmtcdn.com/data/pictures/4/18661594/0a37f7f12faf9295afce797564790182.jpg?output-format=webp&fit=around|771.75:416.25&crop=771.75:416.25;*,*",width: 150,height: 150,),
                        Positioned(
                          top: 0,
                          left: 10,
                          child: Text(
                            "Newest",
                            style: TextStyle(fontSize: 20, color: Colors.green, fontWeight: FontWeight.bold),
                          ),),
                        Positioned(
                          bottom: 0,
                          left: 10,
                          child: Text(
                            "20 Items",
                            style: TextStyle(fontSize: 16, color: Colors.green),
                          ),),

                      ],

                    ),
                  ),
                ),
              ),
              Container(
                width: 150,
                margin: EdgeInsets.only(right: 20),
                height: categoryHeight,
                child: Card(
                  elevation: 10,
                  borderOnForeground: true,
                  child: Padding(
                    padding: const EdgeInsets.all(2.0),
                    child: Stack(
                      children: [
                        Image.network("https://b.zmtcdn.com/data/pictures/5/18265585/8f613fae7df645e4771ca774d33047f2_featured_v2.jpg?output-format=webp",width: 150,height: 150,fit: BoxFit.fill,),
                       Positioned(
                         top: 0,
                         left: 10,
                         child: Text(
                         "DrunkYard",
                         style: TextStyle(fontSize: 20, color: Colors.white, fontWeight: FontWeight.bold),
                       ),),
                        Positioned(
                          bottom: 0,
                          left: 10,
                          child: Text(
                            "20 Items",
                            style: TextStyle(fontSize: 16, color: Colors.white),
                          ),),

                      ],

                    ),
                  ),
                ),
              ),
              Container(
                width: 150,
                margin: EdgeInsets.only(right: 20),
                height: categoryHeight,
                child: Card(
                  elevation: 10,
                  borderOnForeground: true,
                  child: Padding(
                    padding: const EdgeInsets.all(2.0),
                    child: Stack(
                      children: [
                        Image.network("https://b.zmtcdn.com/data/pictures/chains/6/19336726/e1b8d3814aca73d501d2a40bde81991d_featured_v2.jpg",width: 150,height: 150,),
                        Positioned(
                          top: 0,
                          left: 10,
                          child: Text(
                            "Newest",
                            style: TextStyle(fontSize: 20, color: Colors.green, fontWeight: FontWeight.bold),
                          ),),
                        Positioned(
                          bottom: 0,
                          left: 10,
                          child: Text(
                            "20 Items",
                            style: TextStyle(fontSize: 16, color: Colors.green),
                          ),),

                      ],

                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

 

Step 4: Run the application

 

Stacked itesm listview flutter

 


Article Contributed By :
https://www.rrtutors.com/site_assets/profile/assets/img/avataaars.svg

2473 Views

Subscribe For Daily Updates

Flutter Questions
Android Questions