我的目标是在背景图像上方水平滑动颜色滤镜,如下所示:
我正在使用多个矩阵来创建一些颜色滤镜,并且我知道我必须通过 ColorFiltered
包装图像才能应用滤镜:这没有问题。
然后,当我尝试滑动这些过滤器时,我不知道如何应用它们。 这是演示代码:
class PageFilters extends StatefulWidget {
double ratio;
String file;
PageFilters({required this.ratio,required this.file});
@override
_PageFiltersState createState() => _PageFiltersState();
}
class _PageFiltersState extends State<PageFilters> {
FilterList filters = FilterList();
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Stack(
alignment: Alignment.center,
children: [
Image.file(File(widget.file),fit: BoxFit.cover),
PageView.builder(
physics: const ClampingScrollPhysics(),
itemCount: filters.list.length,
itemBuilder: (context,index) {
return Center(
child: ColorFiltered(
colorFilter: ColorFilter.matrix(filters.list[index].matrix),
child: Container(
color: index == 0 ? Colors.transparent : Colors.white.withOpacity(0.1),
alignment: Alignment.center,
height: Constants.maxWidth*widget.ratio,
),
),
);
}),
],
),
);
}
}
只要过滤器可见,过滤器是否在滑动期间直接应用于图像并不重要,我可以检索它并稍后应用它。
感谢您提供任何线索或解决方案
最佳答案
这个想法是沿着与页面移动相反的方向移动图像 - 因此,如果 PageView
将子项(ColorFiltered
小部件)移动到右侧,您必须将孙子(Image
小部件)移动到左侧,使其保持在同一个位置 - 虽然理论上很简单,但实际上可能会让人头疼 - 幸运的是有 CustomSingleChildLayout
小部件这为我们节省了大量工作:
class ColorFilteredPageView extends StatefulWidget {
final ImageProvider image;
final List<ColorFilter> filters;
final List<String> filterNames;
ColorFilteredPageView({
required this.image,
required this.filters,
required this.filterNames,
}) : assert(filters.length == filterNames.length);
@override
_ColorFilteredPageViewState createState() => _ColorFilteredPageViewState();
}
class _ColorFilteredPageViewState extends State<ColorFilteredPageView> {
PageController controller = PageController();
@override
Widget build(BuildContext context) {
return PageView.builder(
controller: controller,
itemCount: widget.filters.length,
itemBuilder: (ctx, index) {
return Stack(
fit: StackFit.expand,
children: [
ClipRect(
child: ColorFiltered(
colorFilter: widget.filters[index],
child: CustomSingleChildLayout(
delegate: _ColorFilteredPageViewDelegate(index, controller),
child: Image(image: widget.image, fit: BoxFit.cover),
),
),
),
...outlinedName(widget.filterNames[index]),
],
);
},
);
}
}
/*
// NOTE
// AnimatedBuilder + FractionalTranslation can also be used
// instead of CustomSingleChildLayout but it is kind of workaround imho....
ClipRect(
child: ColorFiltered(
colorFilter: widget.filters[index],
child: AnimatedBuilder(
animation: controller,
builder: (context, child) {
return FractionalTranslation(
translation: controller.position.haveDimensions?
Offset(controller.page - index, 0) : Offset.zero,
child: child,
);
},
child: Image(image: widget.image, fit: BoxFit.cover),
),
),
),
*/
class _ColorFilteredPageViewDelegate extends SingleChildLayoutDelegate {
final int index;
final PageController controller;
_ColorFilteredPageViewDelegate(this.index, this.controller) : super(relayout: controller);
Offset getPositionForChild(Size size, Size childSize) {
// print('index: $index, dx: ${controller.offset - index * size.width}');
return Offset(controller.offset - index * size.width, 0);
}
@override
bool shouldRelayout(covariant SingleChildLayoutDelegate oldDelegate) => false;
}
Iterable<Widget> outlinedName(String name) {
final styles = [
TextStyle(
foreground: Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeWidth = 4
..maskFilter = MaskFilter.blur(BlurStyle.solid, 2),
fontWeight: FontWeight.w500,
),
TextStyle(
color: Colors.white,
fontWeight: FontWeight.w500,
),
];
return styles.map((style) => Align(
alignment: Alignment(0, 0.75),
child: Text(name,
textScaleFactor: 2.5,
textAlign: TextAlign.center,
style: style,
),
),);
}
现在有一些示例过滤器:
final myFilters = [
ColorFilter.mode(Colors.transparent, BlendMode.dst),
ColorFilter.mode(Colors.teal, BlendMode.softLight),
ColorFilter.mode(Colors.teal, BlendMode.hardLight),
ColorFilter.mode(Colors.deepPurple, BlendMode.hue),
// sepia
ColorFilter.matrix([
0.393, 0.769, 0.189, 0, 0,
0.349, 0.686, 0.168, 0, 0,
0.272, 0.534, 0.131, 0, 0,
0, 0, 0, 1, 0,
]),
// greyscale
ColorFilter.matrix([
0.2126, 0.7152, 0.0722, 0, 0,
0.2126, 0.7152, 0.0722, 0, 0,
0.2126, 0.7152, 0.0722, 0, 0,
0, 0, 0, 1, 0,
]),
// invert
ColorFilter.matrix([
-1, 0, 0, 0, 255,
0, -1, 0, 0, 255,
0, 0, -1, 0, 255,
0, 0, 0, 1, 0,
]),
ColorFilter.linearToSrgbGamma(),
ColorFilter.srgbToLinearGamma(),
ColorFilter.mode(Colors.transparent, BlendMode.dst),
];
final myFilterNames = [
'original image', 'teal soft light', 'teal hard light', 'deep purple hue', 'matrix sepia', 'matrix greyscale', 'matrix invert', 'linearToSrgbGamma', 'srgbToLinearGamma', 'original image again',
];
您可以按如下方式使用它:
child: ColorFilteredPageView(
image: NetworkImage('https://unsplash.com/photos/3fEzry0pIms/download?force=true&w=640'),
filters: myFilters,
filterNames: myFilterNames,
),
关于image - 滑动 Flutter 时应用彩色滤镜图片,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69456158/