My goal is to swipe horizontally colors filters above an image in background like this :
I'm using multiple matrix to create some color filters and I know that I have to wrap the Image by the ColorFiltered
to apply the filter : there is no issue for this.
Then when I'm trying to swipe these filters, I don't know how to apply them. Here is the code for the demo :
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,
),
),
);
}),
],
),
);
}
}
Doesn't matters if the filter is directly apply on the image or not during the swipe as long as the filter is visible, I can retrieve it and apply it later.
Thanks for any lead or solution
CodePudding user response:
the idea is to move the image in the opposite direction to the page movement - so if PageView
is moving the child (ColorFiltered
widget) to the right you have to move the grand child (Image
widget) to the left so it stays in the same place - while simple in theory in practice it can make some headache - fortunately there is CustomSingleChildLayout
widget that save us a lot of work:
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,
),
),);
}
now with some sample filters:
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',
];
you can use it as follows:
child: ColorFilteredPageView(
image: NetworkImage('https://unsplash.com/photos/3fEzry0pIms/download?force=true&w=640'),
filters: myFilters,
filterNames: myFilterNames,
),
CodePudding user response:
simplest way is to use RenderRepaintBoundary to screen capture widget:
static GlobalKey _screenCapture = new GlobalKey();
Uint8List _takeScreenShot() async {
RenderRepaintBoundary boundary = _screenCapture.currentContext.findRenderObject();
ui.Image image = await boundary.toImage(pixelRatio: 2.0);
ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
Uint8List pngBytes = byteData.buffer.asUint8List();
return pngBytes;
}
body: Stack(
key: _screenCapture,
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,
),
),
);
}),
],
),