这个想法是将图像移动到与页面移动相反的方向 - 所以如果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,
),