Flutter Integration
Integrate NeoCharts into a Flutter app by implementing ChartInterface and mounting NxtChartPage.
Drop in one widget, implement one interface, get a fully interactive chart with real-time data, technical indicators, order/position overlays, and drawing tools.
This is the complete integration — trading, alerts, option chain, and scalper mode. Coming from Quick Start? Steps 1–3 below are the same ones you just ran.
1. Add the dependency
Access
NeoCharts is distributed directly to licensed partners, not published to pub.dev. Your IOURING contact provides the Git repository URL and version tag below.
# pubspec.yaml
dependencies:
nxtchart:
git:
url: <repository-url>
ref: <version-tag>Pick ref: from the repository's git tags (e.g. nxtchart-v0.0.35+17) — don't invent one.
fvm flutter pub get2. Implement ChartInterface
import 'package:nxtchart/interface.dart';
class MyChartController implements ChartInterface {
@override
String get storageKey => 'user_123'; // stable, user-scoped key
@override
String get symbolInfo => jsonEncode({
'id': 'RELIANCE',
'name': 'Reliance Industries',
'lotSize': 1,
'precision': 2,
'tickSize': 0.05,
'exchange': 'NSE',
'expiry': '',
'strike': '',
'optType': '',
'weekly': '',
'undID': '',
});
@override
String? get underlyingSymbolInfo => null; // null for equities
@override
String get optionSymbols => '[]';
@override
String? get futureSymbols => null;
@override
String? get indexSymbols => null;
@override
Future<String?> get atmSymbols async => null;
@override
String get marketTiming => jsonEncode({
'timezone': 'Asia/Kolkata',
'sessions': [
['0915-1530'], // Monday
['0915-1530'], // Tuesday
['0915-1530'], // Wednesday
['0915-1530'], // Thursday
['0915-1530'], // Friday
<String>[], // Saturday
<String>[], // Sunday
],
'holidays': <String>[],
'special': <String, dynamic>{},
});
@override
Future<String> get chartTopOptions async {
// Return JSON list of top options ranked by volume (same shape as optionSymbols)
return yourApi.fetchTopOptions();
}
@override
Future<String> loadData({
required String symbolId,
required int from,
required int to,
required int intervalSeconds,
required int requiredBars,
}) async {
final bars = await yourApi.fetchOHLCV(
symbolId: symbolId,
from: from,
to: to,
interval: intervalSeconds,
);
return jsonEncode(bars);
}
@override
Stream<String> marketDataStreamer(String symbols) {
return yourTickStream.map((ticks) => jsonEncode(ticks));
}
@override
Stream<String> searchSymbolsStreamer(String query) {
return yourApi.searchSymbols(query).asStream().map(jsonEncode);
}
@override
// Called when the user taps a related/underlying symbol on screen (e.g. an
// option's underlying). Return a ChartInterface for that symbol to enable
// in-chart navigation, or null to disable it.
ChartInterface? chartInterfaceForSymbol(String symbolJson) => null;
@override
bool get hasOCO => true;
@override
void placeOrder(String params) {
yourBroker.placeOrder(jsonDecode(params));
}
@override
void modifyOrder(String params) => yourBroker.modifyOrder(jsonDecode(params));
@override
void cancelOrder(String orderID) => yourBroker.cancelOrder(orderID);
@override
void placeOCOOrder(String params) =>
yourBroker.placeOCOOrder(jsonDecode(params));
@override
void modifyOCOOrder(String params) =>
yourBroker.modifyOCOOrder(jsonDecode(params));
@override
void cancelOCOOrder(String groupId) => yourBroker.cancelOCOOrder(groupId);
@override
void groupAdjustOrders(String params) =>
yourBroker.groupAdjustOrders(jsonDecode(params));
@override
Stream<String> get ordersStreamer => yourOrdersStream;
@override
Stream<String> get positionsStreamer => yourPositionsStream;
@override
Stream<String> get actionFeedbackStreamer =>
yourActionFeedbackStream.asBroadcastStream();
@override
Stream<String> get ocoOrdersStreamer => yourOcoOrdersStream;
@override
Future<String> fetchOptionDetails({required String underlyingSymbolId}) =>
yourApi.fetchOptionChain(underlyingSymbolId);
@override
Future<String?> fetchOIAnalysis({
required String underlyingSymbolId,
required String expiry,
required int timeFrom,
required int timeTo,
}) =>
yourApi.fetchOIAnalysis(underlyingSymbolId, expiry, timeFrom, timeTo);
@override
Future<String?> fetchOIChange({
required String underlyingSymbolId,
required List<String> expiries,
required int timeFrom,
required int timeTo,
}) =>
yourApi.fetchOIChange(underlyingSymbolId, expiries, timeFrom, timeTo);
@override
Future<String?> fetchOI({
required String underlyingSymbolId,
required List<String> expiries,
}) =>
yourApi.fetchOI(underlyingSymbolId, expiries);
@override
Future<String> fetchPcrIntraday() => yourApi.fetchPcrIntraday();
@override
Future<String> fetchAtmStraddleIntraday() =>
yourApi.fetchAtmStraddleIntraday();
@override
Future<String> fetchAtmIvIntraday() => yourApi.fetchAtmIvIntraday();
@override
void modifyAlert(String params) => yourBroker.modifyAlert(jsonDecode(params));
@override
void createAlert(String params) => yourBroker.createAlert(jsonDecode(params));
@override
void deleteAlert(String alertId) => yourBroker.deleteAlert(alertId);
@override
Stream<String> get alertsStreamer => yourAlertsStream;
@override
void dispose() {
// close streams, cancel subscriptions
}
}Must be broadcast streams
ordersStreamer, positionsStreamer, ocoOrdersStreamer, and actionFeedbackStreamer must be
broadcast streams (.asBroadcastStream() or a broadcast StreamController) — a
single-subscription stream crashes with Bad state: Stream has already been listened to.
3. Mount the widget
import 'package:nxtchart/widgets.dart';
NxtChartPage(dataProvider: MyChartController())Theming
Theming reads your app's Material Theme — the chart uses colorScheme.primary/onPrimary
for its accent color and follows light/dark mode. Requires a Material Theme ancestor with
those set; Cupertino-only apps or an unset color scheme won't get sensible theming for free.
Run the app — you should see a live chart with real-time data, indicators, drawing tools, and working order/position/alert actions.
Scalper mode
Scalper opens the chart in landscape with two side-by-side panels:
- Left panel — main price chart for the primary symbol
- Right panel — ATM CE/PE strike chart (requires
atmSymbols; returnnullto leave blank)
@override
Future<String?> get atmSymbols async =>
jsonEncode(['NIFTY24DEC24000CE', 'NIFTY24DEC24000PE']);Mount with the .scalper constructor instead of the standard one — no additional Dart code needed. Android hosts do need one manifest flag; see below.
NxtChartPage.scalper(dataProvider: MyChartController())Required, not optional
Android: the host activity must set android:enableOnBackInvokedCallback="false" — see
Android integration § 5 for why. Without it, the
system back gesture can exit the whole activity instead of closing an open chart overlay.
Troubleshooting
| Symptom | Fix |
|---|---|
| Back button exits the whole chart/app instead of closing an overlay (Android) | Set android:enableOnBackInvokedCallback="false" on the host <activity> — see android § 5 |
"Bad state: Stream has already been listened to"
Your streamer is a single-subscription stream. Wrap it:
@override
Stream<String> get actionFeedbackStreamer =>
_myController.stream.asBroadcastStream();Or create the controller as broadcast from the start:
final _controller = StreamController<String>.broadcast();This affects actionFeedbackStreamer, ordersStreamer, positionsStreamer, and ocoOrdersStreamer.
loadData never completes / chart stays blank
Check that your loadData implementation is async and actually awaits its data source:
@override
Future<String> loadData({...}) async {
final bars = await yourApi.fetchOHLCV(...); // must await
return jsonEncode(bars);
}A missing await returns an empty future immediately, which the SDK interprets as an empty dataset.
Chart state lost after navigation
Call dispose() on your ChartInterface implementation when the host widget is removed from the tree:
@override
void dispose() {
_chartController.dispose(); // your ChartInterface instance
super.dispose();
}Without this, the chart engine holds subscriptions open and accumulated state may be replayed on the next mount.
See the API Reference for all ChartInterface members and JSON Schemas for wire formats.