Flutter remove white space in ListView

102 views Asked by At

I want to make a cover for the cover of movies, which includes two rows, the first row rotates from right to left and the second row rotates from left to right. I also gave them an angle

But there is a series of white spaces around them and the list views do not expand How to remove that white space?

enter image description here

Here is my code:


class _CinemaTopBannerState extends State<CinemaTopBanner> {
  late ScrollController _scrollControllerLeftToRight;
  late ScrollController _scrollControllerRightToLeft;

  @override
  void initState() {
    super.initState();
    _scrollControllerLeftToRight = ScrollController();
    _scrollControllerRightToLeft = ScrollController();

    // Start auto-scrolling for the first row (left to right)
    Timer.periodic(Duration(milliseconds: 30), (timer) {
      if (_scrollControllerLeftToRight.hasClients) {
        double newOffset = _scrollControllerLeftToRight.offset + 1;

        if (newOffset < _scrollControllerLeftToRight.position.maxScrollExtent) {
          _scrollControllerLeftToRight.animateTo(
            newOffset,
            duration: Duration(milliseconds: 30),
            curve: Curves.linear,
          );
        } else {
          // Reset the position if the end is reached
          _scrollControllerLeftToRight.jumpTo(0.0);
        }
      }
    });

    // Start auto-scrolling for the second row (right to left)
    Timer.periodic(Duration(milliseconds: 30), (timer) {
      if (_scrollControllerRightToLeft.hasClients) {
        double newOffset = _scrollControllerRightToLeft.offset - 1;

        if (newOffset > 0) {
          _scrollControllerRightToLeft.animateTo(
            newOffset,
            duration: Duration(milliseconds: 30),
            curve: Curves.linear,
          );
        } else {
          // Reset the position if the start is reached
          _scrollControllerRightToLeft.jumpTo(_scrollControllerRightToLeft.position.maxScrollExtent);
        }
      }
    });
  }

  @override
  void dispose() {
    _scrollControllerLeftToRight.dispose();
    _scrollControllerRightToLeft.dispose();
    super.dispose();
  }

  List<String> movieCovers = [
    "https://m.media-amazon.com/images/I/71eHZFw+GlL._AC_UF894,1000_QL80_.jpg",
   ...
  ];

  @override
  Widget build(BuildContext context) {
    return Stack(
      alignment: Alignment.center,
      children: [
        SingleChildScrollView(
          padding: EdgeInsets.zero,
          physics: const NeverScrollableScrollPhysics(),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              SizedBox(
                height: 300,
                child: RotationTransition(
                  turns: const AlwaysStoppedAnimation(-29 / 360),
                  child: ListView.builder(
                    padding: EdgeInsets.zero,
                    scrollDirection: Axis.horizontal,
                    controller: _scrollControllerRightToLeft,
                    shrinkWrap: true,
                    itemCount: movieCovers.length,
                    reverse: true,
                    itemBuilder: (context, index) => _buildMovieCover(movieCovers[index]),
                  ),
                ),
              ),
              SizedBox(
                height: 50.w,
              ),
              SizedBox(
                height: 300,
                child: RotationTransition(
                  turns: const AlwaysStoppedAnimation(-29 / 360),
                  child: ListView.builder(
                    padding: EdgeInsets.zero,
                    scrollDirection: Axis.horizontal,
                    shrinkWrap: true,
                    controller: _scrollControllerLeftToRight,
                    reverse: true,
                    itemCount: movieCovers.length,
                    itemBuilder: (context, index) => _buildMovieCover(movieCovers[index]),
                  ),
                ),
              ),
            ],
          ),
        ),
        Container(
          width: 390.w,
          height: 810.w,
          decoration: BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment(0.00, -1.00),
              end: Alignment(0, 1),
              colors: [Colors.white, Colors.white.withOpacity(0), Colors.white],
            ),
          ),
        ),
        SizedBox(
          width: 265.w,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.end,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Text(
                Env.cinemaTitle,
                style: AppTextTheme.tvWidgetsTitleBanner,
              ),
              SizedBox(
                height: 8.w,
              ),
              Text(Env.cinemaTitleDescription, style: AppTextTheme.avaTvCinemaCartsDescription),
            ],
          ),
        )
      ],
    );
  }

  Widget _buildMovieCover(String coverPath) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 8.0),
      child: ClipRRect(
        borderRadius: BorderRadius.circular(12),
        child: CachedNetworkImage(
          imageUrl: coverPath,
          width: 213.w,
          height: 316.w,
          fit: BoxFit.fill,
        ),
      ),
    );
  }
}

enter image description here

3

There are 3 answers

4
Pratik Lakhani On BEST ANSWER

Try This!

Output:

enter image description here

Code:

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

  @override
  State<CinemaTopBanner> createState() => _CinemaTopBannerState();
}

class _CinemaTopBannerState extends State<CinemaTopBanner> {
   final ScrollController _scrollControllerLeftToRight = ScrollController();
   final ScrollController _scrollControllerRightToLeft = ScrollController();

  @override
  void initState() {
    super.initState();

    // Start auto-scrolling for the first row (left to right)
    Timer.periodic(const Duration(milliseconds: 30), (timer) {
      if (_scrollControllerLeftToRight.hasClients) {
        double newOffset = _scrollControllerLeftToRight.offset + 1;

        if (newOffset < _scrollControllerLeftToRight.position.maxScrollExtent) {
          _scrollControllerLeftToRight.animateTo(
            newOffset,
            duration: const Duration(milliseconds: 30),
            curve: Curves.linear,
          );
        } else {
          // Reset the position if the end is reached
          _scrollControllerLeftToRight.jumpTo(0.0);
        }
      }
    });

    // Start auto-scrolling for the second row (right to left)
    Timer.periodic(const Duration(milliseconds: 30), (timer) {
      if (_scrollControllerRightToLeft.hasClients) {
        double newOffset = _scrollControllerRightToLeft.offset - 1;

        if (newOffset > 0) {
          _scrollControllerRightToLeft.animateTo(
            newOffset,
            duration: const Duration(milliseconds: 30),
            curve: Curves.linear,
          );
        } else {
          _scrollControllerRightToLeft
              .jumpTo(_scrollControllerRightToLeft.position.maxScrollExtent);
        }
      }
    });
  }

  @override
  void dispose() {
    _scrollControllerLeftToRight.dispose();
    _scrollControllerRightToLeft.dispose();
    super.dispose();
  }

  List<String> movieCovers = [
    "https://m.media-amazon.com/images/I/71eHZFw+GlL._AC_UF894,1000_QL80_.jpg",
    'https://i.pravatar.cc/150?img=1',
    'https://i.pravatar.cc/150?img=2',
    'https://i.pravatar.cc/150?img=3',
    'https://i.pravatar.cc/150?img=4',
    'https://i.pravatar.cc/150?img=5',
    'https://i.pravatar.cc/150?img=6',
    'https://i.pravatar.cc/150?img=7',
    'https://i.pravatar.cc/150?img=8',
    'https://i.pravatar.cc/150?img=9',
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        alignment: Alignment.center,
        fit: StackFit.expand,
        children: [
          Column(
            mainAxisAlignment: MainAxisAlignment.start,
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.max,
            children: [
              Expanded(
                child: Center(
                  child: OverflowBox(
                    minWidth: 300,
                    maxHeight: 300,
                    maxWidth: MediaQuery.of(context).size.width * 2,
                    child: RotationTransition(
                      alignment: Alignment.bottomRight,
                      turns: const AlwaysStoppedAnimation(-29 / 360),
                      child: ListView.builder(
                        padding: EdgeInsets.zero,
                        scrollDirection: Axis.horizontal,
                        controller: _scrollControllerRightToLeft,
                        shrinkWrap: true,
                        itemCount: movieCovers.length,
                        reverse: true,
                        itemBuilder: (context, index) =>
                            _buildMovieCover(movieCovers[index]),
                      ),
                    ),
                  ),
                ),
              ),
              SizedBox(
                height: 230.w,
              ),
              Expanded(
                child: Center(
                  child: OverflowBox(
                    minWidth: 300,
                    maxHeight: 300,
                    maxWidth: MediaQuery.of(context).size.width * 2,
                    child: RotationTransition(
                      turns: const AlwaysStoppedAnimation(-29 / 360),
                      child: ListView.builder(
                        padding: EdgeInsets.zero,
                        scrollDirection: Axis.horizontal,
                        shrinkWrap: true,
                        controller: _scrollControllerLeftToRight,
                        reverse: true,
                        itemCount: movieCovers.length,
                        itemBuilder: (context, index) =>
                            _buildMovieCover(movieCovers[index]),
                      ),
                    ),
                  ),
                ),
              ),
            ],
          ),
          Container(
            width: 390.w,
            height: 810.w,
            decoration: BoxDecoration(
              gradient: LinearGradient(
                begin: const Alignment(0.00, -1.00),
                end: const Alignment(0, 1),
                colors: [
                  Colors.white,
                  Colors.white.withOpacity(0),
                  Colors.white
                ],
              ),
            ),
          ),
          SizedBox(
            width: 265.w,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.end,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                const Text(
                  'Env.cinemaTitle',
                  // style: AppTextTheme.tvWidgetsTitleBanner,
                ),
                SizedBox(
                  height: 8.w,
                ),
                const Text(
                  'Env.cinemaTitleDescription',
                  //  style: AppTextTheme.avaTvCinemaCartsDescription,
                ),
              ],
            ),
          )
        ],
      ),
    );
  }

  Widget _buildMovieCover(String coverPath) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 8.0),
      child: ClipRRect(
        borderRadius: BorderRadius.circular(12),
        child: CachedNetworkImage(
          imageUrl: coverPath,
          width: 213.w,
          height: 316.w,
          fit: BoxFit.fill,
        ),
      ),
    );
  }
0
Vladyslav Ulianytskyi On

You could try wrap your both rotated lists in OverflowBox and set maxWidth bigger then screen width:

static const _listWidthRatio = 1.6;

   

 ...
              SizedBox(
                height: 300,
                child: OverflowBox(
                  maxWidth: MediaQuery.of(context).size.width * _listWidthRatio, 
                  child: RotationTransition(
                    turns: const AlwaysStoppedAnimation(-29 / 360),
                    child: ListView.builder(
                      padding: EdgeInsets.zero,
                      scrollDirection: Axis.horizontal,
                      controller: _scrollControllerRightToLeft,
                      shrinkWrap: true,
                      itemCount: movieCovers.length,
                      reverse: true,
                      itemBuilder: (context, index) => _buildMovieCover(movieCovers[index]),
                    ),
                  ),
                ),
              ),
              const SizedBox(
                height: 50,
              ),
              SizedBox(
                height: 300,
                child: OverflowBox(
                  maxWidth: MediaQuery.of(context).size.width * _listWidthRatio,
                  child: RotationTransition(
                    turns: const AlwaysStoppedAnimation(-29 / 360),
                    child: ListView.builder(
                      padding: EdgeInsets.zero,
                      scrollDirection: Axis.horizontal,
                      shrinkWrap: true,
                      controller: _scrollControllerLeftToRight,
                      reverse: true,
                      itemCount: movieCovers.length,
                      itemBuilder: (context, index) => _buildMovieCover(movieCovers[index]),
                    ),
                  ),
                ),
              ),
    ...
1
Pratik Lakhani On

Try this!

Output:

enter image description here

Updated code:

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

  @override
  State<CinemaTopBanner> createState() => _CinemaTopBannerState();
}

class _CinemaTopBannerState extends State<CinemaTopBanner> {
  final ScrollController _scrollControllerLeftToRight = ScrollController();
  final ScrollController _scrollControllerRightToLeft = ScrollController();

  @override
  void initState() {
    super.initState();

    // Start auto-scrolling for the first row (left to right)
    Timer.periodic(const Duration(milliseconds: 30), (timer) {
      if (_scrollControllerLeftToRight.hasClients) {
        double newOffset = _scrollControllerLeftToRight.offset + 1;

        if (newOffset < _scrollControllerLeftToRight.position.maxScrollExtent) {
          _scrollControllerLeftToRight.animateTo(
            newOffset,
            duration: const Duration(milliseconds: 30),
            curve: Curves.linear,
          );
        } else {
          // Reset the position if the end is reached
          _scrollControllerLeftToRight.jumpTo(0.0);
        }
      }
    });

    // Start auto-scrolling for the second row (right to left)
    Timer.periodic(const Duration(milliseconds: 30), (timer) {
      if (_scrollControllerRightToLeft.hasClients) {
        double newOffset = _scrollControllerRightToLeft.offset - 1;

        if (newOffset > 0) {
          _scrollControllerRightToLeft.animateTo(
            newOffset,
            duration: const Duration(milliseconds: 30),
            curve: Curves.linear,
          );
        } else {
          _scrollControllerRightToLeft
              .jumpTo(_scrollControllerRightToLeft.position.maxScrollExtent);
        }
      }
    });
  }

  @override
  void dispose() {
    _scrollControllerLeftToRight.dispose();
    _scrollControllerRightToLeft.dispose();
    super.dispose();
  }

  List<String> movieCovers = [
    "https://m.media-amazon.com/images/I/71eHZFw+GlL._AC_UF894,1000_QL80_.jpg",
    'https://i.pravatar.cc/150?img=1',
    'https://i.pravatar.cc/150?img=2',
    'https://i.pravatar.cc/150?img=3',
    'https://i.pravatar.cc/150?img=4',
    'https://i.pravatar.cc/150?img=5',
    'https://i.pravatar.cc/150?img=6',
    'https://i.pravatar.cc/150?img=7',
    'https://i.pravatar.cc/150?img=8',
    'https://i.pravatar.cc/150?img=9',
    'https://i.pravatar.cc/150?img=10',
    'https://i.pravatar.cc/150?img=11',
    'https://i.pravatar.cc/150?img=12',
    'https://i.pravatar.cc/150?img=13',
    'https://i.pravatar.cc/150?img=14',
    'https://i.pravatar.cc/150?img=15',
    'https://i.pravatar.cc/150?img=16',
    'https://i.pravatar.cc/150?img=17',
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        alignment: Alignment.topCenter,
        fit: StackFit.loose,
        children: [
          Column(
            mainAxisSize: MainAxisSize.max,
            children: [
              Flexible(
                child: OverflowBox(
                  minWidth: 300,
                  maxHeight: 300,
                  maxWidth: MediaQuery.of(context).size.width * 2,
                  child: RotationTransition(
                    alignment: Alignment.topCenter,
                    turns: const AlwaysStoppedAnimation(-29 / 360),
                    child: ListView.builder(
                      padding: EdgeInsets.zero,
                      scrollDirection: Axis.horizontal,
                      controller: _scrollControllerRightToLeft,
                      shrinkWrap: true,
                      itemCount: movieCovers.length,
                      reverse: true,
                      itemBuilder: (context, index) =>
                          _buildMovieCover(movieCovers[index]),
                    ),
                  ),
                ),
              ),
              Flexible(
                child: Center(
                  child: OverflowBox(
                    minWidth: 300,
                    maxHeight: 300,
                    maxWidth: MediaQuery.of(context).size.width * 2,
                    child: RotationTransition(
                      turns: const AlwaysStoppedAnimation(-29 / 360),
                      child: ListView.builder(
                        padding: EdgeInsets.zero,
                        scrollDirection: Axis.horizontal,
                        shrinkWrap: true,
                        controller: _scrollControllerLeftToRight,
                        reverse: true,
                        itemCount: movieCovers.length,
                        itemBuilder: (context, index) =>
                            _buildMovieCover(movieCovers[index]),
                      ),
                    ),
                  ),
                ),
              ),
              SizedBox(
                height: 100.w,
              ),
            ],
          ),
          Container(
            width: double.infinity,
            height: double.infinity,
            decoration: BoxDecoration(
              gradient: LinearGradient(
                begin: const Alignment(0.00, -1.00),
                end: const Alignment(0, 1),
                colors: [
                  Colors.white,
                  Colors.white.withOpacity(0),
                  Colors.white
                ],
              ),
            ),
          ),
          Align(
            alignment: Alignment.bottomCenter,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.end,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                const Text(
                  'Env.cinemaTitle',
                  // style: AppTextTheme.tvWidgetsTitleBanner,
                ),
                SizedBox(
                  height: 8.w,
                ),
                const Text(
                  'Env.cinemaTitleDescription',
                  //  style: AppTextTheme.avaTvCinemaCartsDescription,
                ),
              ],
            ),
          )
        ],
      ),
    );
  }

  Widget _buildMovieCover(String coverPath) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 8.0),
      child: ClipRRect(
        borderRadius: BorderRadius.circular(12),
        child: CachedNetworkImage(
          imageUrl: coverPath,
          width: 213.w,
          height: 316.w,
          fit: BoxFit.fill,
        ),
      ),
    );
  }
}