Flutter state management issue - firebase

My problem is related to the provider pattern/library.
I have created some state classes as follows (ive replaced content/var names to try and keep it simple)
class State1 with ChangeNotifier{
String _s;
set s(String newS){
_s = newS;
notifyListeners();
}
}
and then I use a multiprovider to pass it (create the objects in the widget init which acts as parent to everything in the app.)
child: MultiProvider(
providers: [
ChangeNotifierProvider(builder: (context) => state1),
],
child: Stack(
alignment: Alignment.center,
children: stackChildren,
),
Which is then accessed in the children widgets build method using
state1Var = Provider.of<State1>(context);
And all of this works fine..
My issue is when I get to using a navigation push I can no longer access the state.
onPressed: (() {
Navigator.push(
contextField,
MaterialPageRoute(builder: (context) => NewPage()),
);
}),
As i get this error
Could not find the correct Provider<State1> above this NewPage Widget...
I did manage to get it using this
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Consumer(builder: (),child: NewPage(),)),
);
But when I popped the widget using navigator.pop() the state couldnt be used as it said it had been disposed. Am i doing this incorrectly or is there something i am missng?
Sorry if i've made this complicated. I had to remove a lot of code.
Thanks for your help :)

Probably you have a context hierarchy problem. Wrap your MaterialApp with MultiProvider or your Provider must be on top of the Route you are getting the provider within.
Otherwise, you can wrap your Scaffold with Consumer widget. What Consumer does is, simply it's a builder for establishing a connection between your Provider and Route by creating yet another context and lets you to obtain Provider class via InheritedWidget.

Related

Animation in Streambuilder to display Firebase Content

I want to add some animation upon addition or removal of an object (that is constructed using streambuilder by reading data from my firebase database). How the progression works is
User adds task => Task gets added to firebase =>Streambuilder detects change and rebuilds UI (jaggedly due to no animation) => User removes task => Task gets removed from firebase => Streambuilder detects change and rebuilds UI (Again jaggedly as their is no animation)
I want to add animation at the two points where streambuilder is rebuilding the UI and adding the Todo task. Is there any way to do this?
I have researched about the package animated_stream_list but really could'nt understand how I could incorporate it into my code. Any insight about that would be wonderful.
Code:
StreamBuilder(
stream: FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('UserTasks')
.orderBy("Timestamp", descending: true)
.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
return ListView(
children: snapshot.data.docs.map((document) {
//Container that containes text (Make function later on) START
return GestureDetector(
onDoubleTap: () {
//Deleting task when user taps on it
deletetask("${document.data()['Todo']}");
},
child: AnimatedContainer(
// UI elements .....
child: Container(
child: Text(
"${document.data()['Todo']}")
)
.....
), //Animated container end
); //Gesture Detector end
}).toList(),
); //Listview
},
), //StreamBuilder
AnimatedSwitcher might help here. It animates between widgets automatically whenever the child changes. In your case, inside stream builder, wrap your widget with animated switcher. Check this for more information. Animated Switcher
There is a good video explanation of how to use it in the page. For more info, refer to the documentation.
try using AnimatedList.
use snapshot.data.docChanges to detect changes and
call insertItem or removeItem based on docChange.type
maybe something like this
snapshot.data.docChanges.forEach((docChange) {
if (docChange.type == DocumentChangeType.added) {
AnimatedListState.insertItem();
} else if (docChange.type == DocumentChangeType.removed) {
AnimatedListState.removeItem();
}
});
When anything updates then StreamBuilder rebuilds the widget automatically. Internally, StreamBuilder calls State.setState. So, according to me, if you want to add an animation when the item is deleted, you have to add it on the Gesture detection. In your code snippet, you had added on GestureDetection that will remove the item on double click. So, you can do one thing. When the user double clicks, play the animation for some seconds and then call the function to delete the item. After that, StreamBuilde will rebuild itself and displays the updated content.
Hope this will solve your use case. If you have any problem further, please comment below.

How to avoid re-fetching user info from Firebase with Flutter

As soon as the user logs in my app redirects user to homepage and the homepage has a bottomNavigationBar and a AppBar
In this AppBar I display the user's avatar and the appname. To show this I use:
Stack(
children: [
FutureBuilder(
future: getAvatar(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return ClipRRect(
borderRadius: BorderRadius.circular(25.0),
child: Image.asset(
'assets/default_avatar.png',
fit: BoxFit.cover,
),
);
}
if (snapshot.connectionState == ConnectionState.done) {
return Center(
child: SizedBox(
height: 45.0,
child: ClipRRect(
borderRadius: BorderRadius.circular(25.0),
child: FadeInImage.assetNetwork(
placeholder: 'assets/default_avatar.png',
image: snapshot.data['avatar'],
fit: BoxFit.cover,
),
),
),
);
}
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
},
),
],
It works great, but I do not find it is good practice... and every time I change page from the bottom navigation bar the avatar "reloads" (you see the CircularProgressIndicator). doesn't help that it is a stateful class
So my solution to stop this from happening is to save user data somewhere on the phone and just grab that information whenever I need it. BUT HOW
FYI, it is not only the avatar, I will display username and useremail. If anyone has an example that they can redirect me to, that would be great!
Thank you!
You're incorrectly using FutureBuilder.
The docs state
The future must have been obtained earlier, e.g. during State.initState, State.didUpdateWidget, or State.didChangeDependencies. It must not be created during the State.build or StatelessWidget.build method call when constructing the FutureBuilder. If the future is created at the same time as the FutureBuilder, then every time the FutureBuilder's parent is rebuilt, the asynchronous task will be restarted.
You're clearly not doing that here as you are obtaining the Future during build, making it expected that any time that build is called you're seeing the loading indicator.
Obtain the Future in initState and pass that Future to your FutureBuilder.
However, if you're also using a PageView or equivalent to manage your pages, you'll also need to use the AutomaticKeepAliveClientMixin and call super.build(context).

Have to reload the app, when the button was clicked to see changes (Flutter)

This is my project: https://github.com/OstapFilipenko/Picslash
I trying to create an app, that is getting pictures from the unsplash API and display those pictures.
First am calling an async function to get all images and to save them in List. I am using ListView.builder to display them, and on the end of the listView i have a Button that makes a API call and adding new Pictures to ListView.
The function:
var newImages = new List<Picture>();
_getNewPics() async{
counter++;
newImages.clear();
newImages = await API_connection(counter).getPictures();
allImages.addAll(newImages);
}
And here is the button:
child: FlatButton(
child: Text("Load More",
style: new TextStyle(
color: Colors.white
)
),
onPressed: (){
_getNewPics();
},
),
The problem is, that when I start the app nothing is displayed, so I have to ctrl+S (reload the app) and then I see images, the same i have to do when I click on "Load more" button.
It would be great, if somebody would help me:)
You can use setState() with StatefulWidget to rebuild the widget once the new images come.
In java, you have a function called notifyDataSetChanged() which basically tells a listView that the list it is showing has changed and needs to be updated. Here is a detailed answer for Flutter to achieve the same - link

Detect when user interact with the app (Flutter)

I need a little help there.
I want to disconnect the user from the app when he has been inactive for 5 minutes. So I used this solution : Detect when user is not interacting the app in Flutter
It is perfectly working when the user tap on a widget that is not clickable. For example, when I tap on Text widget, the timer is restarting without an issue, but when I tap on a RaisedButton or ListTile, the timer is not restarting.
So, I'm wondering how it is possible for example with this code :
GestureDetector(
onTap: () => print("gestureDetector"),
child: RaisedButton(
onPressed: () => print("raisedButton"),
),
),
to print "gestureDetector" AND "raisedButton".
Thank you in advance for your help, cheers ;)
Use a Listener instead of GestureDetector.
Listener(
onPointerDown: (e) => print('listener'),
child: RaisedButton(
onPressed: () => print('raisedButton'),
),
),

Using a Circular Progress Indicator with Firebase Animated List in Flutter

I want to show a circular progress indicator on my screen until the data is fetched from the database which I am showing using FirebaseAnimatedList. But there is no way to check if the data is available yet. e.g. In FutureBuilder, there is a method hasData. Is there anything similar in FirebaseAnimatedList?
Use defaultChild: in firebaseAnimatedList for creating default screen while the query gets loaded
Possibly you can work with exists() from DataSnapshot.
itemBuilder: (BuildContext context, DataSnapshot snapshot,
Animation<double> animation, int index) {
if(!snapshot.exists()) {
// return fancy loading stuff
}
}
Put CircularProgressIndicator() in defaultChild property of FirebaseAnimatedList.
FirebaseAnimatedList(
defaultChild:Center(child: CircularProgressIndicator()),
query:databaseReference,
itemBuilder(context, DataSnapshot snapshot,
Animation<double> animation, int index) {
return Text('''')})
Their is one Property called defaultChild in FirebaseAnimatedList which will consider executing until data is getting loaded from snap shot or is still waiting and loading to fetch data.
To apply that just write for e.g you want to show Loader until that.
defaultChild: const Center(
child: CircularProgressIndicator(),
),
Inside FirebaseAnimatedList and you will see a loader until data is not fetched.

Resources