583 lines
28 KiB
Dart
583 lines
28 KiB
Dart
import 'dart:developer' as dv;
|
||
|
||
import 'package:dio/dio.dart';
|
||
import 'package:fl_chart/fl_chart.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_svg/svg.dart';
|
||
import 'package:gap/gap.dart';
|
||
import 'package:get/get.dart' hide FormData;
|
||
import 'package:traderscircuit/Utils/text.dart';
|
||
import 'package:traderscircuit/Utils/utils.dart';
|
||
import 'package:traderscircuit/controller/products_controller.dart';
|
||
import 'package:traderscircuit/model/StockDetailsModel/stock_details_model.dart';
|
||
import 'package:traderscircuit/view_model/StockDetailsApi/stock_details_api.dart';
|
||
|
||
import '../../../Utils/Common/CommonAppBar.dart';
|
||
import '../../onBoarding/splashScreen1.dart';
|
||
|
||
class StockDetailsScreen extends StatefulWidget {
|
||
const StockDetailsScreen({super.key});
|
||
|
||
@override
|
||
State<StockDetailsScreen> createState() => _StockDetailsScreenState();
|
||
}
|
||
|
||
class _StockDetailsScreenState extends State<StockDetailsScreen> {
|
||
RxBool isLoading = true.obs;
|
||
StockDetailsModel? stockDetailsModel;
|
||
String instrumentName = Get.arguments["instrument_name"];
|
||
String percentageDifference = "";
|
||
String netChange = "";
|
||
|
||
ProductsController productsController = Get.put(ProductsController());
|
||
|
||
@override
|
||
void initState() {
|
||
dv.log(instrumentName);
|
||
if (productsController.isUpstoxTokenNotExpired.value) {
|
||
StockDetailsApi()
|
||
.getStockDetails(
|
||
FormData.fromMap({"upstox_instrument_key": instrumentName}))
|
||
.then((value) {
|
||
final Map<String, dynamic> data = value.data;
|
||
|
||
Map<String, dynamic> data1 = data["data"]['stock_data']['data'];
|
||
String dynamicKey = data1.keys.first;
|
||
// Access nested data using dynamic key
|
||
Map<String, dynamic> dynamicData = data1[dynamicKey];
|
||
|
||
List<Candles>? candles = [];
|
||
|
||
for (var a in data["data"]['candle_stick']["data"]["candles"]) {
|
||
candles.add(Candles(
|
||
timestamp: a[0],
|
||
open: a[1].toDouble(),
|
||
high: a[2].toDouble(),
|
||
low: a[3].toDouble(),
|
||
close: a[4].toDouble(),
|
||
volume: a[5].toInt(),
|
||
openInterest: a[6],
|
||
));
|
||
}
|
||
candles = candles.reversed.toList();
|
||
stockDetailsModel = StockDetailsModel(
|
||
status: data["status"],
|
||
message: data["message"],
|
||
statusCode: data["status_code"],
|
||
data: Data(
|
||
stockData: StockInfo(
|
||
ohlc: Ohlc.fromJson(dynamicData['ohlc']),
|
||
averagePrice: dynamicData['average_price'].toDouble(),
|
||
instrumentToken: dynamicData['instrument_token'],
|
||
lastPrice: dynamicData['last_price'].toDouble(),
|
||
lastTradeTime: dynamicData['last_trade_time'],
|
||
lowerCircuitLimit:
|
||
dynamicData['lower_circuit_limit'].toDouble(),
|
||
netChange: dynamicData['net_change'].toDouble(),
|
||
oi: dynamicData['oi'],
|
||
oiDayHigh: dynamicData['oi_day_high'],
|
||
oiDayLow: dynamicData['oi_day_low'],
|
||
symbol: dynamicData['symbol'],
|
||
timestamp: dynamicData['timestamp'],
|
||
totalBuyQuantity: dynamicData['total_buy_quantity'],
|
||
totalSellQuantity: dynamicData['total_sell_quantity'],
|
||
upperCircuitLimit: dynamicData['upper_circuit_limit'],
|
||
volume: dynamicData['volume'],
|
||
),
|
||
candleStick: CandleStick(candles: candles),
|
||
optionChain:
|
||
OptionChain.fromJson(data["data"]['option_chain'])));
|
||
|
||
netChange = dynamicData['net_change'].toString();
|
||
_calculatePercentageChange(
|
||
stockDetailsModel!.data!.stockData!.ohlc!.open!,
|
||
stockDetailsModel!.data!.stockData!.lastPrice!);
|
||
|
||
isLoading.value = false;
|
||
});
|
||
} else {
|
||
isLoading.value = false;
|
||
}
|
||
|
||
super.initState();
|
||
}
|
||
|
||
void _calculatePercentageChange(double openPrice, double currentPrice) {
|
||
final percentageChange = ((currentPrice - openPrice) / openPrice) * 100;
|
||
|
||
percentageDifference = percentageChange.toStringAsFixed(2);
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
backgroundColor: Colors.black,
|
||
drawerEnableOpenDragGesture: false,
|
||
extendBody: true,
|
||
appBar: const CommonAppbar(
|
||
titleTxt: "",
|
||
),
|
||
body: Obx(() => !productsController.isUpstoxTokenNotExpired.value
|
||
? Stack(children: [
|
||
const CommonBlurLeft(),
|
||
const CommonBlurRight(),
|
||
Column(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Row(
|
||
children: [
|
||
Image.asset(
|
||
"assets/images/png/under_maintenance.png",
|
||
width: 200,
|
||
height: 220,
|
||
),
|
||
Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
text22W600("Oops!"),
|
||
const Gap(5),
|
||
text16W400("Under Maintenance", clr: Colors.grey)
|
||
],
|
||
)
|
||
],
|
||
),
|
||
const Gap(150),
|
||
],
|
||
)
|
||
])
|
||
: RefreshIndicator(
|
||
color: const Color(0xFF0093FF),
|
||
onRefresh: () async {
|
||
Future.delayed(const Duration(seconds: 1), () {
|
||
isLoading.value = true;
|
||
StockDetailsApi()
|
||
.getStockDetails(FormData.fromMap(
|
||
{"upstox_instrument_key": instrumentName}))
|
||
.then((value) {
|
||
final Map<String, dynamic> data = value.data;
|
||
|
||
Map<String, dynamic> data1 =
|
||
data["data"]['stock_data']['data'];
|
||
String dynamicKey = data1.keys.first;
|
||
// Access nested data using dynamic key
|
||
Map<String, dynamic> dynamicData = data1[dynamicKey];
|
||
|
||
List<Candles>? candles = [];
|
||
|
||
for (var a in data["data"]['candle_stick']["data"]
|
||
["candles"]) {
|
||
candles.add(Candles(
|
||
timestamp: a[0],
|
||
open: a[1].toDouble(),
|
||
high: a[2].toDouble(),
|
||
low: a[3].toDouble(),
|
||
close: a[4].toDouble(),
|
||
volume: a[5].toInt(),
|
||
openInterest: a[6],
|
||
));
|
||
}
|
||
|
||
candles = candles.reversed.toList();
|
||
stockDetailsModel = StockDetailsModel(
|
||
status: data["status"],
|
||
message: data["message"],
|
||
statusCode: data["status_code"],
|
||
data: Data(
|
||
stockData: StockInfo(
|
||
ohlc: Ohlc.fromJson(dynamicData['ohlc']),
|
||
averagePrice:
|
||
dynamicData['average_price'].toDouble(),
|
||
instrumentToken: dynamicData['instrument_token'],
|
||
lastPrice: dynamicData['last_price'].toDouble(),
|
||
lastTradeTime: dynamicData['last_trade_time'],
|
||
lowerCircuitLimit:
|
||
dynamicData['lower_circuit_limit'].toDouble(),
|
||
netChange: dynamicData['net_change'].toDouble(),
|
||
oi: dynamicData['oi'],
|
||
oiDayHigh: dynamicData['oi_day_high'],
|
||
oiDayLow: dynamicData['oi_day_low'],
|
||
symbol: dynamicData['symbol'],
|
||
timestamp: dynamicData['timestamp'],
|
||
totalBuyQuantity:
|
||
dynamicData['total_buy_quantity'],
|
||
totalSellQuantity:
|
||
dynamicData['total_sell_quantity'],
|
||
upperCircuitLimit:
|
||
dynamicData['upper_circuit_limit'],
|
||
volume: dynamicData['volume'],
|
||
),
|
||
candleStick: CandleStick(candles: candles),
|
||
optionChain: OptionChain.fromJson(
|
||
data["data"]['option_chain'])));
|
||
|
||
netChange = dynamicData['net_change'].toString();
|
||
_calculatePercentageChange(
|
||
stockDetailsModel!.data!.stockData!.ohlc!.open!,
|
||
stockDetailsModel!.data!.stockData!.lastPrice!);
|
||
isLoading.value = false;
|
||
});
|
||
});
|
||
},
|
||
child: Stack(children: [
|
||
const CommonBlurLeft(),
|
||
const CommonBlurRight(),
|
||
isLoading.value
|
||
? const Center(
|
||
child: CircularProgressIndicator(
|
||
color: Color(0xFF0093FF),
|
||
),
|
||
)
|
||
: Padding(
|
||
padding: const EdgeInsets.all(15.0),
|
||
child: SingleChildScrollView(
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
text16W700(
|
||
stockDetailsModel!.data!.stockData!.symbol!),
|
||
const Gap(14),
|
||
Row(
|
||
mainAxisAlignment:
|
||
MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
text25W600(
|
||
"₹${stockDetailsModel!.data!.stockData!.lastPrice!.toString()}"),
|
||
Container(
|
||
width: 145,
|
||
height: 40,
|
||
decoration: ShapeDecoration(
|
||
color: const Color(0xFF0093FF),
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius:
|
||
BorderRadius.circular(5)),
|
||
),
|
||
child: Row(
|
||
mainAxisAlignment:
|
||
MainAxisAlignment.center,
|
||
crossAxisAlignment:
|
||
CrossAxisAlignment.center,
|
||
children: [
|
||
SvgPicture.asset(
|
||
"assets/images/svg/option_chain_icon.svg"),
|
||
const Gap(5),
|
||
text12W600("Option Chain"),
|
||
],
|
||
)),
|
||
],
|
||
),
|
||
const Gap(8),
|
||
Row(
|
||
children: [
|
||
text12W400(
|
||
netChange.contains("-")
|
||
? "${stockDetailsModel!.data!.stockData!.netChange} ($percentageDifference%)"
|
||
: "+${stockDetailsModel!.data!.stockData!.netChange} ($percentageDifference%)",
|
||
clr: netChange.contains("-")
|
||
? Colors.redAccent
|
||
: netChange == "0.0"
|
||
? Colors.grey
|
||
: Colors.greenAccent),
|
||
text12W400(" Today"),
|
||
],
|
||
),
|
||
const Gap(55),
|
||
// Container(
|
||
// // margin: const EdgeInsets.symmetric(horizontal: 8),
|
||
// width: Get.width,
|
||
// height: 220,
|
||
// color: Colors.transparent,
|
||
// child: SfCartesianChart(
|
||
// // palette: [
|
||
// // const Color(
|
||
// // 0xFF0093FF,
|
||
// // ).withOpacity(.01),
|
||
// // const Color(
|
||
// // 0xFF0093FF,
|
||
// // ).withOpacity(.3),
|
||
// // ],
|
||
|
||
// tooltipBehavior: _tooltipBehavior,
|
||
// plotAreaBorderWidth: 0,
|
||
// primaryYAxis: NumericAxis(isVisible: false),
|
||
// // Initialize category axis
|
||
// primaryXAxis: CategoryAxis(isVisible: false),
|
||
// series: <LineSeries<SalesData, String>>[
|
||
// LineSeries<SalesData, String>(
|
||
// // Bind data source
|
||
// dataSource: salesDataV,
|
||
// color: Color(0xFF0093FF),
|
||
// enableTooltip: true,
|
||
// xValueMapper: (SalesData sales, _) =>
|
||
// sales.year,
|
||
// yValueMapper: (SalesData sales, _) =>
|
||
// sales.sales)
|
||
// ]),
|
||
// ),
|
||
|
||
Container(
|
||
margin:
|
||
const EdgeInsets.symmetric(horizontal: 15),
|
||
width: Get.width,
|
||
height: 220,
|
||
color: Colors.transparent,
|
||
child: LineChart(
|
||
LineChartData(
|
||
lineTouchData: LineTouchData(
|
||
touchTooltipData: LineTouchTooltipData(
|
||
fitInsideHorizontally: true,
|
||
fitInsideVertically: true,
|
||
|
||
tooltipRoundedRadius:
|
||
9, // maxContentWidth: 150,
|
||
getTooltipItems: (
|
||
List<LineBarSpot> touchedBarSpots,
|
||
) {
|
||
return touchedBarSpots.map((barSpot) {
|
||
return LineTooltipItem(
|
||
"₹ ${Utils.removeDecimal(
|
||
Utils.extractPriceFromGraph(
|
||
stockDetailsModel!
|
||
.data!
|
||
.candleStick!
|
||
.candles!)[
|
||
barSpot.x.toInt()]
|
||
.toStringAsFixed(2),
|
||
)}",
|
||
const TextStyle(
|
||
fontSize: 16,
|
||
fontWeight: FontWeight.w700,
|
||
),
|
||
// children: [
|
||
// const TextSpan(text: "\n"),
|
||
// TextSpan(
|
||
// text: Utils
|
||
// .extractTimeFromGraph(
|
||
// stockDetailsModel!
|
||
// .data!
|
||
// .candleStick!
|
||
);
|
||
}).toList();
|
||
},
|
||
),
|
||
),
|
||
gridData: FlGridData(
|
||
show: true,
|
||
drawVerticalLine: false,
|
||
drawHorizontalLine: false,
|
||
horizontalInterval: 4,
|
||
getDrawingHorizontalLine: (value) {
|
||
return const FlLine(
|
||
color: Color(
|
||
0xff37434d,
|
||
),
|
||
strokeWidth: 1,
|
||
);
|
||
},
|
||
getDrawingVerticalLine: (value) {
|
||
return const FlLine(
|
||
color: Color(
|
||
0xff0093FF,
|
||
),
|
||
strokeWidth: 1,
|
||
);
|
||
},
|
||
),
|
||
titlesData: const FlTitlesData(
|
||
show: false,
|
||
),
|
||
borderData: FlBorderData(
|
||
show: false,
|
||
),
|
||
// minX: 0,
|
||
// maxX: (stockDetailsModel!
|
||
// .data!.candleStick!.candles!.length
|
||
// .toDouble()) -
|
||
// 1,
|
||
// minY: Utils.extractPriceFromGraph(
|
||
// stockDetailsModel!
|
||
// .data!.candleStick!.candles!)
|
||
// .reduce(min)
|
||
// .toDouble(),
|
||
// maxY: Utils.extractPriceFromGraph(
|
||
// stockDetailsModel!
|
||
// .data!.candleStick!.candles!)
|
||
// .reduce(max)
|
||
// .toDouble(),
|
||
lineBarsData: [
|
||
LineChartBarData(
|
||
color: const Color(0xFF0093FF),
|
||
spots: listData(
|
||
Utils.extractPriceFromGraph(
|
||
stockDetailsModel!
|
||
.data!.candleStick!.candles!),
|
||
),
|
||
barWidth: 1.3,
|
||
isStrokeCapRound: true,
|
||
dotData: const FlDotData(
|
||
show: false,
|
||
),
|
||
belowBarData: BarAreaData(
|
||
show: true,
|
||
gradient: LinearGradient(
|
||
colors: [
|
||
const Color(
|
||
0xFF0093FF,
|
||
).withOpacity(.01),
|
||
const Color(
|
||
0xFF0093FF,
|
||
).withOpacity(.3),
|
||
],
|
||
)),
|
||
),
|
||
],
|
||
),
|
||
curve: Curves.linear,
|
||
duration: const Duration(milliseconds: 150),
|
||
),
|
||
),
|
||
|
||
const Gap(20),
|
||
text18W400("Overview"),
|
||
const Gap(15),
|
||
text20W400("Performance"),
|
||
const Gap(22),
|
||
Row(
|
||
mainAxisAlignment:
|
||
MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Column(
|
||
crossAxisAlignment:
|
||
CrossAxisAlignment.start,
|
||
children: [
|
||
text16W400('Today’s Low',
|
||
clr: const Color(0xFF979797)),
|
||
const Gap(5),
|
||
text16W500(
|
||
stockDetailsModel!
|
||
.data!.stockData!.ohlc!.low!
|
||
.toString(),
|
||
)
|
||
],
|
||
),
|
||
Column(
|
||
crossAxisAlignment: CrossAxisAlignment.end,
|
||
children: [
|
||
text16W400('Today’s High',
|
||
clr: const Color(0xFF979797)),
|
||
const Gap(5),
|
||
text16W500(
|
||
stockDetailsModel!
|
||
.data!.stockData!.ohlc!.high!
|
||
.toString(),
|
||
)
|
||
],
|
||
)
|
||
],
|
||
),
|
||
const Gap(40),
|
||
Row(
|
||
mainAxisAlignment:
|
||
MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Column(
|
||
crossAxisAlignment:
|
||
CrossAxisAlignment.start,
|
||
children: [
|
||
text16W400('Open price',
|
||
clr: const Color(0xFF979797)),
|
||
const Gap(5),
|
||
text16W500(
|
||
stockDetailsModel!
|
||
.data!.stockData!.ohlc!.open
|
||
.toString(),
|
||
)
|
||
],
|
||
),
|
||
Column(
|
||
crossAxisAlignment:
|
||
CrossAxisAlignment.start,
|
||
children: [
|
||
text16W400('Prev. Close',
|
||
clr: const Color(0xFF979797)),
|
||
const Gap(5),
|
||
text16W500(
|
||
stockDetailsModel!.data!.candleStick!
|
||
.candles!.last.close
|
||
.toString(),
|
||
)
|
||
],
|
||
),
|
||
Column(
|
||
crossAxisAlignment:
|
||
CrossAxisAlignment.start,
|
||
children: [
|
||
text16W400('Volume',
|
||
clr: const Color(0xFF979797)),
|
||
const Gap(5),
|
||
text16W500(
|
||
stockDetailsModel!
|
||
.data!.stockData!.volume
|
||
.toString(),
|
||
)
|
||
],
|
||
)
|
||
],
|
||
),
|
||
const Gap(25),
|
||
Row(
|
||
children: [
|
||
Column(
|
||
crossAxisAlignment:
|
||
CrossAxisAlignment.start,
|
||
children: [
|
||
text16W400('Lower Circuit',
|
||
clr: const Color(0xFF979797)),
|
||
const Gap(5),
|
||
text16W500(
|
||
stockDetailsModel!
|
||
.data!.stockData!.lowerCircuitLimit
|
||
.toString(),
|
||
)
|
||
],
|
||
),
|
||
const Gap(25),
|
||
Column(
|
||
crossAxisAlignment:
|
||
CrossAxisAlignment.start,
|
||
children: [
|
||
text16W400('Upper Circuit',
|
||
clr: const Color(0xFF979797)),
|
||
const Gap(5),
|
||
text16W500(
|
||
stockDetailsModel!
|
||
.data!.stockData!.upperCircuitLimit
|
||
.toString(),
|
||
)
|
||
],
|
||
)
|
||
],
|
||
),
|
||
const Gap(25),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
]),
|
||
)),
|
||
);
|
||
}
|
||
}
|
||
|
||
List<FlSpot> listData(List<num> data) {
|
||
return data.asMap().entries.map((e) {
|
||
dv.log("Key ${e.key.toString()} Value ${e.value.toString()}");
|
||
return FlSpot(e.key.toDouble(), e.value.toDouble());
|
||
}).toList();
|
||
}
|
||
|
||
class SalesData {
|
||
SalesData(this.year, this.sales);
|
||
final String year;
|
||
final double sales;
|
||
}
|