I'm making an app which shows place detail when clicking a place on Google Maps. I can open a container at bottom of the page but I want to do that when the user swipe this container to up with animation, the container will cover the whole page and I will get extra information about that place and show the user.
How can I do that with Flutter?
You can try to use the package here:
https://pub.dev/packages/sliding_up_panel
However, if this does not fullfill you wish, try this instead: Wrap you Widget with a GestureDetector and an AnimatedController:
double containerHeight = 0;
GestureDetector(
onVerticalDragEnd: (dragUpdateDetails) {
setState(){
containerHeight = //device height or use MediaQuery.of(context).size.height//
}
},
child: AnimatedContainer(
duration: Duration(milliseconds: //how long should it take//),
height: containerHeight,
child: //whatever you want//
)
),
AnimatedController automatically animates between changes of properties. However, you might want to try changing onVertivalDragEnd to something else like onVerticalDragUpdate to fullfill your wish completely. If you want to have that behaviour for the full screen, wrap your first return Widget with the GestureDetector.
I guess DragableScrollableSheet fits your need. There is a good explanation here in the documentation.
You can use DraggableScrollableSheet
Document and Video tutorial available here:
https://api.flutter.dev/flutter/widgets/DraggableScrollableSheet-class.html
Related
I'm currently building a podcast app and what I want to implement is something similar to miniplayer in Spotify.
When user taps on a podcast and start listening, I want to open a miniplayer and make it alive across on all pages.
First thing come up to my mind is using Overlay widget or using a custom navigator for all of the pages and put that navigator inside a stack. I wonder if anyone implement something like this before or what's the best way to approach this.
Try this package with less effort, miniplayer
Stack(
children: <Widget>[
YourApp(),
Miniplayer(
minHeight: 70,
maxHeight: 370,
builder: (height, percentage) {
return Center(
child: Text('$height, $percentage'),
);
},
),
],
),
I would recommend using this with a Stack. Maybe something like this:
class PodcastApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
Expanded(
child: Stack(
children: [
SizedBox.expand(child: PageView()),
Align(
alignment: Alignment.bottomCenter,
child: YOUR_MINI_PLAYER(),
)
],
),
),
BottomNavigationBar(),
],
),
);
}
}
I did the same thing but not for an audio player, where was that
First, build a BasePage that contains all of these tabs
Secondly, use GetIt to transfer the object from one page to another. The goal is to find out if the user played an audio clip or not.
Third You can store in GetIt the audio track, name, picture, ..etc.
In BasePage, you check if this object has been modified or if it is empty. The goal of viewing in BasePage is for it to be visible in all sections of the tab.
Do not hesitate to ask me if something is not clear.
Check out this persistent_bottom_nav_bar package
Features of this package
Highly customizable persistent bottom navigation bar.
Ability to push new screens with or without the bottom navigation bar.
20 styles for the bottom navigation bar.
Includes functions for pushing screen with or without the bottom
navigation bar i.e. pushNewScreen() and pushNewScreenWithRouteSettings().
Based on flutter's Cupertino(iOS) bottom navigation bar.
Can be translucent for a particular tab.
Custom styling for the navigation bar. Click here for more
information.
Handles hardware/software Android back button.
I hope this is what you are looking for.
I use MarkdownBody from flutter_markdown inside a LimitedBox. When pressing a "Show more" button the maxHeight is set to double.infinity and the full text is shown:
LimitedBox(
maxHeight:
showMoreCommentsIds
.contains(
commentId)
? double.infinity
: 100,
child: Wrap(
direction:
Axis.horizontal,
children: <Widget>[
MarkdownBody(
data: showList[index]
.comment
.comment,
)
],
),
),
But how can I find out the height of the text and only display the "Show more" button, if it is necessary?
You can find the length of the text using text.length and based on that information determine if the "Show more" button is needed. For example:
if(text.length > 60) {
_showButton();
}
You may need to do a little testing with the length of text in order to find out which length you want as the threshold. I just chose 60 as an arbitrary number.
I struggled a little bit with a similar problem because I had to know the size of a widget to apply some logic.
I learned that you can prerender the widget in an Offstage widget, determine it's size with the widget key once it is mounted.
You should wait for the rebuild, so you can force it and then you get the size with a function like this:
Future.delayed(Duration(milliseconds:100)).then(
final widgetSize = getWidgetSize(widgetKey);
// You can make logic here to remove the Offstage, variables, and free its space.
// Todo: apply the size in your code after this
)
...
Size getWidgetSize(GlobalKey key) {
final RenderBox renderBox = key.currentContext.findRenderObject();
return renderBox.size;
}
In my case, I needed it after tapping somewhere, so I used it inside a function, but maybe you will apply it directly on the initState(), inside a post frame callback.
initState(){
super.initState();
ServicesBinding.instance.addPostFrameCallback((_) async {
// here
});
}
I need help with the animation, when I click on the FAB icon in the first screen the icon will go up with animation and the other screen (shown in second image) should be displayed with animation like curtain. And the fab icon should be set in the app bar just like second image.
Bottom menu should be there in both of the screen just like the screenshots given.
you can do one thing that just wrap floating action button to AnimatedContainer
AlignmentDirectional _ironManAlignment = AlignmentDirectional.bottomCenter;
...
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: AnimatedContainer(
duration: Duration(seconds: 2),
alignment: _ironManAlignment,
child: FloatingActionButton(
onPressed: () {
_flyIronMan();
},
child: Icon(Icons.add),
),
),
like this
when you press button then call this method
void _flyIronMan() {
setState(() {
_ironManAlignment =
AlignmentDirectional(0.0, -0.8); //AlignmentDirectional(0.0,-0.7);
});
}
Just wrap buttons on each page with Hero widget, like that:
Hero(
tag: 'fab',
child: FloatingActionButton()
);
With the design you want to achieve, I suggest you to use sliding_up_panel instead of pushing new screen.
It has a parameter collapsed, in which you can put FloatingActionButton to expand panel on tap, and show the same button inside the expanded panel. Just set panel non-draggable, and it won't expand on swipe.
Please read it's documentation carefully, I'm sure you can achieve the effect you want with this widget.
I'm trying to create a desktop-style scrollbar, that changes it's size based on the size of the content. My scrollbar shares a ScrollController with a list, and relies on the position.maxExtents to know how large the content area is.
The issue is that when I change the number of rows, the maxExtents will not update, until a scrollEvent is initiated.
I've worked around it with code like this, moving 1px up, and 1px down over 100ms:
widget.controller.jumpTo(controller.position.pixels + 1);
Future.microtask(() => widget.controller.animateTo(controller.position.pixels - 1, duration: 100.milliseconds, curve: Curves.linear));
Which works pretty quite well when the list can scroll. However, when the list is < the height of the view, it can't scroll, and these calls have no effect, and I'm stuck with a stale maxExtents.
How can I just tell the list: "Hey, list, recalculate your children!"?
You can delay your code to when the controller has been updated using the following.
WidgetsBinding.instance.addPostFrameCallback((_) {
...your code which requires controller's max extents...
});
Aside from the issues you've mentioned, animating the scroll position will cause Flutter to draw frames unnecessarily.
Borrowing from #ltk and #Rakaton, I think there's a simpler and more efficient way to do this:
// Run this code whenever the layout changes (i.e. any time you setState
// to update the list content)
WidgetsBinding.instance?.addPostFrameCallback((_) {
widget.scrollController.position.notifyListeners();
});
You may also want to wrap your list component in a SizeChangedLayoutNotifier to detect size changes from things like window resize:
NotificationListener<SizeChangedLayoutNotification>(
onNotification: (notification) {
WidgetsBinding.instance?.addPostFrameCallback((_) {
scrollController.position.notifyListeners();
});
return true;
},
child: SizeChangedLayoutNotifier(
child: ListView(
// ...
),
),
);
I think you should consider to use force pixels, but it's a protected method so it's gonna give you a warning. And I don't know about the performance or another stuffs about it.
widget.controller.position.forcePixels(controller.position.pixels + 1);
or combination of correct pixels and notifylistener.
widget.controller.position.correctPixels(controller.position.pixels + 1);
widget.controller.position.notifyListeners();
Since i want this to be my layout i was trying to implement it with a showModalBottomSheet but the problem is that this widget only works on the button click as it is showing me error when i am trying to call the method as it is in the initState method. So then I started to work with bottomSheet value present in the scaffold but then the background is being displayed only upto the starting of the bottom sheet creating the distance between the model sheet and the background whereas I want it to overlap like in the image..How should I prepare this layout.
There is no such parameter as "bottomSheet" in Scaffold, there is one called bottomAppBar, which is used for making Bottom Bars like the one on the Youtube Android App. So this should help you make the basic structure.
Use a Stack widget to put widgets on top of each other, in first layer, add the image using the NetworkImage widget, then in the second layer, make a Column, like this:
#override
Widget build() => Scaffold(
body: _body(),
);
_body() => Stack(
children: <Widget>[
NetworkImage(your_url_here),
Column(
children: <Widget>[
_basicDetails(),
_guidePanel(),
]
),
]);
Then create 2 new methods after the _body method like this:
_body() => Stack(...);
_basicDetailsPage() => Container();
_guidePanel() => Container();
Create your layout in these 2 methods. Comment if you need more help :)
For additional help, please join the Facebook Group "Let's Flutter With Dart"