FirebaseAnimatedList initial scroll position - flutter

I have a chat app, so I would like to show the last messages when we open the conversation. I have initialized a ScrollController (and attached it to my FirebaseAnimatedList) and tried this :
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
if (controller.hasClients) {
print("message");
controller.jumpTo(controller.position.maxScrollExtent);
}
});
}
It doesn't work, and doesn't print "message".

You can solve the reverse property of FirebaseAnimatedList class by setting true.

Related

Video Player not showing unless I do a hot reload

I have code in the initState that goes something like this (video_player package):
#override void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
final navBar = Provider.of<NavBar>(context, listen: false);
navBar.urls.forEach((element) {
navBar.videoControllers.add(
VideoPlayerController.network(
navBar.urls[videoCount],
videoPlayerOptions: VideoPlayerOptions(
allowBackgroundPlayback: false,
),
)..initialize().then((value) => navBar.initialized = true)
);
}
navBar.notify(); // This is a method that calls notifyListeners() within the NavBar class (with ChangeNotifier like how a Provider class does).
navBar.videoControllers[0].play();
navBar.notify();
}
}
The behaviour of this code is such that the video starts playing, I can hear the audio as well, but the page has not been loaded as I see no UI elements but a white screen.
Once I just do a hot reload, all the UI elements are there and I can see the video playing as well.
Where am I going wrong here?
Maybe you are having a problem with async methods...
Try to move this block:
navBar.notify(); // This is a method that calls notifyListeners() within the NavBar class (with ChangeNotifier like how a Provider class does).
navBar.videoControllers[0].play();
navBar.notify();
Inside the initialize response, something like this:
..initialize().then((value) {
navBar.initialized = true
navBar.notify(); // This is a method that calls notifyListeners() within the NavBar class (with ChangeNotifier like how a Provider class does).
navBar.videoControllers[0].play();
navBar.notify();
})
In this way you will only start the video after it is added to the videoControllers and it`s properly initialized.
The final code:
#override void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
final navBar = Provider.of<NavBar>(context, listen: false);
navBar.urls.asMap().forEach((index, element) {
navBar.videoControllers.add(
VideoPlayerController.network(
navBar.urls[videoCount],
videoPlayerOptions: VideoPlayerOptions(
allowBackgroundPlayback: false,
),
)..initialize().then((value) {
navBar.initialized = true)
// To execute this block just when it is the first item.
if (index == 0) {
navBar.notify();
navBar.videoControllers[0].play();
navBar.notify();
}
}
);
}
}
}
The video_player package works as such that it will not display the video correctly if you display the VideoPlayer before it is initialized in your Widget tree. So just like their tutorial in the flutter packages website (which I sadly overlooked at that part), you need to check for the initialization before displaying it. If you don't, only a hot reload can work.
children:
(navBar.videoControllers[0].value.isInitialized) ?
navBar.scrollingVideos :
[
Container(),
],
Note: scrollingVideos is a List<Widget> that contains the video players.

Flutter: how to use .addListener of TextEditingController

i'm building a simple app that prints the result of the current TextFormField. Such as when the text changes it prints the new value.
I found out that you can achieve this with TextEditingController.addListener that listens for changes and executes a function.
So i wrapped it all in initState as follows
#override
void initState() {
addressController.addListener(() {
print(addressController.text);
});
The problem I have is that sometimes it records changes even when there aren't any:
This is what happens writing a word and then deleting it.
If you add listener then you should remove it somewhere, otherwise there can be situation when TextEditingController will have 2 or more listeners:
#override
void initState() {
addressController.addListener(_addressControllerListener);
super.initState()
}
void _addressControllerListener() {
print(addressController.text);
}
#override
void dispose() {
addressController.removeListener(_addressControllerListener);
super.dispose()
}

Flutter How to recreate stateful widget in build()

I'm developing android/ios app using flutter with provider(state management)
in my app, i have a Main scaffold with bottom navigation menu. (so, one scaffold with many views and controll it using bottom navigation, NOT Navigator.push())
i want to know that is it possible recall initstate() from build().
for example
... Statefulwidget
void initState() {
super.initState();
MYHttp.callAPI_only_once_for_some_reason();
}
Widget build(...) {
var flag = Provider.of<MyProvider>(context).flagdata; // flag is true when push notification has been arrived
if (flag) {
initstate() // apparently it should not work, but i have to recreate whole stateful widget to call initState()
}
}
No it is not possible. The initstate() is only called each time a new widget is painted.
Instead of recalling the initstate. Create a method, add it to use init state and call wherever you want to call it.
Check the code below for an example. It works perfectly:
// create the method.
void makeRequest() {
MYHttp.callAPI_only_once_for_some_reason();
}
void initState() {
//call the created method here
makeRequest();
super.initState();
}
Widget build(...) {
var flag = Provider.of<MyProvider>(context).flagdata; // flag is true when push notification has been arrived
if (flag) {
// call the method here again. if you need to use it.
makeRequest(); // apparently it should not work, but i have to recreate whole stateful widget to call initState()
}
}
I hope this helps.

didChangeAppLifecycleState doesn't work as expected

I hope I understand how didChangeAppLifecycleState worked correctly.
I have page A and page B . When I click the back device button from page B ( Navigator.of(context).pop(); ), I expect didChangeAppLifecycleState in pageA will get called, but it doesn't.
PageA
class _ABCState extends State<ABCrList> with WidgetsBindingObserver {
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
....
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
setState(() {
print(...);
});
}else{
print(state.toString());
}
}
....
This is the initState in pageA. The function used to call backend service.
#override
void initState() {
super.initState();
_bloc.getList(context); // return list and populate to ListView
});
}
The way you're thinking it is Android's way where onResume works, but in Flutter, things don't happen this way.
Generally, this gets called when the system puts the app in the background or returns the app to the foreground.
There are mainly 4 states for it:
resumed: The application is visible and responding to user input.
inactive: The application is in an inactive state and is not receiving user input.
paused: The application is not currently visible to the user, not responding user input, and running in the background.
detached: The application is still hosted on a flutter engine but is detached from any host views.
Edit:
When you're navigating to PageB from PageA, use something like:
Navigator.pushNamed(context, "/pageB").then((flag) {
if (flag) {
// you're back from PageB, perform your function here
setState(() {}); // you may need to call this if you want to update UI
}
});
And from PageB, you'll can use
Navigator.pop(context, true);

Adding a widget to a listview with .contains

In the init state, I add an if statement to check to see if a custom widget exists on the list that I will pass to the Listview.builder, called an AddItemButtonTile. If it doesn't exist, I want to add it to the list.
#override
void initState() {
if (!displayedList.listContent.contains(AddItemButtonTile)) {
displayedList.listContent.add(AddItemButtonTile(openTextField));
}
super.initState();
}
This code results in a new AddItemButtonTile being added every time I navigate away and return to the page. Why is this If statement returning true?
Try:
#override
void initState() {
if (!displayedList.listContent.any((item) => item is AddItemButtonTile)) {
displayedList.listContent.add(AddItemButtonTile(openTextField));
}
super.initState();
}