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.
Related
I implemented the shared preferences package in my Flutter app, with a list widget as radio button, that only save the language preference and not the checkmark.
So when i close the Language screen and come back, the language checkmark goes the the default one even if the language, saved in shared preferences is French or Italian.
This is my Language screen:
class LanguagesScreen extends StatefulWidget {
const LanguagesScreen({Key? key}) : super(key: key);
#override
State<LanguagesScreen> createState() => _LanguagesScreenState();
}
class Item {
final String prefix;
final String? helper;
const Item({required this.prefix, this.helper});
}
var items = [
Item(prefix: 'English', helper: 'English',), //value: 'English'
Item(prefix: 'Français', helper: 'French'),
Item(prefix: 'Italiano', helper: 'Italian'),
];
class _LanguagesScreenState extends State<LanguagesScreen> {
var _selectedIndex = 0;
final _userPref = UserPreferences();
var _selecLangIndex;
int index = 0;
final List<String> entries = <String>['English', 'French', 'Italian'];*/
//init shared preferences
#override
void initState() {
super .initState();
_populateField();
}
void _populateField() async {
var prefSettings = await _userPref.getPrefSettings();
setState((){
_selecLangIndex = prefSettings.language;
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(...
),
body: CupertinoPageScaffold(
child: Container(
child: SingleChildScrollView(
child: CupertinoFormSection.insetGrouped(
children: [
...List.generate(items.length, (index) => GestureDetector(
onTap: () async {
setState(() => _selectedIndex = index);
if (index == 0){
await context.setLocale(Locale('en','US'));
_selecIndex = Language.English;
}
else if (index == 1){
await context.setLocale(Locale('fr','FR'));
_selecIndex = Language.French;
}
child: buildCupertinoFormRow(
items[index].prefix,
items[index].helper,
selected: _selectedIndex == index,
)
)),
TextButton(onPressed:
_saveSettings,
child: Text('save',
)
buildCupertinoFormRow(String prefix, String? helper, {bool selected = false,}) {
return CupertinoFormRow(
prefix: Text(prefix),
helper: helper != null
? Text(helper, style: Theme.of(context).textTheme.bodySmall,)
:null, child: selected ? const Icon(CupertinoIcons.check_mark,
color: Colors.blue, size: 20,) :Container(),
);
}
void _saveSettings() {
final newSettings = PrefSettings(language:_selecIndex);
_userPref.saveSettings(newSettings);
Navigator.pop(context);
}
}
this is the UserPreference:
class UserPreferences {
Future saveSettings(PrefSettings prefSettings) async {
final preferences = await SharedPreferences.getInstance();
await preferences.setInt('language' , prefSettings.language.index );
}
Future<PrefSettings> getPrefSettings() async {
final preferences = await SharedPreferences.getInstance();
final language = Language.values[preferences.getInt('language') ?? 0 ];
return PrefSettings(language: language);
}
}
enum Language { English, French, Italian}
class PrefSettings{
final Language language;
PrefSettings (
{required this.language});
}
I'm betting that the issue is in initState. You are calling _populateField, but it doesn't complete before building because it's an async method, and you can't await for it: so the widget gets build, loading the default position for the checkmark, and only after that _populateField completes...but then it's too late to show the saved data correctly.
In my experience, if I have not already instantiated a SharedPreferences object somewhere else in the code, I use this to load it:
class _LanguagesScreenState extends State<LanguagesScreen> {
[...]
#override
Widget build(BuildContext context) {
return FutureBuilder(
//you can put any async method here, just be
//sure that you use the type it returns later when using 'snapshot.data as T'
future: await SharedPreferences.getInstance(),
builder: (context, snapshot) {
//error handling
if (!snapshot.hasData || snapshot.connectionState != ConnectionState.done) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text(snapshot.error.toString()));
}
var prefs= snapshot.data as SharedPreferences;
//now you have all the preferences available without need to await for them
return Scaffold((
[...]
);
EDIT
I started writing another comment, but there are so many options here that there wasn't enough space.
First, the code I posted should go in your _LanguagesScreenState build method. The FutureBuilder I suggested should wrap anything that depends on the Future you must wait for to complete. I put it up at the root, above Scaffold, but you can move it down the widgets' tree as you need, just remember that everything that needs to read the preferences has to be inside the FutureBuilder.
Second, regarding SharedPreferences.getInstance(), there are two ways: the first is declaring it as a global variable, and loading it even in the main method where everything starts. By doing this you'll be able to reference it from anywhere in your code, just be careful to save the changes everytime is needed. The second is to load it everytime you need, but you'll end up using a FutureBuilder a lot. I don't know if any of these two options is better than the other: the first might have problems if somehow the SharedPreferences object gets lost, while the second requires quite more code to work.
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.
I am building an app using flutter and I'm using the flutter_bloc package for managing state. I have a simple Avatar widget used for displaying for displaying profile photo of a user.
/// A widget for displaying a user profile avatar.
class UserAvatar extends StatelessWidget {
/// The unique identifier for a particular user.
final int userId;
/// The size of the avatar.
final double radius;
UserAvatar({required this.userId, this.radius = 30}) {}
#override
Widget build(BuildContext context) {
context.read<UserInfoBloc>().add(UserInfoEvent.getUserInfo(userId));
return BlocBuilder<UserInfoBloc, UserInfoState>(
builder: (context, state) {
return state.when(initial: () {
return Text('Initial');
}, loading: () {
return Text('Loading');
}, loaded: (user) {
print(user);
return Container(
child: Avatar(
shape: AvatarShape.circle(radius),
loader: Center(
child: ClipOval(
child: Skeleton(
height: radius * 2,
width: radius * 2,
),
),
),
useCache: true,
sources: [NetworkSource(user.avatarUrl ?? '')],
name: user.firstName!.trim(),
onTap: () {
//TODO implement Navigation to profile page
},
),
);
}, error: () {
return Text('error');
});
},
);
}
}
My problem is that the widget will be used multiple times (when displaying a feed and there are contents by the same user). I initially just have the id of the user, then I try to make a network call and try to get the user. I've implemented some form of caching in my repository:
#LazySingleton(as: UserInfoRepository)
class UserInfoRepositoryImpl extends UserInfoRepository {
final GetUserInfoRemoteDataSource remoteDataSource;
final GetUserInfoLocalDataSource localDataSource;
UserInfoRepositoryImpl(
{required this.remoteDataSource, required this.localDataSource});
#override
Future<Either<Failure, User>> getUserInfo(int id) async {
try {
final existsInCache = localDataSource.containsUserModel(id);
if (existsInCache) {
return right(localDataSource.getUserModel(id));
} else {
final result = await remoteDataSource.getUserInfo(id);
localDataSource.cacheUserModel(result);
return right(result);
}
} on ServerExceptions catch (e) {
return left(e.when(() => Failure(),
requestCancelled: () => Failure.requestCancelled(),
unauthorisedRequest: () => Failure.unauthorisedRequest(),
badRequest: (e) => Failure.badRequest(e),
notFound: () => Failure.notFound(),
internalServerError: () => Failure.internalServerError(),
receiveTimeout: () => Failure.receiveTimeout(),
sendTimeout: () => Failure.sendTimeout(),
noInternetConnection: () => Failure.noInternetConnection()));
} on CacheException {
return left(Failure.cacheFailure());
}
}
}
Of course, I used the injectable package for dealing with my dependencies, and I've used the #LazySingleton annotation on the repository. But unfortunately, if I try to display two avatars of the same user, two separate network calls will be made. Of course, I don't want that. How can I solve this problem?
I have been learning flutter for 2-3 months now and I feel I have a reached a fundamental roadblock with understanding state management. This post will be long unfortunately so please bare with me and I hope I put the right detail.
Problem Definition
I have a list of widgets in a shopping cart,im at the point where I click minus and it only has 1 left the widget must be removed.No matter what I try I cant get that widget to be removed. If I click back button and go back into cart the Item will not appear anymore.
I have considered other methods, like disposing the widget(that didn't seem to work) and I was busy implementing Visibility Show/hide widgets in Flutter programmatically
but that doesn't feel like the right way.If my understanding of providers,changeNotifiers,async and future builders,is correct the below method should work and I think its fundamental to my flutter journey to understand why it doesn't work.
Overview:The idea was to use the minus button on CartItemWidget to call a method that updates Json stored on the local device, then repopulate the List cartProdList in ProductProvider which calls
notifyListeners() and then should propagate everywhere the provider is used. Now I have used this pattern successfully 5 times now, the only different this time is it will be removing a widget which I haven't done before. But this should work dynamically if the future is based of the same provider right ?
function call order
CartItemWidget.onPressed:()
calls >>>
ProductProvider.cartMinusOne(String id)
calls >>>
ProductProvider.Future<List<Product>> cartProducts()
well here goes the code.I also wouldn't mind comments on things I could be doing better in all areas.
CartWidget
class CartWidget extends StatefulWidget {
#override
_CartWidgetState createState() => _CartWidgetState();
}
class _CartWidgetState extends State<CartWidget> {
var providerOfProd;
ProductProvider cartProdProvider = new ProductProvider();
#override
void initState() {
_productsList = new ProductsList();
super.initState();
providerOfProd = Provider.of<ProductProvider>(context, listen: false).cartProducts();
}
#override
Widget build(BuildContext context) {
........
Column(children: <Widget>[
FutureBuilder(
future: providerOfProd,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Container(
width: 0,
height: 0,
);
case ConnectionState.done:
return ListView.separated(
..............
},
itemBuilder: (context, index) {
return CartItemWidget(
product: cartProdProvider.cartProdList.elementAt(index),
heroTag: 'cart',
quantity: cartProdProvider.cartProdList.elementAt(index).cartqty,
key: UniqueKey(),
);
},
);
.........
CartItemWidget
class CartItemWidget extends StatefulWidget {
CartItemWidget({Key key, this.product, this.heroTag, this.quantity = 1}) : super(key: key);
// ProductProvider cartProd = new ProductProvider();
String heroTag;
Product product;
int quantity;
#override
_CartItemWidgetState createState() => _CartItemWidgetState();
}
class _CartItemWidgetState extends State<CartItemWidget> {
#override
Widget build(BuildContext context) {
return Consumer<ProductProvider>(
builder: (context, productProv, _) => InkWell(
child: Container(
.............
child: Row(
children: <Widget>[
.............
IconButton(
onPressed: () {
setState(() {
productProv.cartMinusOne(widget.product.id);
widget.quantity = this.decrementQuantity(widget.quantity);
});
}
.............
ProductProvider
class ProductProvider with ChangeNotifier {
ProductProvider() {
cartProducts();
}
List<Product> cartProdList;
cartMinusOne(String id) async {
//Code to minus item,then return as a string to save as local jason
var test = jsonEncode(cartList);
saveLocalJson(test, 'cart.json');
cartProducts();
notifyListeners();
}
Future<List<Product>> cartProducts() async {
String jsonString = await JsonProvider().getProductJson();
String cartString = await getCartJson();
var filterProdList = (json.decode(jsonString) as List).map((i) => Product.fromJson(i)).toList();
//code to get match cart list to product list
cartProdList = filterProdList.where((element) => element.cartqty > 0).toList();
notifyListeners();
return cartProdList;
}
........................
I'm trying to understand how to store a List of Maps in Flutter, display it, and later add and subtract from it by index. I started to use jsonEncode/Decode to save the whole thing as a String but I don't think thats the right way and plus I can't add back to it since its a String and not a List<Map<String, dynamic>> anymore after Encoding. Any help is greatly appreciated.
class Favs extends StatefulWidget {
#override
_Favs createState() => _Favs();
}
class _Favs extends State<Favs> {
SharedPreferences sharedPreferences;
List<Map<String, dynamic>> _favList=[{id: 1, bookTxt: Here is my text., bookAuthor: Isaiah},{id: 2, bookTxt: Here is my text again., bookAuthor: Matt}];
List<dynamic> _newList = [];
#override
void initState(){
super.initState();
getSavedInfo();
}
getSavedInfo() async {
sharedPreferences = await SharedPreferences.getInstance();
var myFavList = sharedPreferences.getString('myFavList');
if (myFavList != null){
var myFavListCheck = jsonDecode(myFavList);
_newList = myFavListCheck;
}
}
_saveToList(List<Map<String, dynamic>> _favList) async {
var s = json.encode(_favList);
sharedPreferences = await SharedPreferences.getInstance();
sharedPreferences.setString('myFavList', s);
print('DONE WITH _saveToList');
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(title: Text('ListView Builder'),),
body: ListView.builder(
itemCount: _newList.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Container(
height: 80.0,
child: Center(
child: Text(_newList[index]['bookTxt'])
)
),
);
},
),
floatingActionButton: _addMoreButton(),
);
}
_addMoreButton(){
_favList.add({'id': '3','bookTxt': 'Here is 3rd text','bookAuthor': 'Johnny'});
_saveToList(_favList);
}
}
Decoding and encoding is the write way. Why dont you try flutter_secure_storage as a safer option?
On you pubspec, add: flutter_secure_storage: ^3.2.1+1 as a dependency.
Then you can use FlutterSecureStorage().write(key: key, value: value) which is async.
and to read just use encodedJson = FlutterSecureStorage().read(key: key) which is also async.
Also, you would have to use yourModel.fromJson(json.decode(encodedJson)), so make sure you also add import 'dart:convert';