Update list of objects inside method Flutter / Dart - flutter

The code reads a firebase database then add each read in a list of objects (SalesData class), chartdata.
The problem is when I update the list (chartdata) in the function readData() I can print it inside databaseReference.once().then((DataSnapshot snapshot) {}, but when it out of this bracket it is always empty. I an wondering how I could use this list in the chart constructor "dataSource: "
P.S if I hot reload the chart builds fine!!
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
// próximo passo: colocar os valores lidos do firebase em uma lista que seja
class _HomeState extends State<Home> {
List<SalesData> chartdata = [];
#override
Widget build(BuildContext context) {
// here I try to update the list chartdata, in order to build the chart
readData();
return Scaffold(
appBar: AppBar(
title: Text('Home'),
),
body: Column(
children: <Widget>[
RaisedButton(
child: Text('Read Data'),
onPressed: () {
readData();
},
),
SfCartesianChart(
primaryXAxis: CategoryAxis(),
series: <LineSeries<SalesData, double>>[
LineSeries<SalesData, double>(
// Bind data source
dataSource: chartdata,
xValueMapper: (SalesData sales, _) => sales.year,
yValueMapper: (SalesData sales, _) => sales.sales)
])
],
));
}
final databaseReference =
FirebaseDatabase.instance.reference().child("UID/Esp_32");
void readData() {
databaseReference.once().then((DataSnapshot snapshot) {
Map<dynamic, dynamic> dados = snapshot.value;
dados.forEach((key, value) {
chartdata.add(SalesData(value['VoltageIn'], value['Sensor40']));
});
});
}
}
class SalesData {
SalesData(this.year, this.sales);
final double year;
final double sales;
}

First remove the method readData() at the beginning of the build method this could cause an infinite loop, you can call in the initState method.
So in your readData method you need to call setState to update the screen, like this:
void readData() {
databaseReference.once().then((DataSnapshot snapshot) {
Map<dynamic, dynamic> dados = snapshot.value;
dados.forEach((key, value) {
chartdata.add(SalesData(value['VoltageIn'], value['Sensor40']));
});
setState((){});
});
}
But I think that you should study some architecture to improve your code, like MVVM.

Related

how to display graph in flutter

Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
child: SfCartesianChart(
// Enables the legend
legend: Legend(isVisible: true),
// Initialize category axis
primaryXAxis: CategoryAxis(),
series: <ChartSeries>[
// Initialize line series
LineSeries<GraphModel, String>(
dataSource: [
// Bind data source
// retrieve data from database
GraphModel(
productName: ['prodName'], count: 'count'.length
),
],
xValueMapper: (GraphModel data, _) => data.productName,
yValueMapper: (GraphModel data, _) => data.count,
)
]
)
)
)
);
}
}
I want to retrieve data from firestore where I create GraphModel but i dont know to declare from firebase to input in the graph. Please help me how to declare this graph
need to import the cloud_firestore package to your pubspec.yaml file
Heres the full code to retrieve data from firestore:
import 'package:cloud_firestore/cloud_firestore.dart';
class GraphModel {
String productName;
int count;
GraphModel({this.productName, this.count});
}
class YourWidget extends StatefulWidget {
#override
_YourWidgetState createState() => _YourWidgetState();
}
class _YourWidgetState extends State<YourWidget> {
List<GraphModel> _graphData = [];
#override
void initState() {
super.initState();
_retrieveDataFromFirestore();
}
void _retrieveDataFromFirestore() async {
final firestore = FirebaseFirestore.instance;
final graphData = await firestore.collection('your_collection').get();
setState(() {
_graphData = graphData.docs.map((doc) => GraphModel(
productName: doc.data()['product_name'],
count: doc.data()['count'],
)).toList();
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
child: SfCartesianChart(
legend: Legend(isVisible: true),
primaryXAxis: CategoryAxis(),
series: <ChartSeries>[
LineSeries<GraphModel, String>(
dataSource: _graphData,
xValueMapper: (GraphModel data, _) => data.productName,
yValueMapper: (GraphModel data, _) => data.count,
),
],
),
),
),
);
}
}

using ChangeNotifier to add items into empty list

I already have data stored on firestore , but now i'd like to add this data into an empty list using ChangeNotifier , similar to this example -https://docs.flutter.dev/development/data-and-backend/state-mgmt/simple .
so below i'd like to add data stored on firebase into the _cart list , with the method I tried below I got the error The argument type 'List<Menu>' can't be assigned to the parameter type 'Menu'(would like to know why?) , it also contains the ui for where i'd like to map the data from cart list into the dialog sheet:
class AddList extends ConsumerWidget {
const AddList({Key? key}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
final menuAsync = ref.watch(menuProvider);
final model = ref.read(cartProvider);
return Scaffold(
appBar: AppBar(
title: const Text("menu"),
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.black,
onPressed: () async {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text("cart"),
content: Column(
children: const [
...model.cart.map((item) => Text(item.mealName)),
],
),
);
});
},
),
body: menuAsync.when(
data: (menu) => Column(
children: menu
.map(
(e) => Card(
child: ListTile(
title: Text(e.mealName),
subtitle: Text(e.price),
trailing: IconButton(
onPressed: () {
model.addProduct(menu);//where im getting error
},
icon: const Icon(Icons.add)))),
)
.toList(),
),
error: (e, s) => Center(child: Text("$e")),
loading: () => const Center(child: CircularProgressIndicator())),
);
}
}
below im using changenotifier to modify the cart list:
final cartProvider =
ChangeNotifierProvider<CartNotifier>((ref) => CartNotifier());
class CartNotifier extends ChangeNotifier {
final List<Menu> _cart = [];
List<Menu> get cart => _cart;
void addProduct(Menu menu) {
_cart.add(menu);
notifyListeners();
}
void removeProduct(Menu menu) {
_cart.remove(menu);
notifyListeners();
}
}
how im reading data from firestore :
final menuProvider = StreamProvider<List<Menu>>(
(ref) => ref.read(addMealRespositoryProvider).menuSearchStream);
Stream<List<Menu>> get menuSearchStream =>
_firestore.collection("menu").snapshots().map(
(event) => event.docs.map((e) => Menu.fromFirestore(e)).toList(),
);
snippet of my data model:
class Menu {
String mealName;
String price;
Menu({
required this.mealName,
required this.price,
});
Map<String, dynamic> toMap() {
return {
"mealName": mealName,
"price": price, },
factory Menu.fromFirestore(DocumentSnapshot doc) {
final map = doc.data() as Map<String, dynamic>;
return Menu(
mealName: map["mealName"] ?? '',
price: map["price"] ?? '',
);
}
}

Flutter: Stream data from rest api using Cubit

The idea is to fetch data from api continuously and show this data in screen, without need to fetch manually, like that if someone else perform a change on server data this change will be shown without user refreshing action.
The implementation is done in Flutter BLoC (cubit).
I have already get a console print of data in the cubit but I can't get same data in the BlocBuilder neither in the BlocListener.
My code is:
//data_repository.dart
import 'dart:async';
import 'data.dart';
class DataRepository {
final DataApi dataApi;
List<Map<String, dynamic>> formatedData = [];
final _controller = StreamController<List<Map<String, dynamic>>>();
DataRepository(this.dataApi) {
getData();
}
Future<void> getData() async {
Timer.periodic(Duration(seconds: 5), (timer) async {
formatedData.clear();
Map<String, dynamic> res = await dataApi.getData();
List<dynamic> data = res['data'];
for (var el in data) {
formatedData.add({'id': el['id'], 'name': el['name']});
}
_controller.add(formatedData);
});
}
#override
Stream<List<Map<String, dynamic>>> data() async* {
yield* _controller.stream;
}
#override
void dispose() => _controller.close();
}
Blockquote
This code is Data Repository it get data and make it available via a StreamController named "_controller";
Here data is got and controller is working perfectly.
My Cubit State is like this:
//data_list_state.dart
class DataListState extends Equatable {
final List<Map<String, dynamic>> data;
DataListState(this.data);
#override
List<Object> get props => [data];
DataListState copyWith({
List<Map<String, dynamic>>? data,
}) {
return DataListState(
data ?? this.data,
);
}
}
When I print within copyWith() I get updated data;
My Cubit code:
//data_list_cubit.dart
class DataListCubit extends Cubit<DataListState> {
DataListCubit(this.dataRepository) : super(DataListState([])) {
loadList();
}
final DataRepository dataRepository;
loadList() {
dataRepository.data().listen((event) {
if (event.isNotEmpty) {
emit(state.copyWith(data: dataRepository.formatedData));
}
});
}
}
When I print in loadList() I get the updated data;
My Screen Code:
//home.dart
class Home extends StatelessWidget {
const Home({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(children: [
BlocListener<DataListCubit, DataListState>(
listener: (context, state) {
if (state.data.isNotEmpty) print(state.data[0].toString());
},
child: BlocBuilder<DataListCubit, DataListState>(
builder: (context, state) {
if (state.data.isNotEmpty) print(state.data[0].toString());
return ListView(
shrinkWrap: true,
children: [
for (Map<String, dynamic> ff in state.data)
ListTile(
title: Text(ff['name']),
leading: Text(ff['id'].toString()),
),
],
);
},
),
),
]),
);
}
}
When I console print here I don't get data just for the first time, and after every 5 secondes (described in my getData()) I get updated data in all my codes excepting in the home.
Can you tell me if my cubit implementation is right ?
What should I do to make it work ?
Thanks in advance
You must add the BlocProvider above the BlocListener or BlocBuilder in HomePage.
tip: Learn BlocListener vs BlocBuilder vs BlocConsumer. All three must be enclosed in BlocProvider to work
class MyHomePage extends StatelessWidget {
DataRepository dataRepository = DataRepository();
#override
Widget build(BuildContext context) {
return SafeArea(
child: BlocProvider<DataListCubit>(
create: (context) => DataListCubit(dataRepository),
child: Scaffold(
body: BlocBuilder<DataListCubit, DataListState>(
builder: (context, state) {
print(state.data);
if (state.data.isNotEmpty) {
return ListView(
shrinkWrap: true,
children: [
for (Map<String, dynamic> ff in state.data)
ListTile(
title: Text(ff['name']),
leading: Text(ff['id'].toString()),
),
],
);
} else {
return const Center(
child: Text('No Data'),
);
}
},
),
),
),
);
}
}
Your Cubit State should be like below as you don't require
Equitable for state management in Cubit implementation
class DataListState {
const DataListState(this.data);
final List<Map<String, dynamic>> data;
DataListState copyWith({
List<Map<String, dynamic>>? data,
}) {
return DataListState(
data ?? this.data,
);
}
}
Full code
In absence of DataApi, I have structured the whole working code with random data as in below :
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'dart:math';
import 'package:flutter_bloc/flutter_bloc.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Demo',
theme: ThemeData(),
home: MyHomePage(),
// home: VibrateHomepage(),
);
}
}
class MyHomePage extends StatelessWidget {
DataRepository dataRepository = DataRepository();
#override
Widget build(BuildContext context) {
return SafeArea(
child: BlocProvider<DataListCubit>(
create: (context) => DataListCubit(dataRepository),
child: Scaffold(
body: BlocBuilder<DataListCubit, DataListState>(
builder: (context, state) {
print(state.data);
if (state.data.isNotEmpty) {
return ListView(
shrinkWrap: true,
children: [
for (Map<String, dynamic> ff in state.data)
ListTile(
title: Text(ff['name']),
leading: Text(ff['id'].toString()),
),
],
);
} else {
return const Center(
child: Text('No Data'),
);
}
},
),
),
),
);
}
}
class DataRepository {
DataRepository() {
getData();
}
DataApi dataApi = DataApi();
List<Map<String, dynamic>> formattedData = [];
final _controller = StreamController<List<Map<String, dynamic>>>();
Future<void> getData() async {
Timer.periodic(const Duration(seconds: 5), (timer) async {
Map<String, dynamic> el = dataApi.getNew();
formattedData.add({'id': el['id'], 'name': el['name']});
_controller.add(formattedData);
});
}
Stream<List<Map<String, dynamic>>> data() async* {
yield* _controller.stream;
}
void dispose() => _controller.close();
}
class DataApi {
var rng = Random();
getNew() {
var rnd = rng.nextInt(100);
return {
"id": rnd,
"name": "Person " + rnd.toString()
};
}
}
class DataListState {
const DataListState(this.data);
final List<Map<String, dynamic>> data;
DataListState copyWith({
List<Map<String, dynamic>>? data,
}) {
return DataListState(
data ?? this.data,
);
}
}
class DataListCubit extends Cubit<DataListState> {
DataListCubit(this.dataRepository) : super(DataListState([])) {
loadList();
}
final DataRepository dataRepository;
loadList() {
dataRepository.data().listen((event) {
if (event.isNotEmpty) {
emit(state.copyWith(data: dataRepository.formattedData));
}
});
}
}

Error: Expected 0 type arguments after call

I created a list of graphs where I define the data in the Sales template but whenever I call it shows an error ( Declared with 0 type parameter ). I added the data but it doesn't show
Error: Expected 0 type arguments.
ErrorLine: new Expanded(child: new charts.BarChart(_chartdata)),
import 'package:flutter/material.dart';
import 'dart:math';
import 'package:charts_flutter/flutter.dart' as charts;
void main() {
runApp(MaterialApp(
home: MyApp(),
));
}
class MyApp extends StatefulWidget {
#override
_State createState() => _State();
}
class Sales {
final String year;
final int sales;
Sales(this.year, this.sales);
}
class _State extends State<MyApp> {
List<Sales> _data = [];
List<charts.Series<Sales, String>> _chartdata = [];
void _makeData() {
// _data = new List<Sales>();
// _chartdata = new List<charts.Series<Sales, String>>();
final rnd = Random();
for(int i = 2010; i < 2019; i++) {
_data.add(new Sales(i.toString(), rnd.nextInt(1000)));
}
_chartdata.add(new charts.Series(
id: 'Sales',
colorFn: (_,__) => charts.MaterialPalette.red.shadeDefault, //Old version
//colorFn: (Sales sales,__) => charts.MaterialPalette.red.shadeDefault,
data: _data,
domainFn: (Sales sales, _) => sales.year,
measureFn: (Sales sales, _) => sales.sales,
fillPatternFn: (_,__) => charts.FillPatternType.solid, // Old version
//fillPatternFn: (Sales sales,__) => charts.FillPatternType.solid,
displayName: 'sales'
)
);
}
#override
void initState() {
_makeData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Layouts'),
backgroundColor: Colors.deepPurpleAccent,
),
body: Container(
padding: const EdgeInsets.all(32.0),
child: Center(
child: Column(
children: <Widget>[
Text('Sales'),
//new Expanded(child: new charts.BarChart<Sales>(_chartdata))
//new Expanded(child: new charts.BarChart<Sales>(_chartdata))
new Expanded(child: new charts.BarChart<Sales>(_chartdata)),
],
),
)
),
);
}
}
the error is shown in <Sales> in last.
Please help me out I don't know how to fix it.

How to show line chart from data json using flutter

i will display linechart from the database using datajson with flutter. but the reference that I can use is hardcode data. Please help
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:charts_flutter/flutter.dart' as charts;
int dataTotal = 0;
class GrafikSpbj extends StatefulWidget {
final List<charts.Series> seriesList;
final bool animate;
GrafikSpbj(this.seriesList, {this.animate});
/// Creates a [LineChart] with sample data and no transition.
factory GrafikSpbj.withSampleData() {
return new GrafikSpbj(
_createSampleData(),
// Disable animations for image tests.
animate: false,
);
}
#override
_GrafikSpbjState createState() => _GrafikSpbjState();
/// Create one series with sample hard coded data.
static List<charts.Series<LinearSales, int>> _createSampleData() {
print(dataTotal);
final desktopSalesData = [
new LinearSales(0, 10),
new LinearSales(1, 50),
new LinearSales(2, 70),
new LinearSales(3, 100),
];
final tableSalesData = [
new LinearSales(0, 20),
new LinearSales(1, 40),
new LinearSales(2, 80),
new LinearSales(3, 100),
];
return [
new charts.Series<LinearSales, int>(
id: 'Desktop',
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
domainFn: (LinearSales sales, _) => sales.year,
measureFn: (LinearSales sales, _) => sales.sales,
data: desktopSalesData,
),
new charts.Series<LinearSales, int>(
id: 'Tablet',
colorFn: (_, __) => charts.MaterialPalette.red.shadeDefault,
domainFn: (LinearSales sales, _) => sales.year,
measureFn: (LinearSales sales, _) => sales.sales,
data: tableSalesData,
),
];
}
}
class _GrafikSpbjState extends State<GrafikSpbj> {
List data;
Timer timer;
int jumlahData = 0;
Future<List> _getData() async {
final response = await http.get("xxxxxxxxxxxxx");
setState(() {
data = json.decode(response.body);
jumlahData =data.length;
print(data[0]['id_spbj']);
print(data[0]['minggu_ke']);
print(data[0]['hasil']);
print(data[0]['target_grafik']);
});
return json.decode(response.body);
}
// makeRequest() async {
// var response = await http.get(
// 'xxxxxxxxxxxxxxxxxxxxxxx',
// headers: {'Accept': 'application/json'},
// );
// setState(() {
// data = json.decode(response.body);
// jumlahData =data.length;
// });
// }
#override
void initState() {
_getData();
dataTotal = jumlahData;
super.initState();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: new Text("Input Pelanggan hal 2"),
),
body: Column(
children: <Widget>[
SizedBox(
width: 600.0,
height: 250.0,
child: new charts.NumericComboChart(widget.seriesList,
animate: widget.animate,
defaultRenderer: new charts.LineRendererConfig(),
customSeriesRenderers: [
new charts.PointRendererConfig(customRendererId: 'customPoint')
]),
),
new Text("data"),
new Text("data"),
new Text("data")
],
),
);
}
}
/// Sample linear data type.
class LinearSales {
final int year;
final int sales;
LinearSales(this.year, this.sales);
}
values
[{"id_spbj":"1","minggu_ke":"1","hasil":"13.4353337","target_grafik":"25.00"},{"id_spbj":"1","minggu_ke":"2","hasil":"28.2629147","target_grafik":"25.00"},{"id_spbj":"1","minggu_ke":"3","hasil":"85.3285762","target_grafik":"25.00"},{"id_spbj":"2","minggu_ke":"1","hasil":"32.0184122","target_grafik":"25.00"},{"id_spbj":"2","minggu_ke":"2","hasil":"53.5296934","target_grafik":"25.00"}]
You can use FL_chart plugin library in flutter, where you draw all types of charts.
please find guideline to use FL_Chart