PageView PageController losing clients - flutter

I have a PageView widget with 2 children, on the first page there's a button where you can call a function, when it returns the PageView should be animated to the second page.
To do this i'm using this code:
if (pageController.hasClients) {
pageController.animateToPage(
1,
duration: Duration(milliseconds: 200),
curve: Curves.easeIn,
);
}
But when calling this after the function returns, the pageController doesn't have any clients.
I have tried placing a button to animate to the second page and pressing that right after the function is done, which works.
I don't understand why it works one place and not the other place..

Related

How do I play a sequence of animations (from different controllers) after a widget loads?

A screen in my app has three buttons. When the screen loads I am trying to have the following sequence happen:
widget loads
button 1 animation
pause
button 2 animation
pause
button 3 animation
Each button is it's own widget with it's own AnimationController and a playAnimation() function. I have tried multiple ways of doing this and can not get this to work.
If I use WidgetsBinding.instance.addPostFrameCallback():
#override
void initState() {
...
WidgetsBinding.instance.addPostFrameCallback((_) {
button1.playAnimation();
sleep(const Duration(milliseconds: 1000));
button2.playAnimation();
sleep(const Duration(milliseconds: 1000));
button3.playAnimation();
});
...
}
I get:
blank screen
2 second pause
widget loads
all animations play at the same time
If I put print statements in I can see the app cycling through the the button presses on the blank screen.
If I use FutureBuilder():
#override
Widget build(BuildContext context) {
...
return FutureBuilder(
future: Future.delayed(Duration(seconds: 5), () {
button1.playAnimation();
sleep(const Duration(milliseconds: 1000));
button2.playAnimation();
sleep(const Duration(milliseconds: 1000));
button3.playAnimation();
}),
...
);
}
I get:
widget loads
all animations play at the same time
It seems the FutureBuilder method is 'closer' to a solution. However, I can't find anything on how to get multiple animation controllers to run in a sequence like this. From what I can tell, staggered animations only work on a single controller/widget.
Any ideas on how to accomplish this? Thanks for your help.
U can do that with one Controller by dividing its duration interval to many animation sequences.
Check this video
My Code sample

Flutter: push a new page and scroll down to certain widget

I have a web app in Flutter. My app bar has 5 buttons. Four of them scroll within the first page. The 5th one pushes a new page.
There are three different widgets on stage:
page A (HomeView)
page B (ContactView)
the AppBar
When the user is on page A, the buttons of page A should scroll down or up, and the button of page B should push the new view. When the user is on page B (contact) and presses any of the buttons of page A, it should push the home page and scroll to the required position.
I tried to do this with ScrollController.animateTo and Riverpod for state management, but I couldn't do it. The page scrolled down but, for some reason, it scrolled up again and didn't maintain position. I couldn't find any related post on the Internet and keepScrollOffset was not the answer. I also didn't know how to scroll to the desired position when the user is not on 0 (it scrolled down from current position).
After that, I tried to do it with GlobalKeys. It worked, up to a certain point. On page A, everything worked fine. However, from page B to page A, it said the keys are duplicated. I used pop instead of pushNamed as suggested in one of the answers of this post (the only answer that worked for me). The behavior was finally as expected, but I can't use pop since I have more pages on the website, so I can't ensure that the user is coming from page A. I tried with pushReplacement as the same answer gives that for an alternative option. It doesn't work now. When trying to go from page B (contact) to page A, it throws error Unexpected null value.. Furthermore, I need to keep the back button functionality since it is a website. I would prefer, then, to not pop the current page from the stack.
The page A (HomeView) is built like this inside of a StatefulWidget:
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
controller: _scrollController,
child: Column(
children: [
HomeHeader(key: HomeView.homeKey),
WhatWeOffer(key: HomeView.servicesKey),
WhatWeDo(key: HomeView.referencesKey),
WhoWeAre(key: HomeView.aboutKey),
const LetsGetInTouch(),
],
),
);
}
The GlobalKeys are defined like this inside of the Page A (HomeView) widget:
static GlobalKey homeKey = GlobalKey(debugLabel: 'homeHeaderKey');
static GlobalKey servicesKey = GlobalKey(debugLabel: 'whatWeOfferKey');
static GlobalKey referencesKey = GlobalKey(debugLabel: 'whatWeDoKey');
static GlobalKey aboutKey = GlobalKey(debugLabel: 'whoWeAreKey');
This is how I implement the navigation to page A:
if (ModalRoute.of(context)?.settings.name != '/') {
Navigator.pushReplacementNamed(
context,
Navigation(context).routes["/"]!,
);
}
Scrollable.ensureVisible(
HomeView.homeKey.currentContext!,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
And this is how I implement the navigation to page B:
Navigator.pushReplacementNamed(
context,
Navigation(context).routes["/contact"]!,
),
Maybe GlobalKey is not the right approach to my problem? Is there any easier way to push a new page and scroll down to a widget?
EDIT:
I think the Unexpected null value problem was caused because the view was not built yet when I was trying to call it. I solved it by making the functions async and waiting before using the keys:
TextButton(
child: Text('home', style: style),
onPressed: () async {
if (ModalRoute.of(context)?.settings.name != '/') {
Navigator.pushReplacementNamed(
context,
Navigation(context).routes['/']!,
);
await Future.delayed(const Duration(milliseconds: 500));
}
Scrollable.ensureVisible(
HomeView.homeKey.currentContext!,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
},
),
However, this does not solve my problem of the back button. As I use pushReplacementNamed instead of pushNamed, I can't go back. Is there a more optimal way to do this, where I can keep the back button functionality?

Flutter: How configure ListView() to auto-scroll to bottom (as new children are added) except when momentarily scrolling up to view older children

(I'm using a ListView() to implement a little logging pane that shows my app events.)
I looked through the ListView() documentation and can't seem to find a property like autoScrollUnlessBrowsing. I need:
The ListView() to auto-scroll to bottom every time a new child is
added (so I don't have to manually drag up to expose new app activity).
However, while I'm temporarily scrolling-up to view old activity I want auto-scrolling momentarily suspended (because it's annoying to have it yank scrolling to bottom interrupting what I'm trying to read). After I'm done browsing, if I manually scroll back to the bottom I want to auto-scroll to re-enable.
ListView(
autoScrollUnlessBrowsing: true,
children: children),
It would be great if Flutter could add this but until then how could this be implemented?
You can use a scroll controller for that.
Define a scroll controller inside your page.
ScrollController _controller = ScrollController();
Then pass it to your listview widget. For example -
ListView.builder(
controller: _controller,
itemCount: 10,
itemBuilder: (_, __) => ListTile(title: Text('demo')),
)
Then in your child adding function, right after the child adding logic, put the following code for smooth scrolling to the end of the listview.
_controller.animateTo(
_controller.position.maxScrollExtent,
duration: Duration(seconds: 1),
curve: Curves.fastOutSlowIn,
);
your function will look something like this.
void addChild(){
// put your adding child logic here
_controller.animateTo(
_controller.position.maxScrollExtent,
duration: Duration(seconds: 1),
curve: Curves.fastOutSlowIn,
);
}
We can also use _controller.jumpTo() for jumping to the end. But for smooth scrolling _controller.animateTo() is preferred.
But I would suggest adding the recently added children to the beginning of the list rather than adding it to the end. I mean rather than list.add(element), use of list.insert(0, element) is preferred.

How do I set the focus of the list view on the last item in chat list

I have a list view where I show the chat list but when the list view always focus to the first item.
i set stack from bottom true in java and this work for me
Try attach ScrollController controller; on ListView.builder() and when to scroll use this
controller.animateTo(controller.position.maxScrollExtent,
duration: Duration(seconds: 2), curve: Curves.easeIn);

What is Default animation duration and curve of PageView swipe Animation?

When the pages on page View are swiped it have have a sort of default animation that brings the page to the center of the screen.
I want the animation curve and duration of this swipe cool down and
animateTo() on button pressed to be the same.
I have tried all different curves and duration none of that matches the default.
I have also tried using Custom Scroll Physics.
In page_view.dart there's an example:
_pageController.animateToPage(
0,
duration: const Duration(milliseconds: 400),
curve: Curves.easeInOut,
);
I think it's reasonable to assume it uses 400 ms and easeInOut as default.