-
-
Save SamadiPour/0fcffb78c0c7ec8e415feade77d9f207 to your computer and use it in GitHub Desktop.
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 '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