Flutter syncfusion Linechart re-draw line between beginning and ending series data - flutter

I use syncfusion chart for flutter. I have json data on php api at my server.
I had this data in flutter api connect.
My json data structure is like this:
{
"tablo": "neyzi",
"cinsiyet": "erkek",
"boy": {
"P3": [
{
"0.0": 45.9,
"3.0": 56.2,
"6.0": 62.8,
"9.0": 67.4,
"12.0": 70.8,
"15.0": 73.8,
"18.0": 76.4
}
],
},
}
I use this code for prepare data for chart:
import 'package:flutter/material.dart';
import 'package:pediatrirutinmobil/pers_chart/chart_olcumdizisi.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
import 'chart_api.dart';
class PersentilChartRepository{
static PersentilChartApiClient _persentilchartApiClient = PersentilChartApiClient();
static List<OlcumDizisi> _p3 =[];
static Future apiden_data_getir ()async{
return await _persentilchartApiClient.veriyigetir();
}
static Future<List<OlcumDizisi>> persentilListesi ()async{
}
static List boyListesi() {
apiden_data_getir().then((value) async{
var P3e = await value.boy.P3[0];
for (final mapEntry in P3e.entries) {
final key = await double.parse(mapEntry.key.toString());
final double value = await double.parse(mapEntry.value.toString());
if (key<=limit){
_p3.add(OlcumDizisi(key, value));
}
}
// _p3.addAll([OlcumDizisi(6,60),OlcumDizisi(7, 80),OlcumDizisi(10, 90)]);
*/
} );
List<ChartSeries<OlcumDizisi,double>> chartSeries = [
new LineSeries<OlcumDizisi, double>(
name: 'P3',
xValueMapper: (OlcumDizisi olcum, _) => olcum.yasay,
yValueMapper: (OlcumDizisi olcum, _) => olcum.olcum,
dataSource: _p3,
color: Colors.red,
width: 0.75,
)
];
return chartSeries;
}
}
class OlcumDizisi {
final double yasay;
final double olcum;
OlcumDizisi(this.yasay, this.olcum);
}
And I use chart page like this:
import 'dart:core';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:pediatrirutinmobil/pers_chart/chart_repo.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
class StackedAreaLineChart extends StatefulWidget {
#override
State<StackedAreaLineChart> createState() => _StackedAreaLineChartState();
}
class _StackedAreaLineChartState extends State<StackedAreaLineChart> {
List _charset;
#override
void initState() async{
_charset = await PersentilChartRepository.boyListesi();
setState(() {
});
// TODO: implement initState
super.initState();
}
#override
void dispose() {
_charset;
// TODO: implement dispose
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: SafeArea(
child: Center(
child: Container(
child: SfCartesianChart(
primaryXAxis: CategoryAxis(),
primaryYAxis: NumericAxis(
numberFormat: NumberFormat.decimalPattern()
),
// Chart title
title: ChartTitle(text: 'TITLE'),
// Enable legend
legend: Legend(isVisible: true),
// Enable tooltip
tooltipBehavior: TooltipBehavior(enable: true),
series:_charset,
enableSideBySideSeriesPlacement: false,
),
),
),
),
);
}
}
so after then all of this page my chart build graphic but if I back another page and reopen chart page Linechart re-build new line beginning point and ending point.
like this:
this
and this
If i use static List data in chart page its perfect but i use static data in future code like this
apiden_data_getir().then((value) async{
_p3.addAll([OlcumDizisi(6,60),OlcumDizisi(7, 80),OlcumDizisi(10, 90)]);
} );
final result same...
is there any idea.
If you have different solution binding api line chart so I thank you for it.
We talk on github and
static Future apiden_data_getir() async {
///this code
if (_p3.isNotEmpty) {
_p3.clear();
}
/// helpfull it's work
final String jsonString = await getJsonFromAssets();
final dynamic jsonResponse = json.decode(jsonString);
var p3e = jsonResponse['boy']['P3'][0];

The problem seems to be with your init state.
Every time you visit the graph page, the same data gets added to the data source repeatedly, creating a closed loop within the graph. You can verify that by putting a debugger point at this line series:_charset to see the repeating values added to the series.
Try wrapping your SfCartesianChart widget with a Future builder and fetching data there instead of making an API call in the initState.

Related

How to use ListView with the data of REST API with Flutter using Obx?

import 'dart:convert';
import 'package:get/get.dart';
import '../../../functions/functions.dart';
import '../models/user_details_modal.dart';
class UserListController extends GetxController {
var data = [];
List<UserDetails> userDetails = <UserDetails>[].obs;
// var userDetails = <UserDetails>[].obs;
// RxList<UserDetails> userDetails = <UserDetails>[].obs;
// RxList<UserDetails> userDetails = RxList();
Future<void> fetchData() async {
String apipage = "getData.php";
Map mappeddata = {};
final response = await sendServerData(apipage, mappeddata);
if (response.statusCode == 200) {
final responseBody = jsonDecode(response.body.toString());
userDetails = userDetailsFromJson(json.encode(responseBody));
print(userDetails);
update();
} else {
Get.snackbar(
'Server Connection Failed', 'Please try again after some time');
}
}
#override
void onInit() {
fetchData();
super.onInit();
}
}
Above code is my controller,
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/user_list_controller.dart';
import '../models/user_details_modal.dart';
class UserListView extends GetView<UserListController> {
#override
final UserListController controller = Get.put(UserListController());
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ListView'),
centerTitle: true,
),
body: Obx(
() => controller.userDetails.isEmpty
? Center(
child: Text("Loading data..."),
)
: ListView.builder(
itemCount: controller.userDetails.length,
itemBuilder: (context, index) {
final UserDetails item = controller.userDetails[index];
return buildListTile(item);
},
),
),
);
}
Widget buildListTile(UserDetails item) {
print("userName: ${item.userName}");
print("userEmail: ${item.userEmail}");
return ListTile(
title: Text(item.userName),
subtitle: Text(item.userEmail),
trailing: IconButton(
icon: const Icon(Icons.edit),
onPressed: () {},
),
);
}
}
This is my view,but it's throwing this error -
"[Get] the improper use of a GetX has been detected.
You should only use GetX or Obx for the specific widget that will be updated.
If you are seeing this error, you probably did not insert any observable variables into GetX/Obx
or insert them outside the scope that GetX considers suitable for an update
(example: GetX => HeavyWidget => variableObservable).
If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX."
i'm a beginner in flutter,so please help me to find the issue here.
I have updated the code,i made some changes in controller part declaring that List,now that prev issue is solved!
But the problem is no data showing up on first load (using Get.to() to navigate to this from another page) but when i hot reload/reload this page it shows those data.
What's the problem?
if you want to use reactive approach using Obx() or Getx(), you need to make your variables observable. You can do this simply by adding .obs to the variable. This makes your variables as Rx type.
There are other ways to make your variables observable, but this is the simplest one.
in the controller:
var data = [].obs;
List<UserDetails> userDetails = [].obs;
Also, you don't need to use update() for this approach.
And whenever you want to change the value or use it in your view
you should access the value like this.
in the view inside the Obx: ( in the code above, no need to use .value for the list)
E.x)
bool isLoading = controller.loading.value
Hope this helped :)

Flutter - programmatically update TextField to show scrolling info

I'd like to show some debug info in my App (e. g. user pressed Button A) and was thinking of using the TextField widget for this.
Using below code, I can record ambient sound from my phone and I'd like to add a widget at the bottom that displays timestamps of when the recordings started and stopped, including length of the recording. The idea is to use a ring buffer (package:circular_buffer) that keeps track of a pre-defined number of text lines being displayed in the TextField. Whenever something happens, the new debug info is added as an element to the ring buffer and the TextField is updated. I am very new to flutter, so I'm completely unsure how to achieve this. I was trying to use setState() but I don't know how to call it from other widgets. Is there a way to register as listener to state changes of other widgets and update the text accordingly?
import 'dart:async';
import 'dart:io';
// locale, datetime formatting
import 'package:intl/intl.dart';
import 'package:flutter/material.dart';
import 'package:record/record.dart';
import 'package:path_provider/path_provider.dart';
// local imports
import './display_timer.dart';
void main() {
runApp(myApp());
}
class myApp extends StatefulWidget {
#override
myAppState createState() => myAppState();
}
class myAppState extends State<myApp> {
final record = Record();
bool bPressed = false;
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Record',
home: Scaffold(
appBar: AppBar(
title: const Text('Record'),
),
body: Center(
child:
Column(mainAxisAlignment: MainAxisAlignment.center, children: [
ElevatedButton(
child: bPressed ? const Text("Stop") : const Text("Record"),
onPressed: () {
setState(() {
bPressed = !bPressed;
});
action();
},
),
if (bPressed)
ElapsedTime(timestamp: DateTime.now().toString())
else
const Text("")
]),
),
));
}
String getTimestampSec() {
DateTime now = DateTime.now();
var dateString = DateFormat('yyyyMMdd_hhmmss').format(now);
return dateString;
}
Future<void> action() async {
// Get the state of the recorder
bool isRecording = await record.isRecording();
// Check and request permission
if (await record.hasPermission()) {
if (isRecording) {
// Stop recording
await record.stop();
} else {
// get write dir
// TODO: add fnc so dir only has to be initialize once
Directory appDocDir = await getApplicationDocumentsDirectory();
String appDocPath = appDocDir.path;
String outFile =
"$appDocPath/${getTimestampSec()}_ambient_test.m4a";
print("saving audio to: $outFile");
// Start recording
await record.start(
path: outFile,
encoder: AudioEncoder.aacLc, // by default
bitRate: 128000, // by default
samplingRate: 44100, // by default
);
}
}
print('Pressed');
}
}
To use setState() every time is not good practice rather than you can use ValueNotifier. Every time you update the string value it notify to your TextField and it will update TextField data.
ValueNotifier<String> logText = ValueNotifier<String>("Welcome");
ValueListenableBuilder(
valueListenable: logText,
builder: (context, logTextUpdate,child) {
return Text(
"${logText.value}",
style: TextStyle(fontSize: 25),
);
}),
when you want to update your text, assign value to logText
logText.value = "Happy to see you!"

adding polyline "sub destinations" google maps flutter, optimize for a single API call

The application that I'm trying to create required the creation of a route that has destinations between the starting and ending point, between the beginning and end provided in the getRouteBetweenCoordinates, I need a way to add a custom Latlong pair that must travel through, instead of finding the quickest route, I need it to route between all points that I provided while still following the road (not just a direct line).
The only method that I could come up with is recalling the setPolyLines function for each stretch that makes up the total route. While this method could get the desired result, it required making multiple API calls, ideally, the entirety of the custom route would be loaded upon that first directions API call.
Here is the code that I'm working with, Is there an easier solution to this problem that I missed? This may be very obvious but I'm new with google maps integration so sorry if that's the case.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
//new polyline between each destination
class Tour extends StatefulWidget {
const Tour({Key? key}) : super(key: key);
#override
_TourState createState() => _TourState();
}
class _TourState extends State<Tour> {
late GoogleMapController mapController;
//poly line variables
Set<Polyline> _polyLine = Set<Polyline>();
List<LatLng> polylineCordinates = [];
late PolylinePoints polylinePoints;
//starting location
static const _start =
CameraPosition(target: LatLng(48.696985, -122.905595), zoom: 17.0);
//METHODS
void _onMapCreated(GoogleMapController controller) {
mapController = controller;
//TODO: provide with start and end point for specific line, end of last ==
//start of next
setPolyLines(PointLatLng(48.696985, -122.905595),
PointLatLng(48.657421, -122.917412));
setPolyLines(PointLatLng(48.657421, -122.917412),
PointLatLng(48.644983, -122.944760));
}
void setPolyLines(PointLatLng start, PointLatLng end) async {
//polyline result DT is a collection of latlng following roads
PolylineResult result = await polylinePoints.getRouteBetweenCoordinates(
"MY API KEY IS HERE",
//route start
start,
//route end
end);
//list of latlng pairs in order of exectecution
//this is preparing the drawing of the line, the set state plots it out
if (result.status == 'OK') {
result.points.forEach((PointLatLng point) {
polylineCordinates.add(LatLng(point.latitude, point.longitude));
});
}
setState(() {
_polyLine.add(Polyline(
width: 10,
//set id to
polylineId: PolylineId("route"),
color: Color(0xFF00BFA6),
points: polylineCordinates));
});
}
#override
void initState() {
polylinePoints = PolylinePoints();
}
#override
void dispose() {
mapController.dispose();
super.dispose();
}
//upon call, modal sheet toggles from the bottom of screen
modalSheet() {
showModalBottomSheet(
context: context,
builder: (context) {
return Column(
children: [
Container(
height: 200,
color: Colors.amber,
),
Container(
height: 100,
color: Colors.blue,
)
],
);
});
}
//adjusts camera position to the _start location
center() {
mapController.animateCamera(CameraUpdate.newCameraPosition(_start));
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GoogleMap(
polylines: _polyLine,
myLocationButtonEnabled: false,
zoomControlsEnabled: false,
onMapCreated: _onMapCreated,
initialCameraPosition: _start),
floatingActionButton: FloatingActionButton(
onPressed: () => center(), child: Icon(Icons.add)),
);
}
}
You can use wayPoints parameter of getRouteBetweenCoordinates method which accepts a list of PolylineWayPoint (List<PolylineWayPoint>).
PolylineResult result = await polylinePoints.getRouteBetweenCoordinates(
googleAPiKey,
PointLatLng(48.696985, -122.905595),
PointLatLng(48.644983, -122.944760),
wayPoints: [PolylineWayPoint(location: "48.657421,-122.917412")]);
Please see the image below for the result using your sample code.

Flutter: Bars are not displaying

I am trying to display the api value on bar chart,
my api data is look like this
{leaves: 3, sick: 2, annual: 0, maternity: 1}
charts.dart
i am getting this data from login screen, and passing it to the leave screen using shared perference.
i want to display it in bar chart, here is my try to do this
class LeavesCharts extends StatelessWidget {
final List<LeavesSeries> data;
LeavesCharts({#required this.data});
#override
Widget build(BuildContext context) {
List<charts.Series<LeavesSeries, String>> series=[
charts.Series(
id: "Leaves",
data: data,
domainFn: (LeavesSeries series, _)=>series.totalleave,
measureFn: (LeavesSeries series,_)=>series.annual,
colorFn: (LeavesSeries series,_)=>series.barColor,
)
];
return
Container(
height: 400,
padding: EdgeInsets.all(20),
child: Card(child: Padding(padding: const EdgeInsets.all(8.0),
child: Column(children: <Widget>[
Text("History of leaves"),
Expanded(child: charts.BarChart(series,animate: true,))
],),
)
,),);
}
}
here i am displaying the chart
String annualCount="4";
String totalleaveCount="4";
String sickCount="4";
String maternityCount="1";
class _RequestForLeaveState extends State<RequestForLeave> {
//getting values using shared perference
_userDetails() async {
SharedPreferences myPrefs = await SharedPreferences.getInstance();
setState(() {
sickCount=myPrefs.getString('sickcount');
totalleaveCount=myPrefs.getString('totalleave');
maternityCount=myPrefs.getString('maternitycount');
annualCount=myPrefs.getString('annualcount');
;
});
}
final List<LeavesSeries> data=[
LeavesSeries(
totalleave: totalleaveCount,
maternity: maternityCount,
sick:sickCount,
annual: 3,
barColor:charts.ColorUtil.fromDartColor(Colors.blue)
),
];
Widget build(BuildContext context) {
return new Scaffold(
appBar: new MyAppBar(
title: Text("Request for leave"),
// onpressed: () {
// Navigator.push(
// context, MaterialPageRoute(builder: (context) => Profile()));
// },
),
drawer: drawer(),
body: LeavesCharts(data:data)
);
}
}
class LeavesSeries {
String totalleave;
int annual;
String sick;
String maternity;
final charts.Color barColor;
LeavesSeries({#required this.totalleave,#required this.annual,#required this.sick,#required this.maternity, #required this.barColor});
}
but it is not displaying any bar, here is the output
update output:
i initialize varibles with some values and it display bar according to that, but i want 4 bars for each variable, how i can do that?
please help where i am doing wrong
Your data object does not contain the newly set data.
In _userDetails() the 4 variables get updated from the prefs, that should work just fine. Because the state has updated, build will be called. However, build will not use these newly set variables in the state, but instead it will access and use the 'old' data object, which contained only empty strings. It does not get overwritten with your new object.
I suggest putting the data object into the state, using the prefs you already retrieve
class _RequestForLeaveState extends State<RequestForLeave> {
LeavesCharts data;
#override
initState() {
super.initState();
data = LeavesSeries(
totalleave: 0,
maternity: 0,
sick: 0,
annual: 0,
barColor: charts.ColorUtil.fromDartColor(Colors.blue)
);
}
//getting values using shared perference
_userDetails() async {
SharedPreferences myPrefs = await SharedPreferences.getInstance();
setState(() {
data = LeavesSeries(
totalleave: myPrefs.getString('totalleave'),
maternity: myPrefs.getString('maternitycount'),
sick: myPrefs.getString('sickcount'),
annual: myPrefs.getString('annualcount'),
barColor: charts.ColorUtil.fromDartColor(Colors.blue)
)
});
}
...
}
Then in build, you can access this data object and use it as expected to retrieve all of its values.

Data is showing only after I hot reload or refresh in flutter

In my database I have a user table where username, some marks are stored. I want to view that data in a table when user go to a particular page. So, when I go to that page at first the data(username and marks) doesn't show. But if I hot reload or use a button to refresh then the data shows properly. My question is how can I do that without hot reload or using a refresh button. Here is my code:
import 'package:flutter/material.dart';
import 'package:flutter_app/database/db_helper.dart';
import 'package:flutter_app/database/user.dart';
class ViewResult extends StatefulWidget {
#override
_ViewResult createState() => _ViewResult();
}
GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey();
class _ViewResult extends State<ViewResult> {
String tempEmail;
bool check = true;
var dbHelper;
var data = List<User>();
#override
void initState() {
setState(() {
dbHelper = DBHelper();
resultData();
});
super.initState();
}
void resultData() {
dbHelper.getUser().then((users) {
for (User temp in users) {
if (temp.type == "student") data.add(temp);
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text("Result"),
),
body: DataTable(
columnSpacing: 22,
columns: [
DataColumn(label: Text("Username")),
DataColumn(label: Text("Beginner")),
DataColumn(label: Text("Intermediate")),
DataColumn(label: Text("Advanced")),
],
rows: data
.map((user) => DataRow(cells: [
DataCell(Text(user.email)),
DataCell(Text(user.beginnerMark.toString())),
DataCell(Text(user.intermediateMark.toString())),
DataCell(Text(user.advancedMark.toString())),
]))
.toList(),
),
backgroundColor: Color.fromRGBO(130, 178, 220, 1),
);
}
}
TLDR: Call setState() from inside then() if not, it will be executed before everything completes and have no effect at all.
getUser().then((response) {
//Your stuff
setState(() {}); //refresh
});
You have to use setState after you have loaded the data and your resultData functions is working with then, that is executed after the initial setState in the initState is finished.
#override
void initState() {
// super.initState(); should be at the start of the method
super.initState();
dbHelper = DBHelper();
resultData();
}
void resultData() {
dbHelper.getUser().then((users) {
for (User temp in users) {
if (temp.type == "student") data.add(temp);
}
// since you have already added the results to data
// setState can have an empty function body
setState(() {});
});
}
I prefer working with async/await instead of then so my solutions would be as follows:
Future<void> resultData() async {
List<User> users = await dbHelper.getUser();
setState(() {
data = users.where((user) => user.type == "student");
});
}
You need setState(() {});.
Add it near the end of resultData function:
void resultData() {
dbHelper.getUser().then((users) {
for (User temp in users) {
if (temp.type == "student") data.add(temp);
}
setState(() {});
});
}
See setState for more information.