Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/rodydavis/flutter_implicit_animations
Flutter animations every frame
https://github.com/rodydavis/flutter_implicit_animations
animation flutter game-logic game-loop games
Last synced: 5 days ago
JSON representation
Flutter animations every frame
- Host: GitHub
- URL: https://github.com/rodydavis/flutter_implicit_animations
- Owner: rodydavis
- License: apache-2.0
- Created: 2022-07-15T00:07:03.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2022-07-16T00:02:46.000Z (over 2 years ago)
- Last Synced: 2024-10-19T14:26:28.281Z (25 days ago)
- Topics: animation, flutter, game-logic, game-loop, games
- Language: C++
- Homepage: https://rodydavis.github.io/flutter_implicit_animations/
- Size: 5.91 MB
- Stars: 37
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Flutter implicit animations
[Live Demo](https://rodydavis.github.io/flutter_implicit_animations/)
![](/screenshots/cubes.png)
## Problem
There are a few options with flutter when creating animations:
- AnimationController
- AnimatedWidget and other AnimatedX classes
- Animations [package](https://pub.dev/packages/animations)This solutions all solve a variety of problems and can solve certain use cases.
When building animations you want to do a piece of work every frame and animated it
The build method already does a great job of rendering the state synchronously and will cache widgets that have not changed.
## Solution
This example aims to build animations in a game-like loop with a update, paint and start callback.
```dart
class SimpleExample extends StatefulWidget {
const SimpleExample({Key? key}) : super(key: key);@override
State createState() => _SimpleExampleState();
}class _SimpleExampleState extends AnimatedState {
var x = 0.0;
var y = 0.0;
var z = 0.0;@override
void update(Duration time) {
final t = delta.inMilliseconds / 1000;
x += t;
y += t;
z += t;
}@override
Widget paint(BuildContext context, BoxConstraints constraints) {
return Material(
child: Center(
child: Container(
width: 100,
height: 100,
transform: Matrix4.identity()
..rotateX(x)
..rotateY(y)
..rotateZ(z),
child: const Text(
'Hello World',
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
),
),
),
),
);
}
}
```By only changing `State` to `AnimatedState` and the build method to paint method with constraints it is possible to draw every frame.
This also makes it possible to animate multiple things at once and update asynchronously.
## Inline painter
There is also a helper class I use a lot to render a inline canvas.
```dart
CustomPaint(
painter: InlinePainter(
draw: (canvas, size) {
final paint = Paint()
..color = cube.color
..style = PaintingStyle.fill
..strokeWidth = 2;
final rect = Rect.fromLTWH(0, 0, size.width, size.height);
canvas.drawRect(rect, paint);
},
),
)
```## Example
```dart
import 'dart:math';import 'package:flutter/material.dart';
import '../widgets/animation.dart';
import '../widgets/inline_painter.dart';class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);@override
State createState() => _HomeScreenState();
}class _HomeScreenState extends AnimatedState {
final List cubes = [];double x = 0.0, y = 0.0, z = 0.0;
Color color = Colors.red;
Offset offset = Offset.zero;void addCube(Size size) {
final randomOffset = Offset(
Random().nextDouble() * size.width,
Random().nextDouble() * size.height,
);
final randomDelta = Random().nextDouble() * 20;
final randomDirection = Offset(
Random().nextDouble() * 2 - 1,
Random().nextDouble() * 2 - 1,
);
final cube = Cube()
..offset = randomOffset
..delta = randomDelta
..direction = randomDirection;
cubes.add(cube);
debugPrint('Cube added: $randomOffset');
}@override
void start(Duration time) {
final size = MediaQuery.of(context).size;
addCube(size);
super.start(time);
}@override
void update(Duration time) {
for (final cube in cubes) {
cube.update(time, constraints.biggest);
}
}@override
Widget paint(BuildContext context, BoxConstraints constraints) {
return Scaffold(
appBar: AppBar(
title: const Text('Animation Example'),
),
body: Stack(
fit: StackFit.expand,
children: [
for (final cube in cubes)
Positioned.fromRect(
rect: cube.rect,
child: CustomPaint(
painter: InlinePainter(
draw: (canvas, size) {
final paint = Paint()
..color = cube.color
..style = PaintingStyle.fill
..strokeWidth = 2;
final rect = Rect.fromLTWH(0, 0, size.width, size.height);
canvas.drawRect(rect, paint);
},
),
),
),
],
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () => addCube(constraints.biggest),
icon: const Icon(Icons.add),
label: Text('Cubes: ${cubes.length}'),
),
);
}
}class Cube {
Color color = Colors.black;
Offset offset = Offset.zero;
Size size = const Size(100, 100);
double delta = 10;
Offset direction = const Offset(0.1, 0.4);
Rect get rect => offset & size;void update(Duration time, Size size) {
// Move cube and change direction if out of bounds
offset = offset + direction * delta;// Top
if (offset.dy < 0) {
direction = Offset(direction.dx, 1);
}
// Bottom
if (offset.dy > size.height) {
direction = Offset(direction.dx, -1);
}
// Left
if (offset.dx < 0) {
direction = Offset(1, direction.dy);
}
// Right
if (offset.dx > size.width) {
direction = Offset(-1, direction.dy);
}// Change color
color = Color.fromARGB(
255,
(offset.dx * 255 / size.width).round(),
(offset.dy * 255 / size.height).round(),
0,
);
}
}```