Flutter App
This commit is contained in:
344
flutter_app/lib/widgets/alerts_panel.dart
Normal file
344
flutter_app/lib/widgets/alerts_panel.dart
Normal file
@@ -0,0 +1,344 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../providers/price_provider.dart';
|
||||
|
||||
class AlertsPanel extends StatefulWidget {
|
||||
const AlertsPanel({super.key});
|
||||
|
||||
@override
|
||||
State<AlertsPanel> createState() => _AlertsPanelState();
|
||||
}
|
||||
|
||||
class _AlertsPanelState extends State<AlertsPanel> {
|
||||
final _auecController = TextEditingController();
|
||||
final _priceController = TextEditingController();
|
||||
String _selectedPreset = '1T';
|
||||
double _auecAmount = 1000000000000; // 1 trillion AUEC
|
||||
bool _showCustomInput = false;
|
||||
|
||||
// Preset AUEC amounts
|
||||
static const Map<String, double> _presetAmounts = {
|
||||
'10T': 10000000000000,
|
||||
'5T': 5000000000000,
|
||||
'1T': 1000000000000,
|
||||
'750B': 750000000000,
|
||||
'500B': 500000000000,
|
||||
'250B': 250000000000,
|
||||
'Other': 0, // Special case for custom input
|
||||
};
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_auecController.dispose();
|
||||
_priceController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _updateAmount(String preset) {
|
||||
setState(() {
|
||||
_selectedPreset = preset;
|
||||
if (preset == 'Other') {
|
||||
_showCustomInput = true;
|
||||
_auecController.text = _auecAmount.toStringAsFixed(0);
|
||||
} else {
|
||||
_showCustomInput = false;
|
||||
_auecAmount = _presetAmounts[preset]!;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _setCustomAmount() {
|
||||
final amount = double.tryParse(_auecController.text);
|
||||
if (amount != null && amount > 0) {
|
||||
setState(() {
|
||||
_auecAmount = amount;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF1A1F3A),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
border: Border.all(color: const Color(0xFF50E3C2), width: 1),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Price Alerts',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
Row(
|
||||
children: [
|
||||
// AUEC amount selector
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
// Dropdown for preset amounts
|
||||
Container(
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF2A2F4A),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
border: Border.all(color: const Color(0xFF50E3C2), width: 1),
|
||||
),
|
||||
child: Center(
|
||||
child: DropdownButtonHideUnderline(
|
||||
child: DropdownButton<String>(
|
||||
value: _selectedPreset,
|
||||
dropdownColor: const Color(0xFF2A2F4A),
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
alignment: AlignmentDirectional.center,
|
||||
items: _presetAmounts.keys.map((String preset) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: preset,
|
||||
alignment: AlignmentDirectional.center,
|
||||
child: Center(
|
||||
child: Text(
|
||||
preset,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (String? newValue) {
|
||||
if (newValue != null) {
|
||||
_updateAmount(newValue);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_showCustomInput) ...[
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
width: 120,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF2A2F4A),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
border: Border.all(color: const Color(0xFF50E3C2), width: 1),
|
||||
),
|
||||
child: TextField(
|
||||
controller: _auecController,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
decoration: const InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||
hintText: 'Enter amount',
|
||||
hintStyle: TextStyle(
|
||||
color: Color(0xFF888888),
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
textAlignVertical: TextAlignVertical.center,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
],
|
||||
onSubmitted: (_) => _setCustomAmount(),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: _setCustomAmount,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF50E3C2),
|
||||
foregroundColor: const Color(0xFF0A0E27),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
||||
minimumSize: const Size(0, 40),
|
||||
),
|
||||
child: const Text(
|
||||
'Set',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _priceController,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Max USD Price',
|
||||
hintStyle: const TextStyle(color: Color(0xFF888888)),
|
||||
filled: true,
|
||||
fillColor: const Color(0xFF2A2F4A),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
borderSide: const BorderSide(color: Color(0xFF50E3C2)),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
borderSide: const BorderSide(color: Color(0xFF50E3C2)),
|
||||
),
|
||||
),
|
||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d*')),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
final price = double.tryParse(_priceController.text);
|
||||
if (_auecAmount > 0 && price != null && price > 0) {
|
||||
await context.read<PriceProvider>().addAlert(_auecAmount, price);
|
||||
_priceController.clear();
|
||||
// Reset to default preset
|
||||
setState(() {
|
||||
_selectedPreset = '1T';
|
||||
_auecAmount = 1000000000000;
|
||||
_showCustomInput = false;
|
||||
_auecController.clear();
|
||||
});
|
||||
}
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF50E3C2),
|
||||
foregroundColor: const Color(0xFF0A0E27),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
|
||||
),
|
||||
child: const Text(
|
||||
'Add Alert',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
Consumer<PriceProvider>(
|
||||
builder: (context, provider, child) {
|
||||
if (provider.alerts.isEmpty) {
|
||||
return const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(20),
|
||||
child: Text(
|
||||
'No alerts set. Add an alert to get notified when prices meet your criteria.',
|
||||
style: TextStyle(color: Color(0xFF888888)),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Column(
|
||||
children: provider.alerts.map((alert) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 10),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF2A2F4A),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
border: Border.all(
|
||||
color: alert.enabled
|
||||
? const Color(0xFF50E3C2)
|
||||
: const Color(0xFF888888),
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${NumberFormat('#,###').format(alert.auecAmount)} AUEC for \$${alert.maxPrice.toStringAsFixed(2)}',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'\$${(alert.maxPrice / (alert.auecAmount / 1000000)).toStringAsFixed(9)} per 1M AUEC',
|
||||
style: const TextStyle(
|
||||
color: Color(0xFF888888),
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () => provider.toggleAlert(alert.id),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: alert.enabled
|
||||
? const Color(0xFF50E3C2)
|
||||
: const Color(0xFF888888),
|
||||
foregroundColor: const Color(0xFF0A0E27),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
),
|
||||
child: Text(
|
||||
alert.enabled ? 'Enabled' : 'Disabled',
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: () => provider.deleteAlert(alert.id),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFFFF6B9D),
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
),
|
||||
child: const Text(
|
||||
'Delete',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user