最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

carousel - How do I make Flutter CarouselSlider's items closer to each other without being clipped? - Stack Overflow

programmeradmin2浏览0评论

I'm trying to display a list of images using a ShaderMask with a CustomPainter while applying a marquee effect. The current marquee package only supports text, so I'm using carousel_slider to achieve that.

I have four different CustomPainter implementations, and the images should appear in the order corresponding to these painters. However, I want the images to be placed closer together, but the current implementation restricts me - each item is separated due to the square shape (refer my image).

I've tried clipBehavior: Clip.none but the carousel item still remains clipped.

here's an unclipped version with larger viewportFraction, just for a clearer picture of the four CustomPainters.

Here's my minimal code.

import 'package:carousel_slider/carousel_slider.dart';
import 'package:figgyz/src/constants/size_config.dart';
import 'package:flutter/material.dart';
import 'dart:ui' as ui;

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

  @override
  State<HomeMarquee> createState() => _HomeMarqueeState();
}

class _HomeMarqueeState extends State<HomeMarquee> {
  late Future<List<ui.Image>> _imagesFuture;

  @override
  void initState() {
    super.initState();
    // Create the images future
    _imagesFuture = Future.wait([
      createShaderMaskImage(RPSCustomPainter1()),
      createShaderMaskImage(RPSCustomPainter2()),
      createShaderMaskImage(RPSCustomPainter3()),
      createShaderMaskImage(RPSCustomPainter4()),
      createShaderMaskImage(RPSCustomPainter1()),
      createShaderMaskImage(RPSCustomPainter2()),
      createShaderMaskImage(RPSCustomPainter3()),
      createShaderMaskImage(RPSCustomPainter4()),
    ]);
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<List<ui.Image>>(
      future: _imagesFuture,
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return const CircularProgressIndicator();
        }
        return SizedBox(
          height: 120,
          width: SizeConfig.screenWidth,
          child: ScrollingImageMarquee(
            images: snapshot.data!,
          ),
        );
      },
    );
  }

  /// **Helper function to create a masked image**
  Widget buildMaskedImage(ui.Image maskImage, double width, double height) {
    return ShaderMask(
      shaderCallback: (Rect bounds) {
        return ImageShader(
          maskImage,
          TileMode.clamp,
          TileMode.clamp,
          Matrix4.identity().storage,
        );
      },
      blendMode: BlendMode.dstIn, // Keeps only the masked area
      child: Imagework(
        '/120', // Replace with your image
        width: width,
        height: height,
        fit: BoxFit.cover,
      ),
    );
  }

  /// Converts CustomPainter into an Image to be used as a Shader
  Future<ui.Image> createShaderMaskImage(CustomPainter customPainter) async {
    const double width = 120;
    const double height = 120;
    final recorder = ui.PictureRecorder();
    final canvas = Canvas(recorder, Rect.fromLTWH(0, 0, width, height));

    customPainter.paint(canvas, const Size(width, height));

    final picture = recorder.endRecording();
    return picture.toImage(width.toInt(), height.toInt());
  }
}

class ScrollingImageMarquee extends StatelessWidget {
  final List<ui.Image> images;

  const ScrollingImageMarquee({
    super.key,
    required this.images,
  });

  /// **Helper function to create a masked image**
  Widget buildMaskedImage(ui.Image maskImage, double width, double height) {
    return ShaderMask(
      shaderCallback: (Rect bounds) {
        return ImageShader(
          maskImage,
          TileMode.clamp,
          TileMode.clamp,
          Matrix4.identity().storage,
        );
      },
      blendMode: BlendMode.dstIn, // Keeps only the masked area
      child: Imagework(
        '/120', // Replace with your image
        width: width,
        height: height,
        fit: BoxFit.cover,
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return CarouselSlider(
      options: CarouselOptions(
        aspectRatio: 1,
        viewportFraction: 0.25,
        clipBehavior: Clip.none,
        autoPlay: true,
        autoPlayCurve: Curves.linear,
        autoPlayAnimationDuration: Duration(seconds: 4),
        enlargeFactor: 0,
        pageSnapping: false
      ),
      items: [
        ...List.generate(images.length, (index) {
          return buildMaskedImage(images[index], 120, 120);
        }),
      ],
    );
  }
}

// Add all four CustomPainter classes here
class RPSCustomPainter1 extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    Path path_0 = Path();
    path_0.moveTo(size.width * 0.004033408, size.height * 0.1023288);
    path_0.cubicTo(size.width * 0.004033408, size.height * 0.04961475, size.width * 0.04605450,
        size.height * 0.006881458, size.width * 0.09789000, size.height * 0.006881458);
    path_0.lineTo(size.width * 0.6909275, size.height * 0.006881458);
    path_0.cubicTo(size.width * 0.7333042, size.height * 0.006881458, size.width * 0.7704242,
        size.height * 0.03575898, size.width * 0.7815133, size.height * 0.07735229);
    path_0.lineTo(size.width * 0.9949250, size.height * 0.8778220);
    path_0.cubicTo(size.width * 1.011100, size.height * 0.9385085, size.width * 0.9661667,
        size.height * 0.9982458, size.width * 0.9043417, size.height * 0.9982458);
    path_0.lineTo(size.width * 0.09789000, size.height * 0.9982458);
    path_0.cubicTo(size.width * 0.04605442, size.height * 0.9982458, size.width * 0.004033408,
        size.height * 0.9555085, size.width * 0.004033408, size.height * 0.9027966);
    path_0.lineTo(size.width * 0.004033408, size.height * 0.1023288);
    path_0.close();

    Paint paint_0_fill = Paint()..style = PaintingStyle.fill;
    paint_0_fill.color = Color(0xffD9D9D9).withOpacity(1.0);
    canvas.drawPath(path_0, paint_0_fill);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

class RPSCustomPainter2 extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    Path path_0 = Path();
    path_0.moveTo(size.width * 0.003862660, size.height * 0.1199154);
    path_0.cubicTo(size.width * -0.008665417, size.height * 0.05908325, size.width * 0.02868986, 0,
        size.width * 0.07967917, 0);
    path_0.lineTo(size.width * 0.7402778, 0);
    path_0.cubicTo(size.width * 0.7755972, 0, size.width * 0.8065278, size.height * 0.02912667,
        size.width * 0.8157708, size.height * 0.07107778);
    path_0.lineTo(size.width * 0.9936181, size.height * 0.8785812);
    path_0.cubicTo(size.width * 1.007097, size.height * 0.9397863, size.width * 0.9696528,
        size.height * 1.000034, size.width * 0.9181319, size.height * 1.000034);
    path_0.lineTo(size.width * 0.2459813, size.height * 1.000034);
    path_0.cubicTo(size.width * 0.2101861, size.height * 1.000034, size.width * 0.1789597,
        size.height * 0.9701282, size.width * 0.1701646, size.height * 0.9274188);
    path_0.lineTo(size.width * 0.003862660, size.height * 0.1199154);
    path_0.close();

    Paint paint_0_fill = Paint()..style = PaintingStyle.fill;
    paint_0_fill.color = Color(0xffD9D9D9).withOpacity(1.0);
    canvas.drawPath(path_0, paint_0_fill);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

class RPSCustomPainter3 extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    Path path_0 = Path();
    path_0.moveTo(size.width * 0.005917484, size.height * 0.1208419);
    path_0.cubicTo(size.width * -0.009296855, size.height * 0.05978359, size.width * 0.03414911, 0,
        size.width * 0.09373629, 0);
    path_0.lineTo(size.width * 0.9079758, 0);
    path_0.cubicTo(size.width * 0.9680565, 0, size.width * 1.011581, size.height * 0.06071487,
        size.width * 0.9954839, size.height * 0.1220615);
    path_0.lineTo(size.width * 0.7835919, size.height * 0.9293675);
    path_0.cubicTo(size.width * 0.7726605, size.height * 0.9710171, size.width * 0.7368742,
        size.height * 0.9998376, size.width * 0.6960855, size.height * 0.9998376);
    path_0.lineTo(size.width * 0.2948984, size.height * 0.9998376);
    path_0.cubicTo(size.width * 0.2536669, size.height * 0.9998376, size.width * 0.2176073,
        size.height * 0.9704017, size.width * 0.2070798, size.height * 0.9281538);
    path_0.lineTo(size.width * 0.005917484, size.height * 0.1208419);
    path_0.close();

    Paint paint_0_fill = Paint()..style = PaintingStyle.fill;
    paint_0_fill.color = Color(0xffD9D9D9).withOpacity(1.0);
    canvas.drawPath(path_0, paint_0_fill);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

class RPSCustomPainter4 extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    Path path_0 = Path();
    path_0.moveTo(size.width * 0.9980000, size.height * 0.09626325);
    path_0.cubicTo(size.width * 0.9980000, size.height * 0.04309855, size.width * 0.9559750, 0,
        size.width * 0.9041417, 0);
    path_0.lineTo(size.width * 0.3302533, 0);
    path_0.cubicTo(size.width * 0.2885575, 0, size.width * 0.2518608, size.height * 0.02821222,
        size.width * 0.2401650, size.height * 0.06925957);
    path_0.lineTo(size.width * 0.01012467, size.height * 0.8765641);
    path_0.cubicTo(size.width * -0.007433675, size.height * 0.9381880, size.width * 0.03762058,
        size.height * 0.9998376, size.width * 0.1002125, size.height * 0.9998376);
    path_0.lineTo(size.width * 0.9041417, size.height * 0.9998376);
    path_0.cubicTo(size.width * 0.9559750, size.height * 0.9998376, size.width * 0.9980000,
        size.height * 0.9567350, size.width * 0.9980000, size.height * 0.9035726);
    path_0.lineTo(size.width * 0.9980000, size.height * 0.09626325);
    path_0.close();

    Paint paint_0_fill = Paint()..style = PaintingStyle.fill;
    paint_0_fill.color = Color(0xffD9D9D9).withOpacity(1.0);
    canvas.drawPath(path_0, paint_0_fill);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

I'm trying to display a list of images using a ShaderMask with a CustomPainter while applying a marquee effect. The current marquee package only supports text, so I'm using carousel_slider to achieve that.

I have four different CustomPainter implementations, and the images should appear in the order corresponding to these painters. However, I want the images to be placed closer together, but the current implementation restricts me - each item is separated due to the square shape (refer my image).

I've tried clipBehavior: Clip.none but the carousel item still remains clipped.

here's an unclipped version with larger viewportFraction, just for a clearer picture of the four CustomPainters.

Here's my minimal code.

import 'package:carousel_slider/carousel_slider.dart';
import 'package:figgyz/src/constants/size_config.dart';
import 'package:flutter/material.dart';
import 'dart:ui' as ui;

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

  @override
  State<HomeMarquee> createState() => _HomeMarqueeState();
}

class _HomeMarqueeState extends State<HomeMarquee> {
  late Future<List<ui.Image>> _imagesFuture;

  @override
  void initState() {
    super.initState();
    // Create the images future
    _imagesFuture = Future.wait([
      createShaderMaskImage(RPSCustomPainter1()),
      createShaderMaskImage(RPSCustomPainter2()),
      createShaderMaskImage(RPSCustomPainter3()),
      createShaderMaskImage(RPSCustomPainter4()),
      createShaderMaskImage(RPSCustomPainter1()),
      createShaderMaskImage(RPSCustomPainter2()),
      createShaderMaskImage(RPSCustomPainter3()),
      createShaderMaskImage(RPSCustomPainter4()),
    ]);
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<List<ui.Image>>(
      future: _imagesFuture,
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return const CircularProgressIndicator();
        }
        return SizedBox(
          height: 120,
          width: SizeConfig.screenWidth,
          child: ScrollingImageMarquee(
            images: snapshot.data!,
          ),
        );
      },
    );
  }

  /// **Helper function to create a masked image**
  Widget buildMaskedImage(ui.Image maskImage, double width, double height) {
    return ShaderMask(
      shaderCallback: (Rect bounds) {
        return ImageShader(
          maskImage,
          TileMode.clamp,
          TileMode.clamp,
          Matrix4.identity().storage,
        );
      },
      blendMode: BlendMode.dstIn, // Keeps only the masked area
      child: Imagework(
        'https://picsum.photos/120', // Replace with your image
        width: width,
        height: height,
        fit: BoxFit.cover,
      ),
    );
  }

  /// Converts CustomPainter into an Image to be used as a Shader
  Future<ui.Image> createShaderMaskImage(CustomPainter customPainter) async {
    const double width = 120;
    const double height = 120;
    final recorder = ui.PictureRecorder();
    final canvas = Canvas(recorder, Rect.fromLTWH(0, 0, width, height));

    customPainter.paint(canvas, const Size(width, height));

    final picture = recorder.endRecording();
    return picture.toImage(width.toInt(), height.toInt());
  }
}

class ScrollingImageMarquee extends StatelessWidget {
  final List<ui.Image> images;

  const ScrollingImageMarquee({
    super.key,
    required this.images,
  });

  /// **Helper function to create a masked image**
  Widget buildMaskedImage(ui.Image maskImage, double width, double height) {
    return ShaderMask(
      shaderCallback: (Rect bounds) {
        return ImageShader(
          maskImage,
          TileMode.clamp,
          TileMode.clamp,
          Matrix4.identity().storage,
        );
      },
      blendMode: BlendMode.dstIn, // Keeps only the masked area
      child: Imagework(
        'https://picsum.photos/120', // Replace with your image
        width: width,
        height: height,
        fit: BoxFit.cover,
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return CarouselSlider(
      options: CarouselOptions(
        aspectRatio: 1,
        viewportFraction: 0.25,
        clipBehavior: Clip.none,
        autoPlay: true,
        autoPlayCurve: Curves.linear,
        autoPlayAnimationDuration: Duration(seconds: 4),
        enlargeFactor: 0,
        pageSnapping: false
      ),
      items: [
        ...List.generate(images.length, (index) {
          return buildMaskedImage(images[index], 120, 120);
        }),
      ],
    );
  }
}

// Add all four CustomPainter classes here
class RPSCustomPainter1 extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    Path path_0 = Path();
    path_0.moveTo(size.width * 0.004033408, size.height * 0.1023288);
    path_0.cubicTo(size.width * 0.004033408, size.height * 0.04961475, size.width * 0.04605450,
        size.height * 0.006881458, size.width * 0.09789000, size.height * 0.006881458);
    path_0.lineTo(size.width * 0.6909275, size.height * 0.006881458);
    path_0.cubicTo(size.width * 0.7333042, size.height * 0.006881458, size.width * 0.7704242,
        size.height * 0.03575898, size.width * 0.7815133, size.height * 0.07735229);
    path_0.lineTo(size.width * 0.9949250, size.height * 0.8778220);
    path_0.cubicTo(size.width * 1.011100, size.height * 0.9385085, size.width * 0.9661667,
        size.height * 0.9982458, size.width * 0.9043417, size.height * 0.9982458);
    path_0.lineTo(size.width * 0.09789000, size.height * 0.9982458);
    path_0.cubicTo(size.width * 0.04605442, size.height * 0.9982458, size.width * 0.004033408,
        size.height * 0.9555085, size.width * 0.004033408, size.height * 0.9027966);
    path_0.lineTo(size.width * 0.004033408, size.height * 0.1023288);
    path_0.close();

    Paint paint_0_fill = Paint()..style = PaintingStyle.fill;
    paint_0_fill.color = Color(0xffD9D9D9).withOpacity(1.0);
    canvas.drawPath(path_0, paint_0_fill);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

class RPSCustomPainter2 extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    Path path_0 = Path();
    path_0.moveTo(size.width * 0.003862660, size.height * 0.1199154);
    path_0.cubicTo(size.width * -0.008665417, size.height * 0.05908325, size.width * 0.02868986, 0,
        size.width * 0.07967917, 0);
    path_0.lineTo(size.width * 0.7402778, 0);
    path_0.cubicTo(size.width * 0.7755972, 0, size.width * 0.8065278, size.height * 0.02912667,
        size.width * 0.8157708, size.height * 0.07107778);
    path_0.lineTo(size.width * 0.9936181, size.height * 0.8785812);
    path_0.cubicTo(size.width * 1.007097, size.height * 0.9397863, size.width * 0.9696528,
        size.height * 1.000034, size.width * 0.9181319, size.height * 1.000034);
    path_0.lineTo(size.width * 0.2459813, size.height * 1.000034);
    path_0.cubicTo(size.width * 0.2101861, size.height * 1.000034, size.width * 0.1789597,
        size.height * 0.9701282, size.width * 0.1701646, size.height * 0.9274188);
    path_0.lineTo(size.width * 0.003862660, size.height * 0.1199154);
    path_0.close();

    Paint paint_0_fill = Paint()..style = PaintingStyle.fill;
    paint_0_fill.color = Color(0xffD9D9D9).withOpacity(1.0);
    canvas.drawPath(path_0, paint_0_fill);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

class RPSCustomPainter3 extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    Path path_0 = Path();
    path_0.moveTo(size.width * 0.005917484, size.height * 0.1208419);
    path_0.cubicTo(size.width * -0.009296855, size.height * 0.05978359, size.width * 0.03414911, 0,
        size.width * 0.09373629, 0);
    path_0.lineTo(size.width * 0.9079758, 0);
    path_0.cubicTo(size.width * 0.9680565, 0, size.width * 1.011581, size.height * 0.06071487,
        size.width * 0.9954839, size.height * 0.1220615);
    path_0.lineTo(size.width * 0.7835919, size.height * 0.9293675);
    path_0.cubicTo(size.width * 0.7726605, size.height * 0.9710171, size.width * 0.7368742,
        size.height * 0.9998376, size.width * 0.6960855, size.height * 0.9998376);
    path_0.lineTo(size.width * 0.2948984, size.height * 0.9998376);
    path_0.cubicTo(size.width * 0.2536669, size.height * 0.9998376, size.width * 0.2176073,
        size.height * 0.9704017, size.width * 0.2070798, size.height * 0.9281538);
    path_0.lineTo(size.width * 0.005917484, size.height * 0.1208419);
    path_0.close();

    Paint paint_0_fill = Paint()..style = PaintingStyle.fill;
    paint_0_fill.color = Color(0xffD9D9D9).withOpacity(1.0);
    canvas.drawPath(path_0, paint_0_fill);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

class RPSCustomPainter4 extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    Path path_0 = Path();
    path_0.moveTo(size.width * 0.9980000, size.height * 0.09626325);
    path_0.cubicTo(size.width * 0.9980000, size.height * 0.04309855, size.width * 0.9559750, 0,
        size.width * 0.9041417, 0);
    path_0.lineTo(size.width * 0.3302533, 0);
    path_0.cubicTo(size.width * 0.2885575, 0, size.width * 0.2518608, size.height * 0.02821222,
        size.width * 0.2401650, size.height * 0.06925957);
    path_0.lineTo(size.width * 0.01012467, size.height * 0.8765641);
    path_0.cubicTo(size.width * -0.007433675, size.height * 0.9381880, size.width * 0.03762058,
        size.height * 0.9998376, size.width * 0.1002125, size.height * 0.9998376);
    path_0.lineTo(size.width * 0.9041417, size.height * 0.9998376);
    path_0.cubicTo(size.width * 0.9559750, size.height * 0.9998376, size.width * 0.9980000,
        size.height * 0.9567350, size.width * 0.9980000, size.height * 0.9035726);
    path_0.lineTo(size.width * 0.9980000, size.height * 0.09626325);
    path_0.close();

    Paint paint_0_fill = Paint()..style = PaintingStyle.fill;
    paint_0_fill.color = Color(0xffD9D9D9).withOpacity(1.0);
    canvas.drawPath(path_0, paint_0_fill);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

Share Improve this question asked Mar 28 at 3:29 emilyemily 3821 gold badge3 silver badges12 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

Increased viewportFraction slightly to 0.3 Added small Padding around items to control spacing

CarouselSlider(
  options: CarouselOptions(
    aspectRatio: 1,
    viewportFraction: 0.3, 
    clipBehavior: Clip.none,
    autoPlay: true,
    autoPlayCurve: Curves.linear,
    autoPlayAnimationDuration: const Duration(seconds: 4),
    enlargeFactor: 0,
   
   
  ),
  items: images.map((image) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 2),
      child: buildMaskedImage(image, 120, 120),
    );
  }).toList(),
)

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论