Skip to content

Instantly share code, notes, and snippets.

@SamadiPour
Forked from imaNNeo/main.dart
Created December 24, 2020 14:40
Show Gist options
  • Save SamadiPour/0fcffb78c0c7ec8e415feade77d9f207 to your computer and use it in GitHub Desktop.
Save SamadiPour/0fcffb78c0c7ec8e415feade77d9f207 to your computer and use it in GitHub Desktop.
import 'dart:async';
import 'dart:math' as math;
import 'dart:ui';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
num degreesToRads(num deg) {
return (deg * math.pi) / 180.0;
}
double randD(double min, double max) =>
math.Random().nextDouble() * (max - min) + min;
class Orbit {
final List<Electron> electrons;
double angle;
Orbit({
@required this.electrons,
@required this.angle,
});
}
extension OrbitsExtension on List<Orbit> {
void updateElectronsPosition() {
this.forEach((orbit) {
orbit.electrons.forEach((electron) {
electron.move();
});
});
}
}
class Electron {
double speed;
double currentSize;
double targetSize;
Color currentColor;
Color targetColor;
double _positionPercent = 0;
double get positionPercent {
return _positionPercent;
}
Electron({
@required this.currentSize,
@required this.targetSize,
@required this.currentColor,
@required this.targetColor,
@required this.speed,
});
void move() {
if (_positionPercent >= 1.0) {
_positionPercent = 0;
}
_positionPercent += speed;
}
static Electron random() {
const colors = [
Colors.greenAccent,
Colors.redAccent,
Colors.cyanAccent,
Colors.purpleAccent,
Colors.yellowAccent,
];
final size = randD(5, 7);
final initialSize = size * 8;
final color = colors[math.Random().nextInt(colors.length)];
return Electron(
currentSize: initialSize,
targetSize: size,
currentColor: color.withOpacity(0.1),
targetColor: color,
speed: randD(0.008, 0.012),
);
}
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.yellow,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key key}) : super(key: key);
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final int _maxOrbitCount = 10;
List<Orbit> _orbits = [
Orbit(electrons: [], angle: 0.0),
Orbit(electrons: [], angle: 60.0),
Orbit(electrons: [], angle: 120.0),
];
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF191636),
body: Center(
child: AtomWidget(
orbits: _orbits,
),
),
floatingActionButton: InkWell(
onLongPress: addOrbit,
child: FloatingActionButton(
onPressed: addElectron,
child: Icon(Icons.add),
),
),
);
}
void addElectron() {
setState(() {
_orbits[math.Random().nextInt(_orbits.length)]
.electrons
.add(Electron.random());
});
}
void addOrbit() {
int count = _orbits.length + 1;
if (count <= _maxOrbitCount) {
final angelDifference = 180 / count;
setState(() {
_orbits
.asMap()
.forEach((index, orbit) => orbit.angle = angelDifference * index);
_orbits.add(Orbit(electrons: [], angle: angelDifference * (count - 1)));
});
}
}
}
class AtomWidget extends StatefulWidget {
final List<Orbit> orbits;
const AtomWidget({Key key, @required this.orbits}) : super(key: key);
@override
_AtomWidgetState createState() => _AtomWidgetState();
}
class _AtomWidgetState extends State<AtomWidget>
with SingleTickerProviderStateMixin {
Timer t;
@override
void initState() {
super.initState();
t = Timer.periodic(Duration(milliseconds: 16), (timer) {
setState(() {
widget.orbits.updateElectronsPosition();
});
});
}
@override
Widget build(BuildContext context) {
final atomSize = (MediaQuery.of(context).size.shortestSide / 3) * 2;
return Stack(
children: [
...widget.orbits
.map(
(orbit) => Center(
child: Transform.rotate(
angle: degreesToRads(orbit.angle),
child: CustomPaint(
painter: _OrbitPainter(orbit),
size: Size(atomSize, atomSize),
),
),
),
)
.toList(),
Center(
child: Container(
width: atomSize / 10,
height: atomSize / 10,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
colors: [
Color(0xffffc560),
Color(0xffff593b),
],
begin: Alignment.bottomLeft,
end: Alignment.topRight,
)),
),
)
],
);
}
@override
void dispose() {
t.cancel();
super.dispose();
}
}
class _OrbitPainter extends CustomPainter {
final Orbit orbit;
_OrbitPainter(this.orbit);
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
Path orbitPath = new Path();
final width = size.shortestSide;
final height = width * 0.24;
orbitPath
.addOval(Rect.fromCenter(center: center, width: width, height: height));
canvas.drawPath(
orbitPath,
Paint()
..color = Color(0xFF26224f)
..style = PaintingStyle.stroke
..strokeWidth = 1,
);
orbit.electrons.forEach((electron) {
electron.currentSize =
lerpDouble(electron.currentSize, electron.targetSize, 0.08);
electron.currentColor =
Color.lerp(electron.currentColor, electron.targetColor, 0.08);
final degree = math.pi * 2 * electron.positionPercent;
canvas.drawCircle(
center +
Offset(math.cos(degree) * (width / 2),
math.sin(degree) * (height / 2)),
electron.currentSize / 2,
Paint()
..color = electron.currentColor
..style = PaintingStyle.fill
..strokeCap = StrokeCap.round
..strokeWidth = electron.currentSize,
);
});
}
@override
bool shouldRepaint(covariant _OrbitPainter oldDelegate) => true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment