Created
July 15, 2025 05:43
-
-
Save chooyan-eng/48f0edc4630551e9a812aee27864f2e9 to your computer and use it in GitHub Desktop.
AnimatedTo sample that can be run on DartPad
This file contains hidden or 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'; | |
import 'package:flutter/material.dart'; | |
import 'package:animated_to/animated_to.dart'; | |
void main() { | |
runApp(const MaterialApp(home: GraphPage())); | |
} | |
/// A page that demonstrates the `AnimatedTo` package with a live graph simulation. | |
/// To see the usage of `AnimatedTo`, jump to line 130. | |
class GraphPage extends StatefulWidget { | |
const GraphPage({super.key}); | |
@override | |
State<GraphPage> createState() => _GraphPageState(); | |
} | |
class _GraphPageState extends State<GraphPage> { | |
static const _items = 6; | |
final List<_Item> data = List.generate( | |
_items, | |
(index) => _Item( | |
key: GlobalObjectKey(index), | |
value: _items - index, | |
color: Colors.primaries[index % Colors.primaries.length], | |
), | |
); | |
Timer? _timer; | |
void _startSimulation() { | |
_timer = Timer.periodic(const Duration(milliseconds: 100), (timer) { | |
final target = Random().nextInt(_items); | |
final item = data.firstWhere((e) => e.key == GlobalObjectKey(target)); | |
setState(() { | |
item.value += Random().nextInt(3) + 1; | |
data.sort((a, b) => b.value.compareTo(a.value)); | |
}); | |
}); | |
} | |
void _stopSimulation() { | |
_timer?.cancel(); | |
_timer = null; | |
} | |
int get _maxValue => data.map((e) => e.value).reduce((a, b) => max(a, b)); | |
@override | |
Widget build(BuildContext context) { | |
return Theme( | |
data: ThemeData.dark().copyWith( | |
scaffoldBackgroundColor: const Color(0xFF1A1A2E), | |
appBarTheme: const AppBarTheme( | |
backgroundColor: Color(0xFF16213E), | |
elevation: 0, | |
), | |
), | |
child: Scaffold( | |
appBar: AppBar( | |
title: const Text('Live Graph Simulation'), | |
), | |
body: Stack( | |
children: [ | |
// Grid background | |
CustomPaint( | |
size: Size.infinite, | |
painter: GridPainter(), | |
), | |
// Graph content | |
Padding( | |
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 60), | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: [ | |
// Stats panel | |
Container( | |
padding: const EdgeInsets.all(16), | |
decoration: BoxDecoration( | |
color: const Color(0xFF0F3460).withAlpha(128), | |
borderRadius: BorderRadius.circular(16), | |
border: Border.all( | |
color: Colors.blue[400]!.withAlpha(128), | |
), | |
), | |
child: Row( | |
mainAxisSize: MainAxisSize.min, | |
children: [ | |
_StatItem( | |
label: 'Max Value', | |
value: _maxValue.toString(), | |
icon: Icons.arrow_upward_rounded, | |
), | |
const SizedBox(width: 24), | |
_StatItem( | |
label: 'Items', | |
value: _items.toString(), | |
icon: Icons.bar_chart_rounded, | |
), | |
], | |
), | |
), | |
const SizedBox(height: 40), | |
// Graph | |
Expanded( | |
child: Row( | |
children: [ | |
// Y-axis | |
Container( | |
width: 2, | |
decoration: BoxDecoration( | |
gradient: LinearGradient( | |
begin: Alignment.topCenter, | |
end: Alignment.bottomCenter, | |
colors: [ | |
Colors.blue[400]!, | |
Colors.blue[400]!.withAlpha(128), | |
], | |
), | |
), | |
), | |
const SizedBox(width: 24), | |
// Bars | |
Expanded( | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
mainAxisSize: MainAxisSize.min, | |
spacing: 10, | |
children: [ | |
...data.map( | |
(e) => AnimatedTo.spring( | |
globalKey: e.key, | |
child: _Bar( | |
value: e.value, | |
maxValue: _maxValue, | |
color: e.color, | |
), | |
), | |
), | |
], | |
), | |
), | |
], | |
), | |
), | |
], | |
), | |
), | |
], | |
), | |
floatingActionButton: FloatingActionButton( | |
onPressed: () { | |
if (_timer != null) { | |
_stopSimulation(); | |
} else { | |
_startSimulation(); | |
} | |
}, | |
child: Icon( | |
_timer != null ? Icons.stop_rounded : Icons.play_arrow_rounded, | |
color: Colors.white, | |
), | |
), | |
), | |
); | |
} | |
} | |
class _Bar extends StatelessWidget { | |
const _Bar({ | |
required this.value, | |
required this.maxValue, | |
required this.color, | |
}); | |
final int value; | |
final int maxValue; | |
final Color color; | |
@override | |
Widget build(BuildContext context) { | |
return LayoutBuilder( | |
builder: (context, constraints) { | |
final width = (constraints.maxWidth * value / maxValue) | |
.clamp(0.0, constraints.maxWidth); | |
return AnimatedContainer( | |
duration: const Duration(milliseconds: 100), | |
height: 30, | |
width: width, | |
decoration: BoxDecoration( | |
gradient: LinearGradient( | |
colors: [ | |
color.withAlpha(200), | |
color, | |
], | |
), | |
borderRadius: BorderRadius.circular(8), | |
), | |
child: Center( | |
child: Text( | |
value.toString(), | |
style: const TextStyle( | |
color: Colors.white, | |
fontWeight: FontWeight.bold, | |
fontSize: 16, | |
), | |
), | |
), | |
); | |
}, | |
); | |
} | |
} | |
class _StatItem extends StatelessWidget { | |
const _StatItem({ | |
required this.label, | |
required this.value, | |
required this.icon, | |
}); | |
final String label; | |
final String value; | |
final IconData icon; | |
@override | |
Widget build(BuildContext context) { | |
return Row( | |
children: [ | |
Container( | |
padding: const EdgeInsets.all(8), | |
decoration: BoxDecoration( | |
color: Colors.blue[400]!.withAlpha(128), | |
borderRadius: BorderRadius.circular(8), | |
), | |
child: Icon( | |
icon, | |
color: Colors.blue[300], | |
size: 20, | |
), | |
), | |
const SizedBox(width: 12), | |
Column( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: [ | |
Text( | |
label, | |
style: TextStyle( | |
color: Colors.blue[300], | |
fontSize: 12, | |
fontWeight: FontWeight.w500, | |
), | |
), | |
Text( | |
value, | |
style: const TextStyle( | |
color: Colors.white, | |
fontSize: 20, | |
fontWeight: FontWeight.bold, | |
), | |
), | |
], | |
), | |
], | |
); | |
} | |
} | |
class _Item { | |
_Item({ | |
required this.key, | |
required this.value, | |
required this.color, | |
}); | |
final GlobalKey key; | |
int value; | |
final Color color; | |
} | |
class GridPainter extends CustomPainter { | |
@override | |
void paint(Canvas canvas, Size size) { | |
final paint = Paint() | |
..color = Colors.blue[900]!.withAlpha(128) | |
..strokeWidth = 1; | |
const spacing = 30; | |
for (var i = 0; i < size.width; i += spacing) { | |
canvas.drawLine( | |
Offset(i.toDouble(), 0), | |
Offset(i.toDouble(), size.height), | |
paint, | |
); | |
} | |
for (var i = 0; i < size.height; i += spacing) { | |
canvas.drawLine( | |
Offset(0, i.toDouble()), | |
Offset(size.width, i.toDouble()), | |
paint, | |
); | |
} | |
} | |
@override | |
bool shouldRepaint(covariant CustomPainter oldDelegate) => false; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment