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.
Related
I am doing a project where I have to keep an favorite icon and a selected fvrt list... Now using sqlflite .. I have done it.. when the user presses the favorite border icon it get changed to red color and the data saves in the favorite list.. when user pressses again in the same button .. the data gets delated from the list and the favorite button change to ist default color... but what i am not able to do is.. the favorite button is default false.. so even if the data is collected in the fvrt list .. all the fvrt button shows _fvrt default favorite btn when i start the app ...
i was wondering how can i check the data in the initState() , if the data already exit in database it fvrt btn will remain red..
here's a little code of the conditon that i haved used .
Widget _buildRow(String pair) {
final bool alreadySaved = _saved.contains(pair);
print("Already saved $alreadySaved");
print(pair);
return IconButton(
icon: new Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,
color:alreadySaved? Colors.red : Colors.white,
),onPressed: (){
setState(() {
if (alreadySaved) {
_saved.remove(pair);
_deleteEmployee(pair);
} else {
_saved.add(pair);
_insert(pair);
}
});
},
);
}
Reading data from your database is an async function - it takes some time. What you can do, is to create a loading state, and show a loading indicator, until the async function finishes.
import 'package:flutter/material.dart';
class MyClass extends StatefulWidget {
#override
_MyClassState createState() => _MyClassState();
}
class _MyClassState extends State<MyClass> {
bool isLoading = false;
List _saved = [];
#override
void initState() {
// Note that you cannot use `async await` in initState
isLoading = true;
_readFromDataBase().then((savedStuff) {
_saved = savedStuff;
isLoading = false;
});
super.initState();
}
#override
Widget build(BuildContext context) {
return !isLoading ? _buildRow("myPair") : CircularProgressIndicator();
}
Widget _buildRow(String pair) {
final bool alreadySaved = _saved.contains(pair);
print("Already saved $alreadySaved");
print(pair);
return IconButton(
icon: new Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,
color:alreadySaved? Colors.red : Colors.white,
),onPressed: (){
setState(() {
if (alreadySaved) {
_saved.remove(pair);
_deleteEmployee(pair);
} else {
_saved.add(pair);
_insert(pair);
}
});
},
);
}
}
Alternatively you can check the FutureBuilder Widget. Here is the official documentation: https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
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.
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
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();
});
}
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.