I'd like to create a a new stateless widget class that is defined by 2 images(top, bottom) and a line(defined by a function, e.g. (x){x+500}
, a width(can be 0, if it shouldn't be drawn), and a color) separating the two images.
For each pixel:
f(x) + width/2
a pixel of bottom shall be drawnf(x) - width / 2
a pixel of top shall be drawnBlow see an example of what mywidget({'top': A, 'bottom': B, 'f': (x){return sin(x)+500;}, 'width': 1, 'color': Color(0xFFFFFFFF)});
could look like:
(0,0)
+------+
| |
| A |
| __ |
|/ \__|
| |
| B |
+------+(e.g. 1920,1080)
Is there a line widget where the shape is defined by a (mathematic) function?
Is this the only way to do it? Or is there a container widget that already allows this? I have looked at the Stack widget but that's not quite solving the problem, as I'd need a structure to decide which pixel is rendered as described above. The function to decide which should happen should be supplyable by the user.
ClipPath
with CustomClipper<Path>
can help you with it.
What you can get:
Example source code:
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: ClippedPartsWidget(
top: Container(
color: Colors.red,
),
bottom: Container(
color: Colors.blue,
),
splitFunction: (Size size, double x) {
// normalizing x to make it exactly one wave
final normalizedX = x / size.width * 2 * pi;
final waveHeight = size.height / 15;
final y = size.height / 2 - sin(normalizedX) * waveHeight;
return y;
},
),
),
),
);
}
class ClippedPartsWidget extends StatelessWidget {
final Widget top;
final Widget bottom;
final double Function(Size, double) splitFunction;
ClippedPartsWidget({
@required this.top,
@required this.bottom,
@required this.splitFunction,
});
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
// I'm putting unmodified top widget to back. I wont cut it.
// Instead I'll overlay it with clipped bottom widget.
top,
Align(
alignment: Alignment.bottomCenter,
child: ClipPath(
clipper: FunctionClipper(splitFunction: splitFunction),
child: bottom,
),
),
],
);
}
}
class FunctionClipper extends CustomClipper<Path> {
final double Function(Size, double) splitFunction;
FunctionClipper({@required this.splitFunction}) : super();
Path getClip(Size size) {
final path = Path();
// move to split line starting point
path.moveTo(0, splitFunction(size, 0));
// draw split line
for (double x = 1; x <= size.width; x++) {
path.lineTo(x, splitFunction(size, x));
}
// close bottom part of screen
path.lineTo(size.width, size.height);
path.lineTo(0, size.height);
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) {
// I'm returning fixed 'true' value here for simplicity, it's not the part of actual question
// basically that means that clipping will be redrawn on any changes
return true;
}
}
User contributions licensed under CC BY-SA 3.0