admin管理员组文章数量:1356863
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 CustomPainter
s.
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 CustomPainter
s.
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
1 Answer
Reset to default 0Increased 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(),
)
本文标签:
版权声明:本文标题:carousel - How do I make Flutter CarouselSlider's items closer to each other without being clipped? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744058431a2583679.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论