Last active
August 19, 2021 17:12
-
-
Save roipeker/9dd0d83fd70874df4c211aafa487a256 to your computer and use it in GitHub Desktop.
Rect area tween.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
void main() { | |
runApp(MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return const MaterialApp( | |
home: HomePage(), | |
); | |
} | |
} | |
class HomePage extends StatefulWidget { | |
const HomePage({Key? key}) : super(key: key); | |
@override | |
_HomePageState createState() => _HomePageState(); | |
} | |
class _HomePageState extends State<HomePage> { | |
Rect rect = Rect.zero; | |
// To take FlutterLogo() size and coordinates. | |
final flutterKey = GlobalKey(); | |
@override | |
Widget build(BuildContext context) { | |
return RectAreaWidget( | |
rect: rect, | |
usePercent: false, | |
child: Scaffold( | |
appBar: AppBar(), | |
body: GestureDetector( | |
onTap: () { | |
final ro = | |
flutterKey.currentContext!.findRenderObject() as RenderBox; | |
final pos = ro.localToGlobal(Offset.zero); | |
setState(() { | |
rect = pos & ro.size; | |
}); | |
}, | |
child: Center( | |
child: Text.rich( | |
TextSpan( | |
text: 'Home page', | |
style: Theme.of(context).textTheme.headline1, | |
mouseCursor: SystemMouseCursors.click, | |
children: [ | |
WidgetSpan( | |
child: FlutterLogo( | |
key: flutterKey, | |
size: 70, | |
), | |
alignment: PlaceholderAlignment.middle, | |
), | |
TextSpan( | |
text: ' click here', | |
style: Theme.of(context).textTheme.caption, | |
), | |
], | |
), | |
textAlign: TextAlign.center, | |
), | |
), | |
), | |
), | |
); | |
} | |
} | |
/// just a sample... | |
class RectAreaWidget extends StatefulWidget { | |
final Widget child; | |
final Rect rect; | |
final bool usePercent; | |
const RectAreaWidget({ | |
Key? key, | |
required this.child, | |
required this.rect, | |
this.usePercent = false, | |
}) : super(key: key); | |
@override | |
_RectAreaWidgetState createState() => _RectAreaWidgetState(); | |
} | |
class _RectAreaWidgetState extends State<RectAreaWidget> | |
with SingleTickerProviderStateMixin { | |
late final _controller = AnimationController( | |
vsync: this, | |
duration: const Duration(seconds: 2), | |
); | |
late List<Rect> _rects; | |
Animation? _seq; | |
late Rect screenSize; | |
@override | |
void didUpdateWidget(RectAreaWidget oldWidget) { | |
/// compare if the "rect" area changed... | |
// if (oldWidget.rect != widget.rect) { | |
WidgetsBinding.instance!.addPostFrameCallback((timeStamp) { | |
updateRects(); | |
_controller.reset(); | |
_controller.forward(); | |
setState(() {}); | |
}); | |
// } | |
super.didUpdateWidget(oldWidget); | |
} | |
void updateRects() { | |
final r = widget.rect; | |
if (mounted) { | |
screenSize = Offset.zero & context.size!; | |
} | |
_rects = [ | |
screenSize, | |
Rect.fromLTRB(0.0, 0.0, r.right, r.bottom), | |
Rect.fromLTWH(r.left, r.top, r.width, r.height), | |
]; | |
_seq = TweenSequence([ | |
TweenSequenceItem( | |
tween: RectTween(begin: _rects[0], end: _rects[1]), | |
weight: 2, | |
), | |
TweenSequenceItem( | |
tween: RectTween(begin: _rects[1], end: _rects[2]), | |
weight: 2, | |
), | |
]).animate( | |
CurvedAnimation( | |
parent: _controller, | |
curve: const Interval(0.3, 1.0, curve: Curves.easeInOut), | |
), | |
); | |
} | |
@override | |
void dispose() { | |
_controller.dispose(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
if (_seq == null) { | |
return widget.child; | |
} | |
final seq = _seq!; | |
return AnimatedBuilder( | |
animation: seq, | |
builder: (_, child) { | |
return CustomPaint( | |
foregroundPainter: RectAreaPaint( | |
rect: seq.value, | |
usePercent: widget.usePercent, | |
), | |
willChange: true, | |
child: child, | |
); | |
}, | |
child: widget.child, | |
); | |
} | |
} | |
/// painter. | |
class RectAreaPaint extends CustomPainter { | |
final Rect rect; | |
final bool usePercent; | |
RectAreaPaint({ | |
required this.rect, | |
this.usePercent = false, | |
}); | |
@override | |
void paint(Canvas canvas, Size size) { | |
final screenPaint = Paint()..color = Colors.black38; | |
final borderPaint = Paint() | |
..color = const Color(0xffCCCCCC) | |
..style = PaintingStyle.stroke | |
..strokeWidth = 2; | |
final screenRect = Offset.zero & size; | |
var r = rect; | |
final _areaRect = usePercent | |
? Rect.fromLTWH( | |
r.left * size.width, | |
r.top * size.height, | |
r.width * size.width, | |
r.height * size.height, | |
) | |
: r; | |
final areaPath = Path()..addRect(_areaRect); | |
final maskPath = Path.combine( | |
PathOperation.difference, | |
Path()..addRect(screenRect), | |
areaPath, | |
); | |
canvas.drawPath(maskPath, screenPaint); | |
canvas.drawPath(areaPath, borderPaint); | |
} | |
@override | |
bool shouldRepaint(covariant CustomPainter oldDelegate) => true; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment