Flutter side nav animation - flutter

I was trying to build to build slack mobile app like side nav where we can swipe right to reveal the drawer and also the main body moves right as drawer opens.
Layout:
Stack
-Drawer()
-Homepage()
On the hamburger icon click, I am using animated position to shift the HomePage() and reveal the drawer.
But how can I implement something like the pageview thing, where first child would be Drawer and second child would be Homepage and I can right to reveal the drawer, but the drawer's width will be only 0.5* viewportfraction so the homepage is also revealed and dimmed.

i faced the same problem and i used Stack to solve it, look at
the example:
Stack(
children: <Widget>[
buildPartnerList(context),
FilterDrawer(),
],
),
in the FilterDrawer i returned one animationBuilder
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animationController,
builder: (context, widget) => buildFilterDrawer(context, widget),
);
}
the buildFilterDrawer its something like this
Container(
color: Theme.of(context).accentColor,
width: widthAnimation.value,
child: Stack(
children: <Widget>[
...
and to make the animation, i have one GestureDetector, when clicked animate my side Bar
_moveFliterDrawerAnimation() {
setState(() {
_isExpanded
? _animationController.reverse()
: _animationController.forward();
_isExpanded = !_isExpanded;
});
}

Related

Flutter: Make a custom bottom bar sliding up when it appears

I am trying to design a music playlist page. So far I am able to create a listview with song cards. When I click on any song, a custom bottom bar appears and the audio starts playing. However, I just hold a state with boolean and show the bottom bar according to that. Instead, I want it to appear like sliding up and reach to the position. Let say in 0.5 seconds.
I have a custom NavBar class
class NavBar extends StatefulWidget
And I use this class in build similar to:
return Column(
children: [
SizedBox(
height: constraints.maxHeight * 0.5,
hild: SlidingBanners(),
),
Expanded(
child: Lists(),
),
NavBar()
],
);
How can I such animation?
Use a SizeTransition widget https://api.flutter.dev/flutter/widgets/SizeTransition-class.html
"SizeTransition acts as a ClipRect that animates either its width or
its height, depending upon the value of axis. The alignment of the
child along the axis is specified by the axisAlignment."
Widget build(BuildContext context) {
return SizeTransition(
sizeFactor: CurvedAnimation(
curve: Curves.fastOutSlowIn,
parent: _animationController,
),
child: Container(height: 100, color: Colors.blue)
);
}
init animation controller in stateful widgets initState()
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 500));
}
Make sure your stateful widget uses SingleTickerProviderStateMixin
class _NavBarState extends State<NavBar>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
then open and close with
_animationController.forward()
_animationController.reverse()
You can pass the _animationController into the NavBar widget's constructor from its parent if you want the parent to control the animation.
Alternatively you can use an AnimatedContainer widget and setState with its height 0 or 100 depending on if Nav should be shown. This becomes a problem for some widgets that cannot be squished to height of 0 though and I would not recommend for any container that contains anything but text
One solution would be to use a SnackBar widget. Since it's automatically animated, you wouldn't want to worry about manually animating the bottom bar. You can insert your Audio Player (bottom bar) widget to the child of the SizedBox.
The bottom bar is made visible by,
ScaffoldMessenger.of(context).showSnackBar(snackBar);
This bottom bar is dismissed (hidden) by dragging down or by,
ScaffoldMessenger.of(context).hideCurrentSnackBar();
There maybe many other solutions as well to this, but reading your question, I hope this is what you wanted.
return Center(
child: ElevatedButton(
onPressed: () {
final snackBar = SnackBar(
duration: Duration(days: 365),
content: SizedBox(
height: 100,
//insert your audio player widget here
child: Column(
children: [
Text("YOUR AUDIOPLAYER WIDGET HERE"),
Text("Audio Controls Here"),
ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
},
child: Text("Audio Player Minimize"),
),
],
),
),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
},
child: Text('Open Audio Player'),
),
);

AlignParentBottom a widget in SingleChildScrollView flutter

I am trying to align a button at bottom of a scrollable screen. For scrolling I used SingleChildScrollView and inside which I added all the widgets. Now the only issue I face is the button is not sticking at the bottom on scroll. Help would be appreciated.
As you can see in the image, the buy now button and add to cart button aligned at bottom. If we scroll the entire page, the button will not be scrollable, instead it will be sticking at bottom. I want to implement in this way.
You can use an stack and put All the widgets except button as its First child and then the button as another child of stack. Below it the example code -
class MyNewApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
alignment: Alignment.center,
children:[
Container(
height:MediaQuery.of(context).size.height,
width:MediaQuery.of(context).size.width,
child:SingleChildScrollView(
child: Column(
// Provide Your Widget here
),
),
),
Positioned(
bottom:0,
child:Align(
alignment:Alignment.center,
child: RaisedButton(
child:Text("Button"),
onPressed:(){},
),
),
),
],
),
);
}
}

Keyboard hides TextField in a PageView

I have a quite complex screen with a page view and a FutureBuilder. In the Pagebuilder I also have a TextFormField, when I now click into the text field the keyboard opens like expected. But it then hides the textfield and a button below the text field.
I already searched for solutions, things i tried:
setting resizeToAvoidBottomInset to true
putting the outer column or the pageview into a SingleChildScrollView
adding some padding (in the size of the keyboard) to the bottom
Any other ideas?
I only have ONE Scaffold.
What approach are you using to get the keyboard size? The best approach is to add a bottom padding that pushes the textfield upwards, you might also want to put the form fields inside the SingleChildScrollView that you've mentioned:
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom + 10
Make your page scrollable and add padding to the button like so:
SingleChildScrollView(
child: Column(
children:
textInput,
Padding(padding: ..., child: button)
...
This would allow user to scroll to the button after edit.
Another solution is to make button float - it would always be above keyboard
return Stack(
children: <Widget>[
Positioned(
child: SingleChildScrollView(
child: Column(
children: [textInput]
..add(Container(
height: 1111, // button heigh, so could scroll underlapping area
)))),
),
Positioned(
child: Align(
alignment: Alignment.bottomCenter,
child: button,
),
)
],
);
Have you tried this?
You should wrap your page with SingleChildScrollView so that you get scrolling in the case when your content grows bigger than the device height because of introduction of keyboard.
Soution
1)Use of WidgetsBindingObserver mixin
2)Adding scrollController to SingleChildScrollView
3)Listening the event of presence of keyboard using didChangeMetrics and scrolling as per the need.
class _SignInState extends State<SignIn> with WidgetsBindingObserver{
ScrollController _scrollController = new ScrollController();
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
#override
void didChangeMetrics() {
_scrollController.jumpTo(200); //as per your need
}
Inside Scaffold widget adding controller to SingleChildScrollView
body: SingleChildScrollView(
controller: _scrollController,
)

Flutter preserve scroll position of CustomScrollView inside hero

I have a pane app with Drawer, which I show in a pane layout when the phone is in landscape mode:
To not animate the drawer between pages, I have put it into a Hero.
class SomePage extends Stateless Widget {
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Hero(
child: Material(
elevation: 4.0,
child: MyDrawer(),
tag: "drawer",
),
Expanded(
child: Scaffold(
appBar: ...,
body: ...
)
),
],
);
}
}
The drawer contains a CustomScrollView.
When the user changes pages using Navigator.pushNamed (I am using MaterialApp with onGenerateRoute), the scroll position of the CustomScrollView is reset.
How can I keep the scroll position between page changes (or how can I synchronize the scroll position of the drawer on different pages)?

Is there a simple way to make android back button act exactly like app bar back button?

I want the hardware back button act exactly like my app bar back button always. I want it for all my screens. WillPopScope seems not easy to use because my appbar will be always the child of the WillPopScope and therefore I would need to pass data to parent for many different cases.
Edit(more details):
In registration page of my app, if the registration process is on going I am blocking the user interaction with the app by using IgnorePointer but of course this does not work for hardware back button.
Also I designed my appbar back button according to the state of the registration page. So there are at least 3 cases for appbar back button. I want all to apply to the hardware back button too. The problem is I am in a child widget therefore I don't want to call function for each case and update the state of the parent widget. It is not useful and It is very painful to do.
If I understood your problem correctly, I have something that might help you. I've implemented this code on one of my projects. However, I have used Provider and Bloc for state management.
signup_page.dart
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: _onWillPop,
child: ChangeNotifierProvider<SignupProvider>(
create: (context) => SignupProvider(),
child: BlocProvider<SignupBloc>(
create: (context) => _signupBloc,
child: BlocListener<SignupBloc, SignupState>(
listener: (context, state) {
if (state is SignupCancelled) {
_closeSignup();
} else if (state is SignupSuccessful) {
_closeSignup(delay: 1000);
} else if (state is SignupInitial) {
_returnToCredentials();
} else if (state is SignupProceededToDetails) {
_proceedToDetails();
}
},
child: Scaffold(
appBar: AppBar(
title: const Text("Signup"),
),
body: SafeArea(
child: PageView(
controller: _pageController,
physics: const NeverScrollableScrollPhysics(),
children: <Widget>[
const SignupCredentialsForm(),
const SignupDetailsForm(),
],
),
),
),
),
),
),
);
}
Future<bool> _onWillPop() async {
int page = _pageController.page.toInt();
_signupBloc.add(
BackButtonPressed(page: page),
);
return false;
}
_closeSignup({int delay = 0}) async {
await Future.delayed(Duration(milliseconds: delay));
Navigator.of(context).pop();
}
_returnToCredentials() {
_pageController.animateToPage(0,
duration: const Duration(milliseconds: 250), curve: Curves.easeIn);
}
_proceedToDetails() {
_pageController.animateToPage(1,
duration: const Duration(milliseconds: 250), curve: Curves.easeIn);
}
If you look at my code, I have a PageView consisting of 2 pages. The PageView pages both use AppBar, and contains the leading back button on the top left.
Normally, pressing the back button on the appbar, or the hardware back button will lead to closing the page, and navigating back to the previous page.
However, since I have WillPopScope, on signup_page, that is overriden. Hence, I'm able to control what I want to happen when the back button (appbar or hardware) is pressed. Which you can see, I can move to the next page view using _proceedToDetails(), or return to the first form using _returnToCredentials(), and close the page if I'm in the first form and back button is pressed, using _closeSignup(), similarly, closing the page even when I'm at the last form if the signup is successful.