This tutorial will explore how to use a Listview in a master-detail scenario. This is a context where whereby we list data in a listview and when a single item is clicked we open a new screen or page to show the details of that item.

In that case the master is the list of items in our listview while the detail is the detail page. We will look at several examples of such type of project.

Example 1: Flutter Recipe App - Master Detail

This project is a recipe app template that teaches master detail in flutter. The master page comprises a list of recipes in a listview as well as a bottomNavigationBar widget. The detail page covers a single a recipe in detail.

Here are the screenshot demos for the project:

Here is the master page:

Recipe App Master Page

And here is the detail page:

Recipe App detail page

Step 1: Create Project

Start by creating an empty Android Studio project.

Step 2: Dependencies

We will need some basic widgets as dependencies. Declare them in your pubspec.yaml as follows:

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^0.1.2
  intl: ^0.16.1
  percent_indicator: ^2.1.1

Then flutter pub get to fetch them.

Step 3: Create Meal Model

Next you need to create a model class to represent a single alongside it's properties like name, image, calories count, preparation as well as ingredients:

/model/meal.dart

class Meal {
  final String mealTime, name, imagePath, kiloCaloriesBurnt, timeTaken;
  final String preparation;
  final List ingredients;

  Meal({this.mealTime, this.name, this.imagePath, this.kiloCaloriesBurnt, this.timeTaken, this.preparation, this.ingredients});
}

List<Meal> meals = [
  Meal(
      mealTime: "BREAKFAST",
      name: "Fruit Granola",
      kiloCaloriesBurnt: "271",
      timeTaken: "10",
      imagePath: "assets/fruit_granola.jpg",
      ingredients: [
        "1 cup of granola",
        "1 banana",
        "1/2 cup of raisins",
        "1 tbsp of honey",
      ],
      preparation:
      '''Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do nunc sed id. Mauris ultrices eros in cursus turpis.'''),
  Meal(
      mealTime: "DINNER",
      name: "Pesto Pasta",
      kiloCaloriesBurnt: "612",
      timeTaken: "15",
      imagePath: "assets/pesto_pasta.jpg",
      ingredients: [
        "1 cup of granola",
        "1 banana",
        "1/2 cup of raisins",
        "1 tbsp of honey",
      ],
      preparation:
      '''Lorem ipsum dolor sit amet, c purus   id. Mauris ultrices eros in cursus turpis.'''),
  Meal(
      mealTime: "SNACK",
      name: "Keto Snack",
      kiloCaloriesBurnt: "414",
      timeTaken: "16",
      imagePath: "assets/keto_snack.jpg",
      ingredients: [
        "1 cup of granola",
        "1 banana",
        "1/2 cup of raisins",
        "1 tbsp of honey",
      ],
      preparation:
      '''Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do nunc sed id. Mauris ultrices eros in cursus turpis.'''),
];

Step 4: Create Details page

You now need to create a detail page for our app. In this case it will be created a statelesswidget;

meal_details.dart

import 'package:flutter/material.dart';
import 'main.dart';
import 'model/meal.dart';

class MealDetail extends StatelessWidget {
  final Meal meal;

  const MealDetail({Key key, this.meal}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      body: ListView(
        children: <Widget>[
          Stack(
           children: <Widget>[
             Container(
               width: MediaQuery.of(context).size.width,
                 height: MediaQuery.of(context).size.width - 70,
                 decoration: BoxDecoration(
                     borderRadius:
                     BorderRadius.vertical(bottom: Radius.circular(30)),
                     boxShadow: [
                       BoxShadow(
                           offset: Offset(0, 2),
                           color: Colors.black54,
                           blurRadius: 6)
                     ]),
                 child: ClipRRect(
                   borderRadius:
                   BorderRadius.vertical(bottom: Radius.circular(30)),
                   child: Image(
                     image: AssetImage(meal.imagePath),
                     fit: BoxFit.cover,
                   ),
                 )),
             Positioned(
               top: 0,
               left: 0,
               child: IconButton(
                 icon: Icon(Icons.arrow_back),
                 iconSize: 25,
                 color: Colors.black,
                 onPressed: () {
                   Navigator.pop(context);
                 },
               ),
             ),
           ],
          ),
          SizedBox(height: 8),
          ListTile(
            title: Text(meal.mealTime,
                style: TextStyle(
                    fontWeight: FontWeight.w400,
                    fontSize: 14,
                    color: Colors.black)),
            subtitle: Text(
              meal.name,
              style: TextStyle(
                  fontSize: 22,
                  fontWeight: FontWeight.w800,
                  color: Colors.black87),
            ),
            trailing: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Row(
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
//                    SizedBox(
//                      width: 30,
//                    ),
                    Text(
                      "${meal.kiloCaloriesBurnt} kcal",
                      style: TextStyle(color: Colors.grey, fontWeight: FontWeight.w600, fontSize: 15),
                    ),
                  ],
                ),
                SizedBox(
                  height: 4,
                ),
                Row(
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    Icon(
                      Icons.access_time,
                      color: Colors.grey,
                      size: 20,
                    ),
                    SizedBox(
                      width: 5,
                    ),
                    Text(
                      "${meal.timeTaken} mins",
                      style: TextStyle(color: Colors.grey, fontWeight: FontWeight.w600, fontSize: 14),
                    )
                  ],
                ),
              ],
            ),
          ),
          SizedBox(height: 8,),
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16),
            child: Text(
              "INGREDIENTS :",
              style: TextStyle(
                fontWeight: FontWeight.w800,
                fontSize: 15,
                color: Colors.blueGrey,
              ),
            ),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
               Text(meal.ingredients[0]),
                Text(meal.ingredients[1]),
                Text(meal.ingredients[2]),
                Text(meal.ingredients[3]),
              ],
            ),
          ),
          SizedBox(height: 10,),
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16),
            child: Text(
              "PREPARATION :",
              style: TextStyle(
                fontWeight: FontWeight.w800,
                fontSize: 15,
                color: Colors.blueGrey,
              ),
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(left: 16,right: 16,bottom: 20),
            child: Text(
              meal.preparation,
              style: TextStyle(
                fontSize: 15,
              ),
            ),
          )
        ],
      ),
    );
  }
}

Step 4: Create Main class

This class will be responsible for constructing the master page with the listview as well as BottomNavigationBar.

Create it as follows:

main.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutterui/meal_details.dart';
import 'package:vector_math/vector_math_64.dart' as math;
import 'model/meal.dart';
import 'package:intl/intl.dart';
import 'package:percent_indicator/percent_indicator.dart';

void main() {
  runApp(MaterialApp(
    title: "flutter demo",
    debugShowCheckedModeBanner: false,
    home: MyApp(),
  ));
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    final heightOfMedia = MediaQuery.of(context).size.height;
    final widthOfMedia = MediaQuery.of(context).size.width;
    final today = DateTime.now();
    return Scaffold(
      backgroundColor: Colors.grey[300],
      bottomNavigationBar: ClipRRect(
        borderRadius: BorderRadius.vertical(top: Radius.circular(25)),
        child: BottomNavigationBar(
          iconSize: 22,
          items: [
            BottomNavigationBarItem(
                icon: Icon(Icons.home), title: Text("Home")),
            BottomNavigationBarItem(
                icon: Icon(Icons.dashboard), title: Text("dashboard")),
            BottomNavigationBarItem(
                icon: Icon(Icons.person), title: Text("profile"))
          ],
        ),
      ),
      body: Stack(
        children: <Widget>[
          Positioned(
            top: 0,
            height: heightOfMedia * 0.35,
            left: 0,
            right: 0,
            child: ClipRRect(
              borderRadius: const BorderRadius.vertical(
                  bottom: const Radius.circular(30)),
              child: Container(
                padding:
                    EdgeInsets.only(top: 33, left: 17, bottom: 5, right: 15),
                color: Colors.white,
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    ListTile(
                      title: Text("${DateFormat("EEEE").format(today)},${DateFormat("d MMMM").format(today)} ",
                          style: TextStyle(
                              fontWeight: FontWeight.w400,
                              fontSize: 14,
                              color: Colors.black)),
                      subtitle: Text(
                        "Hello Saif",
                        style: TextStyle(
                            fontSize: 20,
                            fontWeight: FontWeight.w800,
                            color: Colors.black87),
                      ),
                      trailing:
                          ClipOval(child: Image(
                            image: AssetImage("assets/saif.jpg"),
                            fit: BoxFit.contain,
                          )),
                    ),
                    Row(
                      children: <Widget>[
                        _RadialProgress(
                            width: widthOfMedia * 0.35,
                            height: widthOfMedia * 0.35,
                        progress:0.7),
                        SizedBox(
                          width: 12.5,
                        ),
                        Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          mainAxisSize: MainAxisSize.max,
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: <Widget>[
                            Row(
                              children: <Widget>[
                                Container(
                                  child: Column(
                                    crossAxisAlignment: CrossAxisAlignment.start
                                    ,children: <Widget>[
                                      Padding(
                                        padding: const EdgeInsets.only(left:8.0),
                                        child: Text("Protein"),
                                      ),
                                      Row(
                                        children: <Widget>[
                                          LinearPercentIndicator(
                                            width: widthOfMedia*0.37,
                                            animation: true,
                                            progressColor: Colors.blue,
                                            lineHeight: 10,
                                            percent: 0.7,
                                          ),
                                          Text("30 % left",style: TextStyle(
                                              fontSize: 12,
                                              fontWeight: FontWeight.w100
                                          ),)
                                        ],
                                      )
                                    ],
                                  ),
                                ),
                              ],
                            ),
                            SizedBox(
                              height: 13,
                            ),
                            Container(
                              child: Column(
                                crossAxisAlignment: CrossAxisAlignment.start
                                ,children: <Widget>[
                                  Padding(
                                    padding: const EdgeInsets.only(left:8.0),
                                    child: Text("Carbs"),
                                  ),
                                  Row(
                                    children: <Widget>[
                                      LinearPercentIndicator(
                                        width: widthOfMedia*0.37,
                                        animation: true,
                                        progressColor: Colors.yellow,
                                        lineHeight: 10,
                                        percent: 0.4,
                                      ),
                                      Text("60 % left",style: TextStyle(
                                          fontSize: 12,
                                          fontWeight: FontWeight.w100
                                      ),)
                                    ],
                                  )
                                ],
                              ),
                            ),
                            SizedBox(
                              height: 13,
                            ),
                            Container(
                              child: Column(
                                  crossAxisAlignment: CrossAxisAlignment.start,
                                children: <Widget>[
                                  Padding(
                                    padding: const EdgeInsets.only(left:8.0),
                                    child: Text("Fat"),
                                  ),
                                  Row(
                                    children: <Widget>[
                                      LinearPercentIndicator(
                                        width: widthOfMedia*0.38,
                                        animation: true,
                                        progressColor: Colors.red,
                                        lineHeight: 10,
                                        percent: 0.5,
                                      ),
                                      Text("50 % left",style: TextStyle(
                                          fontSize: 12,
                                          fontWeight: FontWeight.w100
                                      ),)
                                    ],
                                  )
                                ],
                              ),
                            )
                          ],
                        )
                      ],
                    )
                  ],
                ),
              ),
            ),
          ),
          Positioned(
            top: heightOfMedia * 0.37,
            left: 0,
            right: 0,
            child: Container(
              height: heightOfMedia * 0.55,
              //color: Colors.grey,
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                  Padding(
                    padding: EdgeInsets.only(left: 25, bottom: 4),
                    child: Text(
                      "Meals",
                      style: TextStyle(fontSize: 19, color: Colors.black87),
                    ),
                  ),
                  Expanded(
                    child: meal_card(),
                  ),
                  SizedBox(
                    height: 8,
                  ),
                  Expanded(
                    child: Container(
                      margin: EdgeInsets.only(left: 15, right: 15, bottom: 5),
                      decoration: BoxDecoration(
                          borderRadius: BorderRadius.all(Radius.circular(30)),
                          gradient: LinearGradient(
                              colors: [Color(0xFF9CCC65), Color(0xFF558B2F)])),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          SizedBox(
                            height: 15,
                          ),
                          Padding(
                            padding: const EdgeInsets.only(left: 16, bottom: 5),
                            child: Text(
                              "Home Workout",
                              style: TextStyle(
                                  fontSize: 18,
                                  fontWeight: FontWeight.w600,
                                  color: Colors.white),
                            ),
                          ),
                          Padding(
                            padding: const EdgeInsets.only(left: 16),
                            child: Text(
                              "Upper Body",
                              style: TextStyle(
                                  fontSize: 22,
                                  fontWeight: FontWeight.w600,
                                  color: Colors.white),
                            ),
                          ),
                          Row(
                            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                            children: <Widget>[
                              IconButton(
                                icon: Image.asset("assets/chest.png"),
                                color: Colors.blue,
                                iconSize: 65,
                              ),
                              IconButton(
                                icon: Image.asset("assets/back.png"),
                                color: Colors.blue,
                                iconSize: 65,
                              ),
                              IconButton(
                                icon: Image.asset("assets/biceps.png"),
                                color: Colors.blue,
                                iconSize: 65,
                              )
                            ],
                          )
                        ],
                      ),
                    ),
                  )
                ],
              ),
            ),
          )
        ],
      ),
    );
  }
}
class _LinearProgress extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    throw UnimplementedError();
  }

}
class _RadialProgress extends StatelessWidget {
  final double height, width,progress;

  const _RadialProgress({Key key, this.height, this.width,this.progress}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return CustomPaint(
      painter: _RadialPaint(progress: 0.7),
      child: Container(
        height: height,
        width: width,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text("30 %",style: TextStyle(
              fontWeight: FontWeight.w800,
              fontSize: 21,
              color: Colors.black87
            ),),
            Text("Left",style: TextStyle(
                fontWeight: FontWeight.w400,
                fontSize: 16,
                color: Colors.black54
            ),)
          ],
        ),
      ),
    );
  }
}

class _RadialPaint extends CustomPainter {
  final double progress;

  _RadialPaint({this.progress});

  @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
    Paint paint = Paint()
      ..strokeWidth = 10
      ..color = Color(0xFF558B2F)
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;

    Offset centre = Offset(size.width / 2, size.height / 2);
   // canvas.drawCircle(centre, size.width / 2, paint);
    double relativeProgress=360*progress;
    canvas.drawArc(Rect.fromCircle(center: centre, radius: size.width / 2),
        math.radians(-90), math.radians(-relativeProgress), false, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    return true;
  }
}

class meal_card extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
      margin: EdgeInsets.only(left: 20),
      child: ListView.builder(
        scrollDirection: Axis.horizontal,
        itemCount: meals.length,
        itemBuilder: (BuildContext context, int index) {
          Meal meal = meals[index];
          return GestureDetector(
            onTap: (){
              Navigator.of(context).push(MaterialPageRoute(builder: (context)=>MealDetail(meal: meal,)));
            },
            child: Container(
              margin: EdgeInsets.only(right: 15),
              decoration: BoxDecoration(
                color: Colors.white,
                borderRadius: BorderRadius.all(Radius.circular(20)),
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                //mainAxisSize: MainAxisSize.max,
                children: <Widget>[
                  Flexible(
                    fit: FlexFit.tight,
                    child: ClipRRect(
                      borderRadius:
                          BorderRadius.vertical(top: Radius.circular(20)),
                      child: Image.asset(meal.imagePath,
                          width: 150, fit: BoxFit.cover),
                    ),
                  ),
                  Flexible(
                    fit: FlexFit.tight,
                    child: Padding(
                      padding: const EdgeInsets.only(left: 13),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: <Widget>[
                          SizedBox(
                            height: 5,
                          ),
                          Text(meal.mealTime,
                              style: TextStyle(color: Colors.grey, fontSize: 13)),
                          Text(meal.name,
                              style: TextStyle(
                                  fontSize: 16,
                                  fontWeight: FontWeight.bold,
                                  color: Colors.black87)),
                          Text("${meal.kiloCaloriesBurnt} kcal",
                              style: TextStyle(
                                  color: Colors.blueGrey, fontSize: 13)),
                          Row(
                            children: <Widget>[
                              Icon(
                                Icons.timer,
                                size: 17,
                                color: Colors.grey,
                              ),
                              Text("${meal.timeTaken} min",
                                  style: TextStyle(
                                      color: Colors.blueGrey, fontSize: 13)),
                            ],
                          ),
                          SizedBox(height: 5)
                        ],
                      ),
                    ),
                  )
                ],
              ),
            ),
          );
        },
      ),
    );
  }
}

Run

Copy the code or download it in the link below, build and run.

Reference

Here are the reference links:

Number Link
1. Download Example
2. Follow code author

Example 2: Flutter Moveis App - Listview Master Detail

This is yet another beautiful app that teaches you implementation of Master Detail with ListView in flutter. The master page will list the movies while when a single movie item in our listview is clicked we open the detail page where we render the details of that movie.

Here are the demo in screenshots in iOS:

Here is the master page:

Master Page iOS

and here is the detail page:

Detail Page iOS

Step 1: Create Project

Start by creating an empty flutter project.

Step 2: Dependencies

No third party dependency is needed.

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^0.1.2

Step 3: Create Helper classes

Start by creating helper classes and files such as:

(a). /constant/Constant.dart

String SPLASH_SCREEN='SPLASH_SCREEN';
String HOME_SCREEN='HOME_SCREEN';
String ITEM_DETAILS_SCREEN='ITEM_DETAILS_SCREEN';
String LISTVIEW_ITEM_DETAILS_SCREEN='LISTVIEW_ITEM_DETAILS_SCREEN';

Step 4: Create Model class

Create the model class that will represent a single Movie:

/model/Item.dart

import 'package:meta/meta.dart';

class Item {
  int id;
  String name;
  String category;
  String releaseDate;
  String releaseDateDesc;
  String directors;
  String runtime;
  String desc;
  double rating;
  String imageUrl;
  String bannerUrl;
  String trailerImg1;
  String trailerImg2;
  String trailerImg3;

  Item({
    @required this.id,
    @required this.name,
    @required this.category,
    @required this.directors,
    @required this.releaseDate,
    @required this.releaseDateDesc,
    @required this.runtime,
    @required this.desc,
    @required this.rating,
    @required this.imageUrl,
    @required this.bannerUrl,
    @required this.trailerImg1,
    @required this.trailerImg2,
    @required this.trailerImg3,
  });
}

Step 5: Create Screens

Create screens as follows:

(a). /screen/SplashScreen.dart

import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_listview_app/constant/Constant.dart';

class SplashScreen extends StatefulWidget {
  @override
  SplashScreenState createState() => new SplashScreenState();
}

class SplashScreenState extends State<SplashScreen>
    with SingleTickerProviderStateMixin {
  var _visible = true;

  AnimationController animationController;
  Animation<double> animation;

  startTime() async {
    var _duration = new Duration(seconds: 3);
    return new Timer(_duration, navigationPage);
  }

  void navigationPage() {
    Navigator.of(context).pushReplacementNamed(HOME_SCREEN);
  }

  @override
  void initState() {
    super.initState();
    animationController = new AnimationController(
      vsync: this,
      duration: new Duration(seconds: 2),
    );
    animation =
        new CurvedAnimation(parent: animationController, curve: Curves.easeOut);

    animation.addListener(() => this.setState(() {}));
    animationController.forward();

    setState(() {
      _visible = !_visible;
    });
    startTime();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        fit: StackFit.expand,
        children: <Widget>[
          new Column(
            mainAxisAlignment: MainAxisAlignment.end,
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              Padding(
                padding: EdgeInsets.only(bottom: 30.0),
                child: new Image.asset(
                  'assets/images/powered_by.png',
                  height: 25.0,
                  fit: BoxFit.scaleDown,
                ),
              )
            ],
          ),
          new Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              new Image.asset(
                'assets/images/logo.png',
                width: animation.value * 250,
                height: animation.value * 250,
              ),
            ],
          ),
        ],
      ),
    );
  }
}

(b). /screen/GetRatings.dart

import 'package:flutter/material.dart';

class GetRatings extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.only(top: 2.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.start,
        children: <Widget>[
          Icon(
            Icons.star,
            size: 15.0,
            color: Colors.yellow,
          ),
          Icon(
            Icons.star,
            size: 15.0,
            color: Colors.yellow,
          ),
          Icon(
            Icons.star,
            size: 15.0,
            color: Colors.yellow,
          ),
          Icon(
            Icons.star,
            size: 15.0,
            color: Colors.yellow,
          ),
          Icon(
            Icons.star_half,
            size: 15.0,
            color: Colors.yellow,
          ),
        ],
      ),
    );
  }
}

(c). /screen/ListViewItemDetails.dart

import 'package:flutter/material.dart';
import 'package:flutter_listview_app/model/Item.dart';
import 'package:flutter_listview_app/screen/GetRatings.dart';

class ListItemDetails extends StatelessWidget {
  final Item item;

  ListItemDetails(this.item);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      primary: true,
      appBar: AppBar(
        title: Text(item.name),
      ),
      backgroundColor: Color(0xFF761322),
      body: ListView(
        children: <Widget>[
          HeaderBanner(this.item),
          GetTags(),
          Container(
            padding: const EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 20.0),
            child: Text(
              item.desc,
              style: TextStyle(
                fontSize: 13.0,
                color: Colors.white,
              ),
            ),
          ),
          InkWell(
            onTap: () => {},
            child: Container(
              margin: EdgeInsets.fromLTRB(50.0, 5.0, 50.0, 5.0),
              width: 80.0,
              height: 40.0,
              decoration: BoxDecoration(
                color: Colors.white,
                borderRadius: BorderRadius.circular(30.0),
              ),
              child: Center(
                child: Text(
                  'Watch Movies',
                  style: TextStyle(
                    fontSize: 18.0,
                    color: Color(0xFF761322),
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ),
            ),
          ),
          Container(
            padding: const EdgeInsets.fromLTRB(10.0, 20.0, 10.0, 10.0),
            child: Text(
              'Trailers',
              style: TextStyle(
                fontSize: 16.0,
                color: Colors.white,
              ),
            ),
          ),
          GetTrailers(this.item),
        ],
        // ),
        //],
      ),
    );
  }
}

class GetTags extends StatelessWidget {
  GetTags();

  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      margin: EdgeInsets.fromLTRB(5.0, 0.0, 5.0, 10.0),
      height: 35.0,
      child: ListView(
        scrollDirection: Axis.horizontal,
        children: <Widget>[
          InkWell(
            onTap: () => {},
            child: Container(
              width: 100.0,
              height: 35.0,
              margin: EdgeInsets.only(
                left: 5.0,
                right: 5.0,
              ),
              decoration: BoxDecoration(
                color: Color(0xFF761322),
                border: Border.all(color: Colors.white, width: 1.0),
                borderRadius: BorderRadius.circular(30.0),
              ),
              child: Center(
                child: Text(
                  'Action',
                  style: TextStyle(fontSize: 16.0, color: Colors.white),
                ),
              ),
            ),
          ),
          InkWell(
            onTap: () => {},
            child: Container(
              width: 100.0,
              height: 35.0,
              margin: EdgeInsets.only(
                left: 5.0,
                right: 5.0,
              ),
              decoration: BoxDecoration(
                color: Color(0xFF761322),
                border: Border.all(color: Colors.white, width: 1.0),
                borderRadius: BorderRadius.circular(30.0),
              ),
              child: Center(
                child: Text(
                  'Adventure',
                  style: TextStyle(fontSize: 16.0, color: Colors.white),
                ),
              ),
            ),
          ),
          InkWell(
            onTap: () => {},
            child: Container(
              width: 100.0,
              height: 35.0,
              margin: EdgeInsets.only(
                left: 5.0,
                right: 5.0,
              ),
              decoration: BoxDecoration(
                color: Color(0xFF761322),
                border: Border.all(color: Colors.white, width: 1.0),
                borderRadius: BorderRadius.circular(30.0),
              ),
              child: Center(
                child: Text(
                  'Fantasy',
                  style: TextStyle(fontSize: 16.0, color: Colors.white),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class SetTagsItem extends StatelessWidget {
  final String tag;

  SetTagsItem(this.tag);

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: () => {},
      child: Container(
        width: 100.0,
        height: 35.0,
        margin: EdgeInsets.only(
          left: 5.0,
          right: 5.0,
        ),
        decoration: BoxDecoration(
          color: Color(0xFF761322),
          border: Border.all(color: Colors.white, width: 1.0),
          borderRadius: BorderRadius.circular(30.0),
        ),
        child: Center(
          child: Text(
            tag,
            style: TextStyle(fontSize: 16.0, color: Colors.white),
          ),
        ),
      ),
    );
  }
}

class HeaderBanner extends StatelessWidget {
  final Item item;

  HeaderBanner(this.item);

  @override
  Widget build(BuildContext context) {
    return Material(
      elevation: 0.0,
      child: Container(
        height: 380.0,
        child: Stack(
          fit: StackFit.expand,
          children: <Widget>[
            HeaderImage(this.item.bannerUrl),
            HeaderContent(this.item),
          ],
        ),
      ),
    );
  }
}

class HeaderImage extends StatelessWidget {
  final String bannerUrl;

  HeaderImage(this.bannerUrl);

  @override
  Widget build(BuildContext context) {
    return Image.asset(
      bannerUrl,
      width: 600.0,
      height: 380.0,
      fit: BoxFit.cover,
    );
  }
}

class HeaderContent extends StatelessWidget {
  final Item item;

  HeaderContent(this.item);

  @override
  Widget build(BuildContext context) {
    return Align(
      alignment: Alignment.bottomLeft,
      child: Container(
        //color: Colors.black.withOpacity(0.1),
        constraints: BoxConstraints.expand(
          height: 110.0,
        ),
        child: Padding(
          padding: const EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 0.0),
          child: Container(
            child: Row(
              children: [
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Container(
                        padding: const EdgeInsets.only(bottom: 1.0),
                        child: Text(
                          item.name,
                          overflow: TextOverflow.ellipsis,
                          maxLines: 1,
                          style: TextStyle(
                            fontSize: 26.0,
                            color: Colors.white,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                      ),
                      GetRatings(),
                      Container(
                        margin: EdgeInsets.fromLTRB(0.0, 5.0, 0.0, 0.0),
                        child: Text(
                          item.directors,
                          style: TextStyle(
                            color: Colors.white,
                            fontSize: 15.0,
                          ),
                        ),
                      ),
                      Container(
                        //margin: EdgeInsets.fromLTRB(0.0, 5.0, 0.0, 0.0),
                        child: Text(
                          item.releaseDateDesc,
                          style: TextStyle(
                            color: Colors.white,
                            fontSize: 15.0,
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
          //child:
        ),
      ),
    );
  }
}

class GetTrailers extends StatelessWidget {
  final Item item;

  GetTrailers(this.item);

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 10.0),
      height: 100.0,
      child: ListView(
        scrollDirection: Axis.horizontal,
        children: <Widget>[
          Container(
            child: Image.asset(
              item.trailerImg1,
              width: 160.0,
              height: 100.0,
              fit: BoxFit.cover,
            ),
          ),
          Container(
            padding: const EdgeInsets.only(left: 5.0),
            child: Image.asset(
              item.trailerImg2,
              width: 160.0,
              height: 100.0,
              fit: BoxFit.cover,
            ),
          ),
          Container(
            padding: const EdgeInsets.only(left: 5.0),
            child: Image.asset(
              item.trailerImg3,
              width: 160.0,
              height: 100.0,
              fit: BoxFit.cover,
            ),
          ),
        ],
      ),
    );
  }
}

(d). /screen/ItemList.dart

import 'package:flutter/material.dart';
import 'package:flutter_listview_app/screen/GetRatings.dart';
import 'package:flutter_listview_app/screen/ListViewItemDetails.dart';
import 'package:flutter_listview_app/model/Item.dart';

class ItemList extends StatelessWidget {
  final Item item;

  const ItemList({@required this.item});

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => ListItemDetails(this.item),
          ),
        );
      },
      child: Card(
        elevation: 1.0,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(10.0),
        ),
        child: Container(
          padding: EdgeInsets.all(8.0),
          child: Row(
            //crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Image.asset(
                item.imageUrl,
                height: 120.0,
                width: 120.0,
                fit: BoxFit.fitHeight,
              ),
              Flexible(
                //padding: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0),
                child: Padding(
                  padding: EdgeInsets.only(left: 10.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      Text(
                        item.name,
                        overflow: TextOverflow.ellipsis,
                        maxLines: 1,
                        style: TextStyle(
                          fontSize: 12.0,
                          color: Color(0xFFD73C29),
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                      Text(
                        item.category,
                        style: TextStyle(
                          color: Colors.black54,
                          fontSize: 9.0,
                        ),
                      ),
                      SizedBox(height: 0.0),
                      GetRatings(),
                      SizedBox(height: 2.0),
                      Row(
                        mainAxisAlignment: MainAxisAlignment.start,
                        children: <Widget>[
                          Container(
                            margin: EdgeInsets.only(right: 4.0),
                            child: Column(
                              children: <Widget>[
                                Text(
                                  'RELEASE DATE:',
                                  style: TextStyle(
                                    color: Colors.black38,
                                    fontSize: 9.0,
                                    fontWeight: FontWeight.bold,
                                  ),
                                ),
                                Text(
                                  item.releaseDate,
                                  style: TextStyle(
                                    color: Colors.black,
                                    fontSize: 9.0,
                                    fontWeight: FontWeight.bold,
                                  ),
                                ),
                              ],
                            ),
                          ),
                          Container(
                            margin: EdgeInsets.only(left: 4.0),
                            child: Column(
                              children: <Widget>[
                                Text(
                                  'RUNTIME:',
                                  style: TextStyle(
                                    color: Colors.black38,
                                    fontSize: 9.0,
                                    fontWeight: FontWeight.bold,
                                  ),
                                ),
                                Text(
                                  item.runtime,
                                  style: TextStyle(
                                    color: Colors.black,
                                    fontSize: 9.0,
                                    fontWeight: FontWeight.bold,
                                  ),
                                ),
                              ],
                            ),
                          ),
                        ],
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class HeaderContent extends StatelessWidget {
  final Item item;

  HeaderContent(this.item);

  @override
  Widget build(BuildContext context) {
    return Align(
      alignment: Alignment.bottomLeft,
      child: Container(
        alignment: Alignment.center,
        margin: EdgeInsets.only(left: 10.0, top: 5.0, right: 10.0),
        child: Row(
          children: [
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    item.name,
                    style: TextStyle(
                      fontSize: 12.0,
                      color: Color(0xFFD73C29),
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  Text(
                    item.category,
                    style: TextStyle(
                      color: Colors.black54,
                      fontSize: 9.0,
                    ),
                  ),
                  GetRatings(),
                  MovieDesc(this.item),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class MovieDesc extends StatelessWidget {
  final Item item;

  MovieDesc(this.item);

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.fromLTRB(0.0, 5.0, 0.0, 0.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.start,
        children: <Widget>[
          Container(
            child: Column(
              children: <Widget>[
                Text(
                  'RELEASE DATE:',
                  style: TextStyle(
                    color: Colors.black38,
                    fontSize: 9.0,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                Text(
                  item.releaseDate,
                  style: TextStyle(
                    color: Colors.black,
                    fontSize: 9.0,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ],
            ),
          ),
          Container(
            margin: EdgeInsets.only(left: 10.0, right: 10.0),
            child: Column(
              children: <Widget>[
                Text(
                  'RUNTIME:',
                  style: TextStyle(
                    color: Colors.black38,
                    fontSize: 9.0,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                Text(
                  item.runtime,
                  style: TextStyle(
                    color: Colors.black,
                    fontSize: 9.0,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

(e). /screen/HomeScreen.dart

import 'package:flutter/material.dart';
import 'package:flutter_listview_app/model/Item.dart';
import 'package:flutter_listview_app/screen/ItemList.dart';

class HomeScreen extends StatelessWidget {
  List<Item> itemList;

  @override
  Widget build(BuildContext context) {
    itemList = _itemList();

    return Scaffold(
      appBar: AppBar(
        title: Text('Movies'),
      ),
      body: _listView(),
    );
  }

  Widget _listView() {
    return Container(
      padding: EdgeInsets.all(8.0),
      child: Column(
        children: <Widget>[
          Expanded(
              child: ListView(
            padding: EdgeInsets.symmetric(vertical: 8.0),
            children: itemList
                .map(
                  (Item) => ItemList(item: Item),
                )
                .toList(),
          )),
        ],
      ),
    );
  }

  List<Item> _itemList() {
    return [
      Item(
        id: 0,
        name: 'Avengers: Infinity War',
        category: 'Action, Adventure, Fantasy',
        desc: 'The Avengers and their allies must be willing '
            'to sacrifice all in an attempt to defeat '
            'the powerful Thanos before his blitz of '
            'devastation and ruin puts an end to the universe.'
            '\nAs the Avengers and their allies have continued '
            'to protect the world from threats too large for '
            'any one hero to handle, a danger has emerged '
            'from the cosmic shadows: Thanos.',
        rating: 8.7,
        directors: 'Directors: Anthony Russo, Joe Russo',
        releaseDate: '27 April 2018',
        releaseDateDesc: 'USA (2018), 2h 29min',
        runtime: '2h 29min',
        bannerUrl: 'assets/images/movie_banner_1.png',
        imageUrl: 'assets/images/ic_preview_1.png',
        trailerImg1: 'assets/images/ic_thumb_11.png',
        trailerImg2: 'assets/images/ic_thumb_12.png',
        trailerImg3: 'assets/images/ic_thumb_13.png',
      ),
      Item(
        id: 1,
        name: 'Transformers: The Last Knight',
        category: 'Action, Adventure, Sci-Fi',
        desc: 'Autobots and Decepticons are at war, with humans '
            'on the sidelines. Optimus Prime is gone. The key to '
            'saving our future lies buried in the secrets of the past, '
            'in the hidden history of Transformers on Earth.',
        rating: 5.2,
        directors: 'Directors: Michael Bay',
        releaseDate: '21 June 2017',
        releaseDateDesc: 'USA (2017), 2h 34min',
        runtime: '2h 34min',
        bannerUrl: 'assets/images/movie_banner_2.png',
        imageUrl: 'assets/images/ic_preview_2.png',
        trailerImg1: 'assets/images/ic_thumb_21.png',
        trailerImg2: 'assets/images/ic_thumb_21.png',
        trailerImg3: 'assets/images/ic_thumb_21.png',
      ),
      Item(
        id: 2,
        name: 'Pacific Rim: Uprising',
        category: 'Action, Adventure, Sci-Fi',
        desc: 'Jake Pentecost, son of Stacker Pentecost, reunites with '
            'Mako Mori to lead a new generation of Jaeger pilots, including '
            'rival Lambert and 15-year-old hacker Amara, against a new Kaiju threat.',
        rating: 5.7,
        directors: 'Directors: Steven S. DeKnight',
        releaseDate: '23 March 2018',
        releaseDateDesc: 'USA (2018), 1h 51min',
        runtime: '1h 51min',
        bannerUrl: 'assets/images/movie_banner_3.png',
        imageUrl: 'assets/images/ic_preview_3.png',
        trailerImg1: 'assets/images/ic_thumb_31.png',
        trailerImg2: 'assets/images/ic_thumb_31.png',
        trailerImg3: 'assets/images/ic_thumb_31.png',
      ),
      Item(
        id: 3,
        name: 'Thor: Ragnarok',
        category: 'Action, Adventure, Comedy',
        desc: 'Thor is imprisoned on the planet Sakaar, and must '
            'race against time to return to Asgard and stop Ragnarök, '
            'the destruction of his world, at the hands of the powerful '
            'and ruthless villain Hela.',
        rating: 7.9,
        directors: 'Directors: Taika Waititi',
        releaseDate: '3 November 2017',
        releaseDateDesc: 'USA (2017), 2h 10min',
        runtime: '2h 10min',
        bannerUrl: 'assets/images/movie_banner_4.png',
        imageUrl: 'assets/images/ic_preview_4.png',
        trailerImg1: 'assets/images/ic_thumb_41.png',
        trailerImg2: 'assets/images/ic_thumb_41.png',
        trailerImg3: 'assets/images/ic_thumb_41.png',
      ),
    ];
  }
}

Step 6: Create main class

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_listview_app/constant/Constant.dart';
import 'package:flutter_listview_app/screen/HomeScreen.dart';
import 'package:flutter_listview_app/screen/SplashScreen.dart';

void main() => runApp(
      MaterialApp(
        title: 'ListView Demo',
        debugShowCheckedModeBanner: false,
        theme: ThemeData(
          primarySwatch: Colors.red,
          accentColor: Color(0xFF761322),
        ),
        home: SplashScreen(),
        routes: <String, WidgetBuilder>{
          SPLASH_SCREEN: (BuildContext context) => SplashScreen(),
          HOME_SCREEN:(BuildContext context)=>HomeScreen(),
        },
      ),
    );

Run

Copy the code or download it in the link below, build and run.

Reference

Here are the reference links:

Number Link
1. Download Example
2. Follow code author