Skip to content

Instantly share code, notes, and snippets.

@wilsonowilson
Last active February 26, 2021 16:25
Show Gist options
  • Save wilsonowilson/a25d8d30886c4bcf12d7c1ac9e497ce8 to your computer and use it in GitHub Desktop.
Save wilsonowilson/a25d8d30886c4bcf12d7c1ac9e497ce8 to your computer and use it in GitHub Desktop.
RenderObject Swipe Card
import 'dart:math';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
class FlipCardPlayground extends StatelessWidget {
@override
Widget build(BuildContext context) {
GestureDetector(
);
return FlipCard(
numbers: numbers,
);
}
}
class FlipCard extends LeafRenderObjectWidget {
FlipCard({
required this.numbers,
this.height = 300,
this.width = 200,
});
final double height;
final double width;
final List<int> numbers;
@override
RenderObject createRenderObject(BuildContext context) {
return RenderFlipCard(
height: height,
width: width,
numbers: numbers,
);
}
@override
void updateRenderObject(BuildContext context, RenderFlipCard renderObject) {
renderObject
..height = height
..width = width
.._numbers = numbers;
}
}
class RenderFlipCard extends RenderBox {
RenderFlipCard({
required double height,
required double width,
required List<int> numbers,
}) : _height = height,
_width = width,
_numbers = numbers {
currentNumber = numbers[0];
_dragRecognizer = VerticalDragGestureRecognizer()
..onUpdate = _onDrag
..onEnd = _onEnd;
}
VerticalDragGestureRecognizer? _dragRecognizer;
double _dragProgress = 0;
List<int> _numbers;
int? currentNumber;
double _height;
set height(double height) {
_height = height;
markNeedsLayout();
}
double _width;
set width(double width) {
_width = width;
markNeedsLayout();
}
@override
void performLayout() {
size = Size(_width, _height);
}
@override
void paint(PaintingContext context, Offset offset) {
final canvas = context.canvas;
canvas.save();
canvas.translate(offset.dx, offset.dy);
final bgPaint2 = Paint()..color = Colors.grey.shade900;
canvas.drawRRect(
RRect.fromLTRBR(0, 0, _width, _height, Radius.circular(10)),
bgPaint2,
);
_paintText(context, offset);
canvas.restore();
}
void _drawTopLayer(PaintingContext context, Offset offset) {
final bgPaint = Paint()..color = Colors.grey.shade900;
final canvas = context.canvas;
_useTransform(context, offset, () {
canvas.drawRRect(
RRect.fromLTRBAndCorners(0, 0, _width, _height / 2,
topLeft: Radius.circular(10), topRight: Radius.circular(10)),
bgPaint,
);
});
}
void _useTransform(PaintingContext context, Offset offset, Function painter) {
final canvas = context.canvas;
final transform = Matrix4.identity();
transform.setEntry(3, 2, 0.001);
final originalMatrix = transform;
final centerOriginTranslation = Alignment.center.alongSize(size);
transform.translate(
-centerOriginTranslation.dx * 0, centerOriginTranslation.dy * 0.5);
canvas.save();
transform.multiply(originalMatrix);
transform.translate(
centerOriginTranslation.dx, -centerOriginTranslation.dy);
final translation = Alignment.topCenter.alongSize(size);
transform.translate(translation.dx, translation.dy);
canvas.translate(-translation.dx * 2, 0);
transform.rotateX(pi * _dragProgress);
context.pushTransform(needsCompositing, offset, transform,
(context, offset) {
painter.call();
});
canvas.restore();
}
void _useVerticalTransform(
PaintingContext context, Offset offset, Function painter) {
final canvas = context.canvas;
final transform = Matrix4.identity();
final originalMatrix = transform;
final centerOriginTranslation = Alignment.center.alongSize(size);
transform.translate(
-centerOriginTranslation.dx * 0, centerOriginTranslation.dy * 0.5);
canvas.save();
transform.multiply(originalMatrix);
transform.translate(
centerOriginTranslation.dx, -centerOriginTranslation.dy);
final translation = Alignment.topCenter.alongSize(size);
transform.translate(translation.dx, translation.dy);
canvas.translate(-translation.dx * 2, 0);
transform.rotateX(pi);
context.pushTransform(needsCompositing, offset, transform,
(context, offset) {
painter.call();
});
canvas.translate(0, size.height / 2);
canvas.restore();
}
void _paintText(PaintingContext context, Offset offset) {
final canvas = context.canvas;
final textStyle = const TextStyle(
color: Colors.white,
fontSize: 100,
fontWeight: FontWeight.bold,
);
final textSpan = TextSpan(
text: '$currentNumber',
style: textStyle,
);
final textPainter = TextPainter(
text: textSpan,
textDirection: TextDirection.ltr,
);
final numberAhead = currentNumber! + 1;
final textSpan2 = TextSpan(
text: '$numberAhead',
style: textStyle,
);
final textPainter2 = TextPainter(
text: textSpan2,
textDirection: TextDirection.ltr,
);
textPainter.layout(
minWidth: 0,
maxWidth: size.width,
);
textPainter2.layout();
final textCenter = Offset(
size.width / 2 - textPainter.width / 2,
size.height / 2 - textPainter.height / 2,
);
final textCenter2 = Offset(
size.width / 2 - textPainter2.width / 2,
size.height / 2 - textPainter2.height / 2,
);
canvas.save();
canvas.clipRect(
Rect.fromLTRB(
0,
size.height / 2,
size.width,
size.height,
),
);
textPainter.paint(canvas, textCenter);
canvas.restore();
canvas.save();
canvas.clipRect(
Rect.fromLTRB(
0,
0,
size.width,
size.height / 2,
),
);
textPainter2.paint(canvas, textCenter2);
canvas.restore();
const borderHeight = 2;
final borderPaint = Paint()..color = Colors.white;
canvas.drawRRect(
RRect.fromLTRBR(
0,
size.height / 2 - borderHeight / 2,
size.width,
size.height / 2 + borderHeight / 2,
Radius.circular(5),
),
borderPaint,
);
_drawTopLayer(context, offset);
canvas.save();
final topRect = Rect.fromLTRB(0, 0, size.width, size.height / 2);
_useTransform(context, offset, () {
canvas.save();
canvas.clipRect(topRect);
if (_dragProgress <= 0.5) textPainter.paint(canvas, textCenter);
canvas.restore();
});
// canvas.save();
_useTransform(context, offset, () {
canvas.save();
canvas.clipRect(topRect);
// canvas.rotate(pi * 0.1);
_useVerticalTransform(context, offset, () {
if (_dragProgress > 0.5) textPainter2.paint(canvas, textCenter2);
});
canvas.restore();
});
canvas.restore();
// canvas.restore();
}
@override
void handleEvent(PointerEvent event, HitTestEntry entry) {
assert(debugHandleEvent(event, entry));
if (event is PointerDownEvent) {
_dragRecognizer!.addPointer(event);
}
}
@override
bool hitTestSelf(Offset position) => true;
void _onDrag(DragUpdateDetails details) {
final position = details.localPosition;
final dy = position.dy.clamp(0, size.height);
_dragProgress = dy / size.height;
markNeedsPaint();
}
void _onEnd(DragEndDetails details) {
if (_dragProgress > 0.5) updateCount();
_dragProgress = 0;
}
void updateCount() async {
final currentNumberIndex = _numbers.indexOf(currentNumber!);
if (currentNumberIndex == _numbers.length - 1) {
currentNumber = numbers.first;
} else {
currentNumber = _numbers[currentNumberIndex + 1];
}
markNeedsPaint();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment