hyperliquid_chart_engine 0.1.0
hyperliquid_chart_engine: ^0.1.0 copied to clipboard
Professional financial charting engine for Flutter — TradingView-style charts with 21+ chart types, drawing tools, indicators, and real-time data.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:hyperliquid_chart_engine/hyperliquid_chart_engine.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Chart Engine Example',
debugShowCheckedModeBanner: false,
theme: ThemeData(
brightness: Brightness.dark,
scaffoldBackgroundColor: const Color(0xFF1a1c24),
),
home: const ChartExampleScreen(),
);
}
}
class ChartExampleScreen extends ConsumerStatefulWidget {
const ChartExampleScreen({super.key});
@override
ConsumerState<ChartExampleScreen> createState() => _ChartExampleScreenState();
}
class _ChartExampleScreenState extends ConsumerState<ChartExampleScreen> {
ChartType _chartType = ChartType.candles;
late List<BarPrimitive> _bars;
bool _showSMA20 = true;
bool _showSMA50 = true;
bool _showSMA200 = true;
bool _showEMA20 = false;
bool _showEMA50 = false;
bool _showEMA200 = false;
bool _showRSI = false;
bool _showMACD = false;
bool _showStochastic = false;
bool _showBollingerBands = false;
bool _showVolume = true;
@override
void initState() {
super.initState();
_bars = _generateMockData();
}
static List<BarPrimitive> _generateMockData() {
final bars = <BarPrimitive>[];
double price = 65000.0;
int time = DateTime.now()
.subtract(const Duration(days: 100))
.millisecondsSinceEpoch;
for (int i = 0; i < 1500; i++) {
final open = price;
final close = open + (DateTime.now().microsecond % 1000) - 500.0;
final high =
(open > close ? open : close) + (DateTime.now().microsecond % 500);
final low =
(open < close ? open : close) - (DateTime.now().microsecond % 500);
final volume = 10.0 + (DateTime.now().microsecond % 100);
bars.add(
BarPrimitive(
timestamp: time,
open: open,
high: high,
low: low,
close: close,
volume: volume,
),
);
price = close;
time += const Duration(minutes: 5).inMilliseconds;
}
return bars;
}
@override
Widget build(BuildContext context) {
final atrState = ref.watch(atr14Provider);
final showATR = atrState?.visible ?? false;
return Scaffold(
appBar: AppBar(
title: const Text('Hyperliquid Chart Engine'),
backgroundColor: const Color(0xFF232530),
actions: [
DropdownButton<ChartType>(
value: _chartType,
dropdownColor: const Color(0xFF232530),
items: const [
DropdownMenuItem(value: ChartType.candles, child: Text('Candle')),
DropdownMenuItem(value: ChartType.bars, child: Text('Bar')),
DropdownMenuItem(value: ChartType.line, child: Text('Line')),
DropdownMenuItem(value: ChartType.area, child: Text('Area')),
DropdownMenuItem(
value: ChartType.heikinAshi,
child: Text('Heikin Ashi'),
),
DropdownMenuItem(value: ChartType.renko, child: Text('Renko')),
DropdownMenuItem(value: ChartType.kagi, child: Text('Kagi')),
DropdownMenuItem(
value: ChartType.pointFigure,
child: Text('Point & Figure'),
),
],
onChanged: (val) {
if (val != null) setState(() => _chartType = val);
},
),
IconButton(
icon: const Icon(Icons.show_chart),
tooltip: 'Draw Trend Line',
onPressed: () {
ref.read(activeDrawingToolProvider.notifier).state =
DrawingToolType.trendLine;
},
),
IconButton(
icon: const Icon(Icons.clear),
tooltip: 'Clear Drawings',
onPressed: () {
ref.read(drawingsProvider.notifier).clearAll();
},
),
IconButton(
icon: const Icon(Icons.ads_click),
tooltip: 'Cursor Mode',
onPressed: () {
ref.read(activeDrawingToolProvider.notifier).state =
DrawingToolType.crosshair;
},
),
const SizedBox(width: 16),
],
),
body: Row(
children: [
Expanded(
child: Center(
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.75,
height: MediaQuery.of(context).size.height * 0.8,
child: DecoratedBox(
decoration: BoxDecoration(
border: Border.all(color: Colors.white24),
),
child: ChartWidget(
bars: _bars,
chartType: _chartType,
interval: IntervalType.m5,
showSMA20: _showSMA20,
showSMA50: _showSMA50,
showSMA200: _showSMA200,
showEMA20: _showEMA20,
showEMA50: _showEMA50,
showEMA200: _showEMA200,
showRSI: _showRSI,
showMACD: _showMACD,
macdHeightRatio: 0.3,
showStochastic: _showStochastic,
stochasticHeightRatio: 0.3,
showBollingerBands: _showBollingerBands,
showVolume: _showVolume,
panels: [
if (showATR)
const PanelConfig(
type: PanelType.atr,
heightRatio: 0.3,
),
],
onAlertCreated: (alert) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Alert created at ${alert.price}'),
),
);
},
onAlertTriggered: (alert) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Alert triggered at ${alert.price}!'),
backgroundColor: Colors.green,
),
);
},
),
),
),
),
),
const AlertControlPanel(),
],
),
bottomNavigationBar: Container(
color: const Color(0xFF232530),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Wrap(
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
spacing: 16,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
Checkbox(
value: _showSMA20,
onChanged: (val) => setState(() => _showSMA20 = val ?? false),
),
const Text('SMA 20'),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Checkbox(
value: _showSMA50,
onChanged: (val) => setState(() => _showSMA50 = val ?? false),
),
const Text('SMA 50'),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Checkbox(
value: _showSMA200,
onChanged: (val) =>
setState(() => _showSMA200 = val ?? false),
),
const Text('SMA 200'),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Checkbox(
value: _showEMA20,
activeColor: const Color(0xFF4CAF50),
onChanged: (val) => setState(() => _showEMA20 = val ?? false),
),
const Text('EMA 20'),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Checkbox(
value: _showEMA50,
activeColor: const Color(0xFF9C27B0),
onChanged: (val) => setState(() => _showEMA50 = val ?? false),
),
const Text('EMA 50'),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Checkbox(
value: _showEMA200,
activeColor: const Color(0xFF00BCD4),
onChanged: (val) =>
setState(() => _showEMA200 = val ?? false),
),
const Text('EMA 200'),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Checkbox(
value: _showRSI,
activeColor: Colors.blue,
onChanged: (val) => setState(() => _showRSI = val ?? false),
),
const Text('RSI 14'),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Checkbox(
value: _showMACD,
activeColor: Colors.blueAccent,
onChanged: (val) => setState(() => _showMACD = val ?? false),
),
const Text('MACD'),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Checkbox(
value: _showStochastic,
activeColor: Colors.deepOrange,
onChanged: (val) =>
setState(() => _showStochastic = val ?? false),
),
const Text('Stochastic'),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Checkbox(
value: showATR,
activeColor: const Color(0xFF9C27B0),
onChanged: (val) =>
ref.read(atr14Provider.notifier).toggleVisibility(),
),
const Text('ATR'),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Checkbox(
value: _showBollingerBands,
activeColor: const Color(0xFF2196F3),
onChanged: (val) =>
setState(() => _showBollingerBands = val ?? false),
),
const Text('Bollinger'),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Checkbox(
value: _showVolume,
activeColor: Colors.deepPurple,
onChanged: (val) =>
setState(() => _showVolume = val ?? false),
),
const Text('Volume'),
],
),
],
),
),
);
}
}
class AlertControlPanel extends ConsumerWidget {
const AlertControlPanel({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final alerts = ref.watch(alertsProvider);
return Container(
width: 250,
color: Colors.grey[900],
child: Column(
children: [
const Padding(
padding: EdgeInsets.all(16),
child: Text(
'Price Alerts\n(Long Press for new)',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
Expanded(
child: ListView.builder(
itemCount: alerts.length,
itemBuilder: (context, index) {
final alert = alerts[index];
return ListTile(
title: Text(
'Price: ${alert.price.toStringAsFixed(2)}',
style: const TextStyle(color: Colors.white),
),
subtitle: Text(
alert.condition == AlertCondition.crossingUp
? '↑ Up'
: '↓ Down',
style: TextStyle(
color: alert.status == AlertStatus.active
? Colors.blue
: Colors.green,
),
),
trailing: IconButton(
icon: const Icon(Icons.delete, color: Colors.red),
onPressed: () {
ref.read(alertsProvider.notifier).removeAlert(alert.id);
},
),
);
},
),
),
Padding(
padding: const EdgeInsets.all(16),
child: ElevatedButton(
onPressed: () {
ref.read(alertsProvider.notifier).clearAll();
},
child: const Text('Clear All'),
),
),
],
),
);
}
}