Related
I want to show data when i clicked dot plot chats
like this (for example i use SfCircularChart but i want to use Scatter it's not free for SfCircularChart ,So I had to build it myself):
My code result this:
I want this:
this is my code:
class SimpleScatterPlotChart extends StatelessWidget {
List<charts.Series<_HeartRateData, num>> seriesList;
final bool animate;
SimpleScatterPlotChart({required this.seriesList, this.animate = false});
factory SimpleScatterPlotChart.withSampleData() {
return new SimpleScatterPlotChart(
seriesList: _createSampleData(),
animate: false,
);
}
#override
Widget build(BuildContext context) {
return Container(
height: 250,
width: 350,
child: new charts.ScatterPlotChart(
seriesList,
animate: animate,
primaryMeasureAxis: new charts.NumericAxisSpec(
renderSpec: new charts.GridlineRendererSpec(
labelStyle: new charts.TextStyleSpec(
color: charts.Color.fromHex(code: '#7A7A7A')
),
)
),
//primaryMeasureAxis: new charts.NumericAxisSpec(renderSpec: new charts.NoneRenderSpec()),
domainAxis: charts.NumericAxisSpec(
showAxisLine: false,
renderSpec: charts.NoneRenderSpec(),
),
),
);
}
/// Create one series with sample hard coded data.
static List<charts.Series<_HeartRateData, num>> _createSampleData() {
return [
new charts.Series<_HeartRateData, num>(
id: 'Sales',
colorFn: (_HeartRateData sales, _) {
//final bucket = sales.heartRate / maxMeasure;
if (sales.heartRate < 61) {
return charts.Color.fromHex(code: '#F8D277');
} else if (sales.heartRate < 100) {
return charts.Color.fromHex(code: '#61D2A4');
} else {
return charts.Color.fromHex(code: '#DD5571');
}
},
domainFn: (_HeartRateData sales, _) => sales.date,
measureFn: (_HeartRateData sales, _) => sales.heartRate,
radiusPxFn: (_HeartRateData sales, _) => sales.radius,
data: apiData,
),
];
}
}
I have tried creating this bar graph using SfCartesianChart, and have been able to complete 95% of it, but unable to remove the middle labels.
...
Lobo,
Greetings from Syncfusion.
We have validated your query and we have achieved your requirement by using the axisLabelFormatter callback in axis. We have rendered the first and last axis labels alone using this axisLabelFormatter callback, and its invoked while rendering each axis label in the chart. Please refer the following code snippet.
Code snippet :
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Syncusion flutter charts'),
centerTitle: true,
),
body: SfCartesianChart(
primaryXAxis: CategoryAxis(
axisLabelFormatter: (AxisLabelRenderDetails details) {
String text = details.value == 0
? 'Jun 2022'
: details.value == chartData.length - 1
? 'Mar 2022'
: "";
return ChartAxisLabel(text, details.textStyle);
},
majorGridLines: const MajorGridLines(width: 0),
majorTickLines: const MajorTickLines(width: 0),
),
primaryYAxis: NumericAxis(
isVisible: false,
majorTickLines: const MajorTickLines(width: 0),
majorGridLines: const MajorGridLines(width: 0),
),
series: <ChartSeries<ChartSampleData, String>>[
ColumnSeries<ChartSampleData, String>(
dataSource: chartData,
dataLabelSettings: const DataLabelSettings(isVisible: true),
xValueMapper: (ChartSampleData sales, _) => sales.x,
yValueMapper: (ChartSampleData sales, _) => sales.y),
],
),
),
);
}
}
class ChartSampleData {
ChartSampleData(this.x, this.y);
final String? x;
final double? y;
}
final List<ChartSampleData> chartData = [
ChartSampleData('jan', 50.56),
ChartSampleData('Feb', 49.42),
ChartSampleData('Mar', 53.21),
ChartSampleData('Apr', 64.78),
ChartSampleData('May', 59.97),
];
ScreenShot:
Syncfusion Custom Axis Label
Regards,
Lokesh Palani.
How can I set the range in x axis in charts_flutter? My data sample is like (5000, 5.0),(5001, 25.2),(5002, 100.5),(5003, 75.8).
My code is
/// Example of a simple line chart.
import 'package:charts_flutter/flutter.dart' as charts;
import 'package:flutter/material.dart';
class SimpleLineChart extends StatelessWidget {
final List<charts.Series> seriesList;
final bool animate;
SimpleLineChart(this.seriesList, {this.animate});
/// Creates a [LineChart] with sample data and no transition.
factory SimpleLineChart.withSampleData() {
return SimpleLineChart(
_createSampleData(),
// Disable animations for image tests.
animate: false,
);
}
#override
Widget build(BuildContext context) {
return Container(
height: 300,
// decoration: const BoxDecoration(color: Color(0xff232d37)),
width: double.infinity,
padding:
const EdgeInsets.only(right: 18.0, left: 12.0, top: 24, bottom: 12),
child: charts.LineChart(seriesList, animate: animate),
);
}
/// Create one series with sample hard coded data.
static List<charts.Series<LinearSales, int>> _createSampleData() {
final data = [
LinearSales(5000, 5.0),
LinearSales(5001, 25.2),
LinearSales(5002, 100.5),
LinearSales(5003, 75.8),
];
return [
charts.Series<LinearSales, int>(
id: 'Sales',
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
domainFn: (LinearSales sales, _) {
return sales.year;
},
measureFn: (LinearSales sales, _) {
return sales.sales;
},
data: data,
domainLowerBoundFn: (s, _) => 5000,
domainUpperBoundFn: (s, _) => 5010,
)
];
}
}
/// Sample linear data type.
class LinearSales {
final int year;
final double sales;
LinearSales(this.year, this.sales);
}
The charts looks like
It shows a straight line but I want x axis to start at 5000 and end on 5010. I tried setting it in
domainLowerBoundFn and domainUpperBoundFn but it still starts at 0. How do I fix it?
Ref: Charts Flutter Gallery
You can set domainAxis.
How about this?
child: charts.LineChart(
seriesList,
animate: animate,
domainAxis: const charts.NumericAxisSpec(
tickProviderSpec:
charts.BasicNumericTickProviderSpec(zeroBound: false),
viewport: charts.NumericExtents(5000.0, 5005.0),
),
),
or this?
child: charts.LineChart(
seriesList,
animate: animate,
domainAxis: const charts.NumericAxisSpec(
tickProviderSpec:
charts.BasicNumericTickProviderSpec(zeroBound: false),
),
),
You can use the next code for Y-axis:
primaryMeasureAxis: charts.NumericAxisSpec(
tickProviderSpec:
charts.BasicNumericTickProviderSpec(desiredTickCount: 3)),
Full example looks like:
ScatterPlotChart(
[seriesDistorted],
animate: true,
domainAxis: const NumericAxisSpec(
tickProviderSpec: BasicNumericTickProviderSpec(
// Vertical axis every 2 ticks from 0 to 10
dataIsInWholeNumbers: true,
zeroBound: false,
desiredTickCount: 6),
),
primaryMeasureAxis: const NumericAxisSpec(
tickProviderSpec: BasicNumericTickProviderSpec(desiredTickCount: 6)),
);
I am trying to use CheckboxListTile in a popup widget so the user can check as many items as needed and then click OK to accept the checked items which adds them to a list of items in main widget
What happens is CheckboxListTile loses checked state when scrolling down for some items, when getting back up in the list it is not checked anymore,
here is my code for the CheckboxListTile
class ItemTileWithImage extends StatefulWidget {
final Model model;
ItemTileWithImage(this.model);
#override
_ItemTileWithImageState createState() => _ItemTileWithImageState();
}
class _ItemTileWithImageState extends State<ItemTileWithImage> {
bool _checked = false;
#override
Widget build(BuildContext context) {
Color hintColor = Theme.of(context).hintColor;
Color primaryColor = Theme.of(context).primaryColor;
return Container(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
height: 85,
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(color: hintColor, width: 0.5),
),
),
child: CheckboxListTile(
activeColor: primaryColor,
checkColor: Colors.white,
value: _checked,
onChanged: (val) {
setState(() {
_checked = val;
});
if (val) {
Provider.of<OrderProvider>(context, listen: false)
.insertModelItems(widget.model.code);
} else {
Provider.of<OrderProvider>(context, listen: false)
.removeModelItems(widget.model.remoteId);
}
},
secondary: widget.model.imageUrl != null
? CachedNetworkImage(
height: 80,
width: 60,
imageUrl: widget.model.imageUrl,
progressIndicatorBuilder: (context, url, downloadProgress) {
return Center(
child: Container(
width: 30,
height: 30,
child: CircularProgressIndicator(
value: downloadProgress.progress,
backgroundColor: primaryColor,
strokeWidth: 2,
),
),
);
},
errorWidget: (context, url, error) => Icon(Icons.error),
)
: Image.asset(
'assets/images/mini_logo.png',
width: 50,
height: 50,
),
title: Text(widget.model.code ?? '${widget.model.barCode}'),
subtitle: Text(
widget.model.name ?? '',
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
);
}
}
Flutter apps typically 'clean up' your off screen state. Some off-screen widgets will experience the loss of certain view related attributes. This is discussed some in the Keys section here. You can instantiate stateless widgets in a way where their variable attributes are stored off for later recall.
Here is a demo utilizing rows of CheckboxListTile widgets which keep their state even with scrolling/going off-screen. You can use a DartPad of the below code here.
// Remember CheckboxListTile status with scrolling demo.
// Currently utilizes rows in implementation.
// This can be cut down to maintain a single checkbox list tile.
import 'package:flutter/material.dart';
void main() => runApp(const MaterialApp(
home: Home(),
));
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
// Refresh Callback for descendant widgets (if checkboxes are generated in seperate widget)
refresh() {setState(() {});}
// Rows with checkbox title and check value
// Must update second value in entry to maintain check status while scrolling
List<List> rowData = [
['row1', true], ['row2', false], ['row3', false],
['row4', false], ['row5', false], ['row6', false],
['row7', false], ['row8', false], ['row9', false],
['row10', false], ['row11', false], ['row12', false],
['row13', false], ['row14', false], ['row15', false],
['row16', false], ['row17', false], ['row18', false],
['row19', false], ['row20', false], ['row21', false],
['row22', false], ['row23', false], ['row24', false]];
List<Row> rows = []; // Holds row objects generated from rowData (with checkboxes)
int row = 0;
int rowCount = 0;
#override
Widget build(BuildContext context) {
rows = []; // Refresh rows
for (int i=0; i<rowData.length; i++) { // Init/refresh row widgets
rows.add(Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded (
child: CheckboxListTile(
value: rowData[i][1] as bool, // Check value
title: SizedBox(
width: 100,
child: Text(rowData[i][0], style: const TextStyle(fontSize: 50))
), // Title for check box
checkColor: Colors.black,
dense: false,
onChanged: (newValue) { // On click of check box
setState(() {
print("Checkbox clicked");
rowData[i][1] = newValue; // Update value in the RowData list
});
}
),
)
]
));
}
return Scaffold(
appBar: AppBar(
title: const Text('Remember Checkboxes Demo'),
centerTitle: true,
backgroundColor: const Color(0xff66b366),
),
body: Column (
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: ListView(
children: <Widget>[for (var i in rows) i,] // Fill in rows
),
)
]
)
);
}
}
Abstracting the idea for the question's implementation, you can replace your _checked variable to be a passed in boolean from the parent of the ItemTileWithImage widget. Also, you would want to pass in a function, like the refresh() function in the demo, to call in the onChange block after updating your shared check value. This allows you to refresh the state of your parent widget to include the updated check status.
Note: I am a relative novice with Flutter so apologies if this solution is considered bad practice.
The above implementation was developed with Flutter 2.5.3 and Dart 2.14.4.
I have been working with the online gallery of Flutter charts (https://google.github.io/charts/flutter/gallery.html) but I'm struggling to add a title for x & y axis values.
Can somebody help me or tell me how to add the labels to the graph?
Its possible using behaviors property, check the code
var chart = charts.LineChart(seriesList,
behaviors: [
new charts.ChartTitle('Dimension',
behaviorPosition: charts.BehaviorPosition.bottom,
titleStyleSpec: chartsCommon.TextStyleSpec(fontSize: 11),
titleOutsideJustification:
charts.OutsideJustification.middleDrawArea),
new charts.ChartTitle('Dose, mg',
behaviorPosition: charts.BehaviorPosition.start,
titleStyleSpec: chartsCommon.TextStyleSpec(fontSize: 11),
titleOutsideJustification:
charts.OutsideJustification.middleDrawArea)
],
defaultRenderer: new charts.LineRendererConfig(includePoints: true));
Source https://google.github.io/charts/flutter/example/behaviors/chart_title
use the 'behavior' list for set title of chart
Widget build(BuildContext context) {
return new charts.LineChart(
seriesList,
animate: animate,
behaviors: [
new charts.ChartTitle('Top title text',
subTitle: 'Top sub-title text',
behaviorPosition: charts.BehaviorPosition.top,
titleOutsideJustification: charts.OutsideJustification.start,
innerPadding: 18),
new charts.ChartTitle('Bottom title text',
behaviorPosition: charts.BehaviorPosition.bottom,
titleOutsideJustification:
charts.OutsideJustification.middleDrawArea),
new charts.ChartTitle('Start title',
behaviorPosition: charts.BehaviorPosition.start,
titleOutsideJustification:
charts.OutsideJustification.middleDrawArea),
new charts.ChartTitle('End title',
behaviorPosition: charts.BehaviorPosition.end,
titleOutsideJustification:
charts.OutsideJustification.middleDrawArea),
],
);
}
You can do it by using behaviors using line annotations iterating your list data and make a new LineAnnotationSegment array but you should be aware that some titles may overlap when the next time point is very close.
final data = [
LinearPrices(DateTime(2020, 9, 19), 5),
LinearPrices(DateTime(2020, 9, 26), 15),
LinearPrices(DateTime(2020, 10, 3), 20),
LinearPrices(DateTime(2020, 10, 10), 17),
];
#override
Widget build(BuildContext context) {
return charts.TimeSeriesChart(seriesList, animate: false, behaviors: [
charts.RangeAnnotation( data.map((e) => charts.LineAnnotationSegment(
e.timestamp, charts.RangeAnnotationAxisType.domain,
middleLabel: '\$${e.price}')).toList()),
]);
}
Nevertheless you can use a callback to paint when the user clicks the line by painting either a custom text at the bottom or as a custom label using behaviors like this:
import 'package:flutter/material.dart';
import 'package:charts_flutter/flutter.dart' as charts;
import 'package:intl/intl.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
final data = [
LinearPrices(DateTime(2020, 9, 19), 5),
LinearPrices(DateTime(2020, 9, 26), 15),
LinearPrices(DateTime(2020, 10, 3), 20),
LinearPrices(DateTime(2020, 10, 10), 17),
];
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Chart'),
),
body: ChartPricesItem(data),
));
}
}
class ChartPricesItem extends StatefulWidget {
final List<LinearPrices> data;
ChartPricesItem(this.data);
static List<charts.Series<LinearPrices, DateTime>> _createSeries(
List<LinearPrices> data) {
return [
charts.Series<LinearPrices, DateTime>(
id: 'Prices',
colorFn: (_, __) => charts.MaterialPalette.deepOrange.shadeDefault,
domainFn: (LinearPrices sales, _) => sales.timestamp,
measureFn: (LinearPrices sales, _) => sales.price,
data: data,
)
];
}
#override
_ChartPricesItemState createState() => _ChartPricesItemState();
}
class _ChartPricesItemState extends State<ChartPricesItem> {
DateTime _time;
double _price;
// Listens to the underlying selection changes, and updates the information relevant
void _onSelectionChanged(charts.SelectionModel model) {
final selectedDatum = model.selectedDatum;
DateTime time;
double price;
// We get the model that updated with a list of [SeriesDatum] which is
// simply a pair of series & datum.
if (selectedDatum.isNotEmpty) {
time = selectedDatum.first.datum.timestamp;
price = selectedDatum.first.datum.price;
}
// Request a build.
setState(() {
_time = time;
_price = price;
});
}
#override
Widget build(BuildContext context) {
final simpleCurrencyFormatter =
charts.BasicNumericTickFormatterSpec.fromNumberFormat(
NumberFormat.compactSimpleCurrency());
var behaviors;
// Check if the user click over the line.
if (_time != null && _price != null) {
behaviors = [
charts.RangeAnnotation([
charts.LineAnnotationSegment(
_time,
charts.RangeAnnotationAxisType.domain,
labelDirection: charts.AnnotationLabelDirection.horizontal,
labelPosition: charts.AnnotationLabelPosition.margin,
labelStyleSpec:
charts.TextStyleSpec(fontWeight: FontWeight.bold.toString()),
middleLabel: '\$$_price',
),
]),
];
}
var chart = charts.TimeSeriesChart(
ChartPricesItem._createSeries(widget.data),
animate: false,
// Include timeline points in line
defaultRenderer: charts.LineRendererConfig(includePoints: true),
selectionModels: [
charts.SelectionModelConfig(
type: charts.SelectionModelType.info,
changedListener: _onSelectionChanged,
)
],
// This is the part where you paint label when you click over the line.
behaviors: behaviors,
// Sets up a currency formatter for the measure axis.
primaryMeasureAxis: charts.NumericAxisSpec(
tickFormatterSpec: simpleCurrencyFormatter,
tickProviderSpec:
charts.BasicNumericTickProviderSpec(zeroBound: false)),
/// Customizes the date tick formatter. It will print the day of month
/// as the default format, but include the month and year if it
/// transitions to a new month.
///
/// minute, hour, day, month, and year are all provided by default and
/// you can override them following this pattern.
domainAxis: charts.DateTimeAxisSpec(
tickFormatterSpec: charts.AutoDateTimeTickFormatterSpec(
day: charts.TimeFormatterSpec(
format: 'd', transitionFormat: 'dd/MM/yyyy'),
minute: charts.TimeFormatterSpec(
format: 'mm', transitionFormat: 'dd/MM/yyyy HH:mm'))),
);
var chartWidget = Padding(
padding: EdgeInsets.all(16),
child: SizedBox(
height: 200.0,
child: chart,
),
);
final children = <Widget>[chartWidget];
// If there is a selection, then include the details.
if (_time != null) {
children.add(Padding(
padding: EdgeInsets.only(top: 4.0),
child: Text(DateFormat('dd/MM/yyyy hh:mm').format(_time),
style: Theme.of(context).textTheme.bodyText1)));
}
return SingleChildScrollView(
child: Column(
children: <Widget>[
const SizedBox(height: 8),
Text("Product Prices", style: Theme.of(context).textTheme.headline5),
Column(children: children),
],
),
);
}
}
/// Sample linear data type.
class LinearPrices {
final DateTime timestamp;
final double price;
LinearPrices(this.timestamp, this.price);
}
This is the result: