How to get data from Firestore in Scoped Model - Flutter - firebase

I'm trying to get data from Firestore, in debug print the future does it job and list gets data and in debugPrint length is +, but when I try to get data in another Widget list recives null, in debugPrint length is 0 .
model.dart
class BBModel extends Model {
int _counter = 10;
int get counter => _counter;
var db = dbBB;
List<BB> _bbs;
List<BB> get bbs => _bbs;
Future<List<BB>> getBBs() async {
var snapshot = await db.getDocuments();
for (int i = 0; i < snapshot.documents.length; i++) {
_bbs.add(BB.fromSnapshot(snapshot.documents[i]));
print(bbs.length.toString()); //recives 23
}
notifyListeners();
return _bbs;
}
}
main.dart
void main() {
var model = BBModel();
model.getBBs();
runApp(ScopedModel<BBModel>(model: BBModel(), child: MyApp()));
}
statefullpage.dart
Expanded(
flex: 1,
child: Container(
height: 400.0,
child: ScopedModelDescendant<BBModel>(
builder: (context, child, model) {
return ListView.builder(
itemCount: model.bbs.length,
itemBuilder: (context, index) {
return Text(model.bbs[index].bbID);
});
}))),

Looks like the code you're written in main.dart is wrong. The instatiated model is different from the one you've sent in your ScopedModel.
Correction
Change model: model to model: BBModel() in your main.dart file.
void main() {
final model = BBModel();
model.getBBs();
runApp(ScopedModel<BBModel>(model: model, child: MyApp()));
}

In main.dart, I would try doing:
void main() {
var model = BBModel();
model.getBBs().then((someVariableName){
runApp(ScopedModel<BBModel>(model: BBModel(), child: MyApp()));
});
}
note: "someVariableName" will contain a List< BB>

To wait you can use the
await model.getBBs();
Apart from this however, I do not recommend uploading data to the main, as you would slow down the use of the app, as the data is getting bigger. Upload the data only to the pages you need and find a way to do this.

Related

Data is not being read from firebase Realtime Database using a Stream in Flutter

since Firebase was updated I have been having some issues, first it was because now, instead of a map, an Object? is returned and that is now fixed, but I can't seem to get any data from the database. I can put it there fine but the reading is not working.
This is on my firebase_utils .dart file
FirebaseDatabase data = FirebaseDatabase.instance;
DatabaseReference database = data.ref();
Future<void> init() async {
FirebaseAuth.instance.userChanges().listen((user) {
if (user != null) {
//_loginState = ApplicationLoginState.loggedIn;
} else {
//_loginState = ApplicationLoginState.loggedOut;
}
});
if (!kIsWeb) {
data.setPersistenceEnabled(true);
data.setPersistenceCacheSizeBytes(10000000);
}
}
I have this class:
class ReservationStreamPublisher {
Stream<List<Reservation>> getReservationStream() {
final stream = database.child('reservations').onValue;
final streamToPublish = stream
.map((event) {
List<Reservation> reservationList = [];
Map<String, dynamic>.from(event.snapshot.value as dynamic)
.forEach((key, value) => reservationList.add(Reservation.fromRTDB(value)));
print(reservationList);
return reservationList;
});
return streamToPublish;
}
}
Next is my Reservation.fromRTDB
factory Reservation.fromRTDB(Map<String, dynamic> data) {
return Reservation(
pin: data['pin'],
day: data['day'],
hour: data['hour'],
duration: data['duration'],
state: data['state'],
userEmail: data['client_email'],
completed: data['completed'],
id: data['id'],
dateMade: '',
timeMade: '');
}
And this is one of the places where I am supposed to show data
Text('Slots Reservados neste dia:'),
_selectedDate != null
? StreamBuilder(
stream:
ReservationStreamPublisher().getReservationStream(),
builder: (context, snapshot) {
final tilesList = <ListTile>[];
if (snapshot.hasData) {
List reservations =
snapshot.data as List<Reservation>;
int i = 0;
do {
if (reservations.isNotEmpty) {
if (reservations[i].day !=
(DateFormat('dd/MM/yyyy')
.format(_selectedDate!))) {
reservations.removeAt(i);
i = i;
} else
i++;
}
} while (i < reservations.length);
try {
tilesList
.addAll(reservations.map((nextReservation) {
return ListTile(
leading: Icon(Icons.lock_clock),
title: Text(
"Das ${nextReservation.hour} as ${nextReservation.duration}"),
);
}));
} catch (e) {
return Text(
'Ainda nao existem reservas neste dia');
}
}
// }
if (tilesList.isNotEmpty) {
return Expanded(
child: ListView(
children: tilesList,
),
);
}
return Text('Ainda nao existem reservas neste dia');
})
: SizedBox(),
I am not getting any error at the moment, but no data is returned.This is a Reservation example on my RTDB

Flutter: How to remove awaits to take advantage of Firebase Offline Persistence?

I'm using a drop-down list (DropDown), whose elements are obtained from Firebase. The form works right, however when the internet connection is lost the Firebase Offline Persistence property doesn't work and the CircularProgressIndicator stays active. Reading some responses such as Using Offline Persistence in Firestore in a Flutter App, it is indicated that awaits should not be handled, however it is not clear to me how to achieve it:
class EstanqueAlimentarPage extends StatefulWidget {
#override
_EstanqueAlimentarPageState createState() => _EstanqueAlimentarPageState();
}
class _EstanqueAlimentarPageState extends State<EstanqueAlimentarPage> {
final formKey = GlobalKey<FormState>();
AlimentoBloc alimentoBloc = new AlimentoBloc();
AlimentoModel _alimento = new AlimentoModel();
AlimentarModel alimentar = new AlimentarModel();
List<AlimentoModel> _alimentoList;
bool _alimentoDisponible = true;
#override
void dispose() {
alimentoBloc.dispose();
super.dispose();
}
#override
void initState() {
_obtenerListaAlimentoUnaVez();
super.initState();
}
Future<void> _obtenerListaAlimentoUnaVez() async {
_alimentoList = await alimentoBloc.cargarAlimento(idEmpresa); // Await that I want to eliminate
if (_alimentoList.length > 0) { // Here appears a BAD STATE error when the internet connection goes from off to on
_alimento = _alimentoList[0];
_alimentoDisponible = true;
} else {
_alimentoDisponible = false;
}
_cargando = false;
setState(() {});
}
#override
Widget build(BuildContext context) {
return Form(
key: formKey,
child: Column(
children: <Widget> [
_crearTipoAlimento(_alimentoList),
SizedBox(height: 8.0),
_crearComentarios(),
]
)
),
_crearBoton('Guardar'),
}
Widget _crearTipoAlimento(List<AlimentoModel> lista) {
return Container(
decoration: _cajaBlanca,
child:
!_cargando // If it isn't loading, Dropdown must be displayed
? DropdownButtonFormField<AlimentoModel>(
decoration: InputDecoration(
labelText: 'Nombre del Alimento',
contentPadding: EdgeInsets.only(top:5.0),
prefixIcon: Icon(FontAwesomeIcons.boxOpen, color: Theme.of(context).primaryColor,),
border: InputBorder.none,
),
value: _alimento,
items: lista.map((AlimentoModel value) {
return DropdownMenuItem<AlimentoModel>(
child: Text(value.nombre),
value: value,
);
}).toList(),
onChanged: (_alimentoDisponible) ? (AlimentoModel _alimentoSeleccionado) {
print(_alimentoSeleccionado.nombre);
_alimento = _alimentoSeleccionado;
setState(() {});
} : null,
disabledHint: Text('No hay Alimento en Bodega'),
onSaved: (value) {
alimentar.idAlimento = _alimento.idAlimento;
alimentar.nombreAlimento = _alimento.nombreRef;
}
)
: Center (child: CircularProgressIndicator(strokeWidth: 1.0,))
);
}
Widget _crearComentarios() {
return TextFormField(
// -- DESIGN OTHER FIELDS -- //
onSaved: (value) {
alimentar.comentarios = value;
}
),
);
}
Widget _crearBoton(String texto) {
return RaisedButton(
// -- DESIGN -- //
onPressed: (_guardando) ? null : _submit,
),
);
}
void _submit() {
// CODE TO WRITE FORM IN FIREBASE
}
}
The function code from my BLOC is:
Future<List<AlimentoModel>> cargarAlimento(String idEmpresa, [String filtro]) async {
final alimento = await _alimentoProvider.cargarAlimento(idEmpresa, filtro); //It's one await more
_alimentoController.sink.add(alimento);
return alimento;
}
And the Query from PROVIDER is:
Future<List<AlimentoModel>> cargarAlimento(String idEmpresa, [String filtro]) async {
Query resp;
final List<AlimentoModel> alimento = new List();
resp = db.child('empresas').child(idEmpresa).child('bodega/1').child('alimento')
.orderByChild('cantidad').startAt(0.000001);
return resp.once().then((snapshot) {
if (snapshot.value == null) return [];
if (snapshot.value['error'] != null) return [];
snapshot.value.forEach((id, alim){
final temp = AlimentoModel.fromJson(Map<String,dynamic>.from(alim));
temp.idAlimento = id;
alimento.add(temp);
});
return alimento;
});
When using Firebase offline, you omit the await only on things that change the server (e.g., creating or updating a record). So you won't wait for the server to say "yes I wrote it", you assume that it's written.
In your case, however, you are not writing data, you are reading data. You will have to keep await in your example. The way you load your data has orderByChild and startAt, maybe those are preventing offline loading. Normally, you get it if it's already in the cache: https://firebase.google.com/docs/firestore/manage-data/enable-offline#get_offline_data
You mention a BAD STATE error, maybe if you provide that, we may be able to pinpoint the issue a bit better.

Is there a way to save canvas drawing in flutter to firebase other than converting it to image

i am creating a flutter app where the user has a canvas which he can draw on and then he can save the canvas to Firestore and can recall and edit whenever he wants.I have seen tutorials in creating a canvas and drawing on it but i dont know how to save it to firebase, i have seen some say to convert it as an image and save it to firebase storage but after saving it as an image can the user recall and edit it , and is it possible to save all the points the user has drawn on canvas in the form of a list
Below is a code i am working on,
In this i am trying to save all the points in a list and update it to firebase
class _HomePageState extends State<HomePage> {
List<Offset> _points = <Offset>[];
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Container(
child: new GestureDetector(
onPanUpdate: (DragUpdateDetails details) {
setState(() {
RenderBox object = context.findRenderObject();
Offset _localPosition =
object.globalToLocal(details.globalPosition);
_points = new List.from(_points)..add(_localPosition);
});
},
onPanEnd: (DragEndDetails details) => _points.add(null),
child: new CustomPaint(
painter: new Signature(points: _points),
size: Size.infinite,
),
),
),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.save),
onPressed: () => Firestore.instance.collection('points').document('XcX3MbUWt3hiBJyiPMIO'
).updateData({'points':FieldValue.arrayUnion(_points)})
),
);
}
}
class Signature extends CustomPainter {
List<Offset> points;
Signature({this.points});
#override
void paint(Canvas canvas, Size size) {
Paint paint = new Paint()
..color = Colors.blue
..strokeCap = StrokeCap.round
..strokeWidth = 10.0;
for (int i = 0; i < points.length - 1; i++) {
if (points[i] != null && points[i + 1] != null) {
canvas.drawLine(points[i], points[i + 1], paint);
}
}
}
#override
bool shouldRepaint(Signature oldDelegate) => oldDelegate.points != points;
}
It gives an error
Unhandled Exception: Invalid argument: Instance of 'Offset'
Is this method of saving possible of so how to do
I am able to print the offsets of the canvas

How to convert Future<int> to int?

So I'm trying to display a pie chart using the fl_chart plugin. The data for the chart is being retrieved from firestore. I have this function that is used to display the data:
List<PieChartSectionData> showSection(AsyncSnapshot<QuerySnapshot> snapshot) {
return List.generate(length, (i) {
final isTouched = i == touchedIndex;
final double fontSize = isTouched ? 25 : 16;
final double radius = isTouched ? 60 : 50;
return PieChartSectionData(
color: Color(int.parse(cerealData[i].colorVal)),
value: cerealData[i].rating,
title: cerealData[i].rating.toString(),
radius: radius,
titleStyle: TextStyle(
fontSize: fontSize,
fontWeight: FontWeight.bold,
color: const Color(0xffffffff)),
);
});
}
The List.generate() takes an int as an argument. Since I'm displaying realtime data, I'm trying to get the number of documents present in my collection. For that, I have a function called getLength():
void getLength(AsyncSnapshot<QuerySnapshot> snapshot) async {
length = await Firestore.instance.collection('cereal').snapshots().length;
cerealData =
snapshot.data.documents.map((e) => Cereal.fromJson(e.data)).toList();
}
However, when I run the code, I get:
Another exception was thrown: type 'Future<int>' is not a subtype of type 'int'
The entire code:
class _FlChartPageState extends State<FlChartPage> {
int touchedIndex;
var length;
List<Cereal> cerealData;
void getLength(AsyncSnapshot<QuerySnapshot> snapshot) async {
length = await Firestore.instance.collection('cereal').snapshots().length;
cerealData =
snapshot.data.documents.map((e) => Cereal.fromJson(e.data)).toList();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('cereal').snapshots(),
builder: (context, snapshot) {
getLength(snapshot);
if (!snapshot.hasData)
return CircularProgressIndicator();
else {
return PieChart(PieChartData(
pieTouchData: PieTouchData(touchCallback: (pieTouchResponse) {
setState(() {
if (pieTouchResponse.touchInput is FlLongPressEnd ||
pieTouchResponse.touchInput is FlPanEnd) {
touchedIndex = -1;
} else {
touchedIndex = pieTouchResponse.touchedSectionIndex;
}
});
}),
borderData: FlBorderData(show: false),
sectionsSpace: 0,
centerSpaceRadius: 40,
sections: showSection(snapshot)));
}
}),
);
}
List<PieChartSectionData> showSection(AsyncSnapshot<QuerySnapshot> snapshot) {
return List.generate(length, (i) {
final isTouched = i == touchedIndex;
final double fontSize = isTouched ? 25 : 16;
final double radius = isTouched ? 60 : 50;
return PieChartSectionData(
color: Color(int.parse(cerealData[i].colorVal)),
value: cerealData[i].rating,
title: cerealData[i].rating.toString(),
radius: radius,
titleStyle: TextStyle(
fontSize: fontSize,
fontWeight: FontWeight.bold,
color: const Color(0xffffffff)),
);
});
}
}
I read somewhere that awaiting the future gets rid of the Future. But that doesn't work here.
How do I fix this?
Edit: It works if I simply pass the number of documents instead of length in List.generate(). But this won't work if there are changes to the collection. So how do I convert Future to int?
I think you aren't getting the length of the documents, you are getting the length of the snapshots if you want to get the documents length :
QuerySnapshot querySnapshot = await Firestore.instance.collection('cereal').getDocuments();
int length = querySnapshot.documents.length;
In get getLength function you are trying to get length which is actually async task which returns future and because of that you are getting following error.
Change your method with following metod
getLength()async{
Firestore.instance.collection('cereal').snapshots().length.then((len){
length = len;
cerealData =
snapshot.data.documents.map((e) => Cereal.fromJson(e.data)).toList();
});
}

Flutter: Only the first item in the Stack is dismissible

I am cracking my head over this one. I want all my items in my stack to be dismissible. I am able to show all my items but only the first item is dismissible. Why?
Here is the code snippet:
Widget build(BuildContext context) {
return new StreamBuilder <QuerySnapshot>(
stream: Firestore.instance.collection('records').document(uid).collection("pdrecords").snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
print("snapShot call");
final int recordCount = snapshot.data.documents.length;
print("got data. records: $recordCount");
if (recordCount >0) {
return new Stack (
alignment: listSlideAnimation.value,
children:
snapshot.data.documents.map((DocumentSnapshot document) {
count = count + multiplyFactor; print("count: $count");
index ++;
//print("data: ${document.data}");
return Dismissible(
resizeDuration: null,
dismissThresholds: _dismissThresholds(),
//background: new LeaveBehindView(),
key: ObjectKey(document.documentID) ,
onDismissed: (DismissDirection direction) {
direction == DismissDirection.endToStart
? print("favourite")
: print("remove");
// Do stuff
},
child:
new ListPDRecord(
id: document.documentID,
margin: listSlidePosition.value * count, //new EdgeInsets.only(bottom: 80.0) * count, //listSlidePosition.value * 5.5,
width: listTileWidth.value,
date: document["date"],
),
);
}).toList(),
);
} else {
return NoDataListData();
}
} else {
// No data
return new NoDataListData();
}
});
}
}
I suspect is the KEY and I have tried many variations of different keys such as manually increment of index, etc. but still, only the first item is dismissible.
Any pointers?
After many rounds of experiments, I got it resolved realising the following points:
You can generate a list of widgets inside a stack like the above but
The dismissible will probably won't work because each item inside the Stack has a different margin and the top one has the largest margin.
In the end, I have to change the Stack to a ListView and dismissible now works for every item.

Resources