This question already has an answer here:
Firestore - unexpected reads
(1 answer)
Closed 2 years ago.
I'm working on a project for a logistics warehouse.
The idea is that I use Cloud Firestore for store pallets in a rack and that I can find it back in the system.
I can see my pallets in the app and it works well.
Cloud Firestore has a read amount of 50.000 reads per day.
If I start up the app it calculates the number of documents in the list and I can see it in the usage tab.
That's okay but even if I do nothing in the app, the number increases.
Is there a solution to read the data only if the flutter page opens?
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class overzicht extends StatefulWidget {
#override
_overzichtState createState() => _overzichtState();
}
class _overzichtState extends State<overzicht> {
Future _data;
Future getPosts() async{
var firestore = Firestore.instance;
QuerySnapshot qn = await firestore.collection("locaties").getDocuments();
return qn.documents;
}
#override
void initState() {
super.initState();
_data = getPosts();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Center(child: Text('Overzicht')),backgroundColor: Color.fromRGBO(17, 32, 42, 1),),
body: Container(
child: FutureBuilder(
future: _data,
builder: (_, snapshot) {
if(snapshot.connectionState == ConnectionState.waiting){
return Center(
child: Text('Loading...'),
);
} else{
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (_, index){
return ListTile(
title: Text(snapshot.data[index].data["location"]),
);
});
}
}),
),
);
}
}
This has been answered before here:
If you leave the console open on a collection/document with busy write activity, the console will automatically read the changes that update the console's display. This is very often the source of unexpected reads.
Related
I'm using ApolloClient 3 the GitHub GraphQL API to retrieve all releases from a repo.
This is what the query looks like:
query ($owner: String!, $name: String!, $first: Int, $after: String, $before: String) {
repository(owner: $owner, name: $name) {
id
releases(orderBy: {field: CREATED_AT, direction: DESC}, first: $first, after: $after, before: $before) {
nodes {
name
publishedAt
resourcePath
tagName
url
id
isPrerelease
description
descriptionHTML
}
totalCount
pageInfo {
endCursor
hasNextPage
hasPreviousPage
startCursor
}
}
}
}
This is what the result payload looks like:
This returns me the first x entries (nodes). So far, all good.
I need to implement pagination and I make use of the fetchMore function provided by ApolloClient useQuery. Calling fetchMore fetches the next x entries successfully but these are not displayed in my component list.
According to the ApolloClient Pagination documentation, it seems necessary to handle the merging of the fetchMore results with the ApolloClient caching mechanism. The documentation is understandable for simple situations but I am struggling to implement a solution for the situation where the actual array of results that needs to be merged togeher is deeply nested in the query result (repository -> releases -> nodes).
This is my implementation of the InMemoryCache options merge:
const inMemoryCacheOptions = {
addTypename: true,
typePolicies: {
ReleaseConnection: {
fields: {
nodes: {
merge(existing, incoming, options) {
const previous = existing || []
const results = [...previous, ...incoming]
return results
}
}
}
},
}
}
The results array here contains the full list, including the existing entries and the new x entries. This is essentially the correct result. However, my component list which is using the useQuery and fetchMore functionality does not get the new entries after the fetchMore is called.
I have tried various combinations in the inMemoryCacheOptions code above but so far I have been unsuccessful.
To add more context, this is the related component code:
export default function Releases() {
const { loading, error, data, fetchMore } = useQuery(releasesQuery, {
variables: {
owner: "theowner",
name: "myrepo",
first: 15
}
});
if (loading) return null;
if (error) {
console.error(error);
return null;
}
if (data) {
console.log(data?.repository?.releases?.pageInfo?.endCursor);
}
const handleFetchMore = () => {
fetchMore({
variables: {
first: 15,
after: data?.repository?.releases?.pageInfo?.endCursor
}
});
};
return (
<div>
<ul>
{data?.repository?.releases?.nodes?.map(release => (
<li key={release.id}>{release.name}</li>
))}
</ul>
<button onClick={handleFetchMore}>Fetch More</button>
</div>
);
}
After fetchMore the component doesn't rerender with the new data.
If anyone has any other ideas that I could try, I'd be grateful.
I finally managed to solve this. There was no change to the react component code but the InMemoryCacheOptions now looks like this:
const inMemoryCacheOptions = {
addTypename: true,
typePolicies: {
Repository: {
fields: {
releases: {
keyArgs: false,
merge(existing, incoming) {
if (!incoming) return existing;
if (!existing) return incoming;
const { nodes, ...rest } = incoming;
// We only need to merge the nodes array.
// The rest of the fields (pagination) should always be overwritten by incoming
let result = rest;
result.nodes = [...existing.nodes, ...nodes];
return result;
}
}
}
}
}
};
The main change from my original code is that I now define the typePolicy for the releases field of the Repository type. Previously I was trying to get directly to the nodes field of the Release type. Since my Repository type the root of the gql query and used in the component, it now reads the merged results from the cache.
If I specified the typePolicy for Query as mentioned in the docs, I would not be able to specify the merge behaviour for the releases field because it would be one level too deep (i.e. Query -> repository -> releases). This is what lead to my confusion in the beginning.
I'm working with a collection which documents looks like these
Visit {
point: document-ref
}
the point contains the lat and long for the point.
As you can see, I'm retrieving the document for each visit. But I'm also having a huge problem: I'm rocketing my cloud usage.
Is there a way to do this without getting my usage up in the sky?
I'm using react with with react-redux-firebase
useSelector(({ firestore: { ordered: { visitas } } }) => {
return (
visitas &&
Promise.all(
visitas.map(async v => {
return { ...v, punto: (await v.puntoId.get()).data() };
})
).then(v => {setVisitas(v)})
);
});
I'm looking for a way to retrieve the documents, with the point already loaded in it.
I was trying to implement a checkbox that would disable and enable a button. But I was blocked by this following error:
I/flutter (19999): The following assertion was thrown building DisclaimerPage(dirty, state: DisclaimerPageState#24b3d):
I/flutter (19999): setState() or markNeedsBuild() called during build.
I/flutter (19999): This Overlay widget cannot be marked as needing to build because the framework is already in the
I/flutter (19999): process of building widgets. A widget can be marked as needing to be built during the build phase
I/flutter (19999): only if one of its ancestors is currently building. This exception is allowed because the framework
I/flutter (19999): builds parent widgets before children, which means a dirty descendant will always be built.
I/flutter (19999): Otherwise, the framework might not visit this widget during this build phase.
Here is my code for building the page:
class DisclaimerPageState extends State<DisclaimerPage> {
bool checkBoxValue = false;
#override
Widget build(BuildContext context) {
var _floatButtonOnPressed;
if (checkBoxValue) {
_floatButtonOnPressed = navToHome();
}
return new Scaffold(
appBar: AppBar(
title: Text("Disclaimer"),
leading: Container(),
),
body: Container(
child: Card(
child: Column(
children: <Widget>[
Text("ADHJASDKHKDAD"),
Row(
children: <Widget>[
Checkbox(
value: checkBoxValue,
onChanged: (bool newValue) {
setState(() {
checkBoxValue = newValue;
});
}),
Text("I understand"),
],
),
],
),
),
),
floatingActionButton: FloatingActionButton.extended(
onPressed: _floatButtonOnPressed,
label: Text("PROCEED"),
icon: Container(),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
navToHome() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ImmunityHomePage(title: 'ImMUnity')),
);
}
}
Am I doing something wrong? My checkbox implementation is based on the documentation of checkbox.dart. So I am a little confused here. Help is appreciated. Thank you so much!
Instead of checking checkBoxValue & setting onPressed, you should check checkBoxValue inside your on press function then manage navigation.
SO first remove this
if (checkBoxValue) {
_floatButtonOnPressed = navToHome();
}
Then update onPressed of floating action button as
onPressed: (){navToHome();},
Then update you navToHome() function
navtoHome(){
if(checkValue){
//your navigation code here
}
}
I'm trying to make something similar to google/apple maps screen in flutter. I just started experimenting in Flutter and I have a hard time understand that "Draggable widget". Could someone give me example code how they made their slide-up view, I can learn from? I can't find any.
There's also the sliding_up_panel Flutter library you could use to implement the same sort of design that Google/Apple Maps uses.
Draggable (and DragTarget) is not used for what you call slide-up view
slide-up view, from the android world, is for bottom aligned modals.
Draggable is to transfer data using Drag&Drop.
In flutter, bottom modals are fairly simple :
First, make sure you have somewhere above in your tree a Scaffold. As it's what will position everything together.
Then, call either showBottomSheet or showModalBottomSheet with whatever content you like. The content will now automatically appear at the bottom of your screen.
That's it, your job is done ! You can optionally now add a custom close event. For this, you'll just have to call Navigator.pop(context).
But both modal and non-modal bottom sheet can be closed out of the box using common gestures. Such as back button, navbar back, of click outside.
Full example :
class MyExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(title: new Text('Example')),
body: new Center(
child: new Builder(builder: (context) {
return new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new RaisedButton(
onPressed: () => modal(context),
child: new Text("modal"),
),
new RaisedButton(
onPressed: () => showSlideupView(context),
child: new Text("non modal"),
),
],
);
}),
),
),
);
}
void showSlideupView(BuildContext context) {
showBottomSheet(
context: context,
builder: (context) {
return new Container(
child: new GestureDetector(
onTap: () => Navigator.pop(context),
child: new Text("Click me to close this non-modal bottom sheet"),
),
);
});
}
modal(BuildContext context) {
showModalBottomSheet(
context: context,
builder: (context) {
return new Container(
child: new Text("This is a modal bottom sheet !"),
);
});
}
}
Now you can use Sliding Up Panel plugin to do that, its awesome.
As an alternative: From the docs https://api.flutter.dev/flutter/material/showModalBottomSheet.html
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
child: const Text('showModalBottomSheet'),
onPressed: () {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return Container(
height: 200,
color: Colors.amber,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('Modal BottomSheet'),
ElevatedButton(
child: const Text('Close BottomSheet'),
onPressed: () => Navigator.pop(context),
)
],
),
),
);
},
);
},
),
);
}
I have a query which gets me a list of notes and a subscription which listens and inserts new notes by altering the query. However the problem is the first note doesn't get added.
So let me add more detail, initially the query response with an object which contains an attribute called notes which is an array of 0 length, if we try and add a note the attribute gets removed. The note is created so if I refresh my application the query will return the note then If I try and add a note again the note gets added to the array in the query object.
Here is my notes container where I query for notes and create a new property to subscribe to more notes.
export const NotesDataContainer = component => graphql(NotesQuery,{
name: 'notes',
props: props => {
console.log(props); // props.notes.notes is undefined on first note added when none exists.
return {
...props,
subscribeToNewNotes: () => {
return props.notes.subscribeToMore({
document: NotesAddedSubscription,
updateQuery: (prevRes, { subscriptionData }) => {
if (!subscriptionData.data.noteAdded) return prevRes;
return update(prevRes, {
notes: { $unshift: [subscriptionData.data.noteAdded] }
});
},
})
}
}
}
})(component);
Any help would be great, thanks.
EDIT:
export const NotesQuery = gql`
query NotesQuery {
notes {
_id
title
desc
shared
favourited
}
}
`;
export const NotesAddedSubscription = gql`
subscription onNoteAdded {
noteAdded {
_id
title
desc
}
}
`;
Another EDIT
class NotesPageUI extends Component {
constructor(props) {
super(props);
this.newNotesSubscription = null;
}
componentWillMount() {
if (!this.newNotesSubscription) {
this.newNotesSubscription = this.props.subscribeToNewNotes();
}
}
render() {
return (
<div>
<NoteCreation onEnterRequest={this.props.createNote} />
<NotesList
notes={ this.props.notes.notes }
deleteNoteRequest={ id => this.props.deleteNote(id) }
favouriteNoteRequest={ this.props.favouriteNote }
/>
</div>
)
}
}
Another edit:
https://github.com/jakelacey2012/react-apollo-subscription-problem
YAY got it to work, simply the new data sent down the wire needs to be the same shape as the original query.
e.g.
NotesQuery had this shape...
query NotesQuery {
notes {
_id
title
desc
shared
favourited
}
}
yet the data coming down the wire on the subscription had this shape.
subscription onNoteAdded {
noteAdded {
_id
title
desc
}
}
notice shared & favourited are missing from the query on the subscription. If we added them it would now work.
This is the problem, react-apollo internally detects a difference and then doesn't add the data I guess It would be useful if there was a little more feed back.
I'm going to try and work with the react-apollo guys to see if we can put something like that in place.
https://github.com/apollographql/react-apollo/issues/649