I'm trying to show a snackbar.
After I click on a gesture detector, this snack has two buttons.
The problem is that the snackbar appears for seconds and then disappears.
So I have two questions:
How to stop the snackbar from disappearing until the user takes an action and clicks on a button?
Also, the snackbar has a height of the whole screen.
How can I make it have a specific height in the bottom of the screen?
You can use a long duration
HomeScreen.scaffoldKey.currentState.showSnackBar(
SnackBar(duration: const Duration(minutes: 5), content: Text(message)));
See also https://material.io/design/components/snackbars.html#behavior
Appearing and disappearing
Snackbars appear without warning, and don't require user interaction.
They automatically disappear from the screen after a minimum of four
seconds, and a maximum of ten seconds.
I wrote a little something about how i solved this https://dev.to/eyewritecode/persisting-flutter-snackbar-until-user-interacts-with-it-2583
You basically have to set a long duration
duration: Duration(days: 1)
And call the following whenever you want to hide it.
Scaffold.of(context).hideCurrentSnackBar();
You can change the height of a snackbar by setting a different Max Number of lines (default is 2 usually):
You can do this like this (example with 10 lines):
View snackbarView = snackbar.getView();
TextView snackTextView = (TextView) snackbarView.findViewById(
com.google.android.material.R.id.snackbar_text);
snackTextView.setMaxLines(10);
snackbar.show();
final Snackbar snack = Snackbar.make(findViewById(android.R.id.content), helpMsg, Snackbar.LENGTH_INDEFINITE);
snack.setAction("OK", new View.OnClickListener() {
#Override
public void onClick(View v) {
// Respond to the click dismiss is automatic with this
}
});
View view = snack.getView();
FrameLayout.LayoutParams params =(FrameLayout.LayoutParams)view.getLayoutParams();
params.gravity = Gravity.TOP;
view.setLayoutParams(params);
snack.show();
Related
I am trying to correct a page of my PageView, while the user is changing pages.
Unfortunately, all possible page change operations (jumpTo(), animateTo(), jumpToPage()) dismiss the current user input (or animation). Which looks like:
Input: user drags to next page
Programatical change: during page change page adjust operation is called
Current Output: Current drag is dismissed and the page jumps ( or animates) without any regard to further user input to the desired page
Is there any way to change pages, while the user animates the pageview? So that the output might look like:
Desired Output: Current drag is being kept and the page only changes. User still can continue to drag.
The usual operations dismiss all animation and user inputs. As also described in the documentation:
Any active animation is canceled. If the user is currently scrolling, that action is canceled.
In order to archive what you are trying to do you can use the following class extension:
class SmoothPageController extends PageController {
SmoothPageController({
int initialPage = 0,
bool keepPage = true,
double viewportFraction = 1.0,
}) : super(
initialPage: initialPage,
keepPage: keepPage,
viewportFraction: viewportFraction,
);
/// Jumps the scroll position from its current value to the given value,
/// without animation, and without checking if the new value is in range.
/// Any active animation is being kept. If the user is currently scrolling,
/// that action is kept and will proceed as expected.
/// Copied from ScrollController.
void smoothJumpTo(double value) {
assert(positions.isNotEmpty,
'ScrollController not attached to any scroll views.');
for (final ScrollPosition position in List<ScrollPosition>.of(positions)) {
position.correctPixels(value);
}
}
}
Now you can call smoothJumpTo() just like jumpTo() but without dismissing active animations, such as the user input.
final SmoothPageController pageController = SmoothPageController(initialPage: 0);
In my app, I have a homepage that has 5 tabs on the bottom. On each tabbed page, there is an app bar that has a '+' symbol as an action, which navigates you to a different page. The navigation with that '+' button to the new page is done with the following code, alongside the Flutter Platform Widgets package:
Navigator.of(context, rootNavigator: true)
.push(
platformPageRoute(
context: context,
builder: (context) => Page1(),
),
);
I use the platformPageRoute feature as an easy way to navigate with a native feel. Now, that works fine to navigate to a new page, but the issue comes when I use
Navigator.pop(context);
to navigate back to the original page. When I use that to navigate back to that original page, it pays no attention to the tab that was selected originally. For example, if I were originally on the second tab on the homepage and then use the '+' button on that tab and then finally use
Navigator.pop(context);
on that new page, it returns the first tab of the homepage. Is there any way of ensuring when I use the above command, it goes to the right tab? I have tried something along the lines of:
Navigator.popUntil(context, '/homepageTab2');
alongside a named route, to return to the correct tab on the homepage, although that returns a black screen. Why might that be? I have also tried using:
Navigator.pushAndRemoveUntil(
context,
platformPageRoute(
context: context,
builder: (context) =>
HomePage(selectedPage: 1),
),
(route) => false,
);
This does not work either, since it returns the selected/correct page tab content, but with the first tab selected. In addition, the other
'problem' for me is that the animation is a 'push' one and that doesn't 'match' with the animation when I have more often used
Navigator.pop(context);
to navigate back to a screen. Is there a way to maybe use pushAndRemoveUntil but then change the animation to match a pop animation?
Thanks!
EDIT:
I have just noticed that with the situation I have described above, it is actually returning the correct screen content when I use Navigator.pop(context); but the tab in the tab bar at the bottom is showing as the first tab, in the second tab's position, essentially duplicating the first tab, until I navigate to a new tab and back, at which time it shows the correct tab in the correct position. I hope that makes sense!
As it turns out, the issue wasn't related to Navigator.pop(context); being used. It was the way I was controlling the selected tab. I'm posting this as an answer incase it helps someone else.
Initially, I created late values for a tab controller and the current selected page, like so:
late TabController _tabController;
late ScrollController _scrollController;
late int _selectedPage;
Then, I created a list of widgets that represented the actual page to display for each selected tab:
List<Widget> _pageWidgets = <Widget>[
Page1();
Page2();
Page3();
Page4();
Page5();
];
Then (and I think this was the bit that wasn't working) I used initState() as follows:
void initState() {
super.initState();
// Initialising a value that allows the 'final' page selector to be changed
_selectedPage = widget.selectedPage;
// Initialising the tab controller
_tabController = TabController(
length: 5,
vsync: this,
initialIndex: _selectedPage,
);
// updating the tab index when a new item is selected
_tabController.addListener(() {
setState(() {
_selectedPage = _tabController.index;
//_tabIndex = _tabController.index;
});
});
// Creating the scroll controller
_scrollViewController = ScrollController();
// Scrolling view to top when a new tab is selected
_tabController.addListener(() {
setState(() {
_scrollViewController
.jumpTo(_scrollViewController.position.minScrollExtent);
});
});
}
I then controlled the page content like this:
body: _pageWidgets.elementAt(_selectedPage),
I'm not 100% sure why this wasn't working, although I believe it would have something to do with the fact that initState() would only be called during the build and therefore placing the functionality inside there would mean changes wouldn't be detected. Either way, my new method, which works perfectly, is:
/// Controls the screen to display first
int _index = 0;
/// Creating a navigation key to control tab bar navigation
final _navigationKey = GlobalKey<CurvedNavigationBarState>();
Then, within the Scaffold() I show the page content like this:
body: _pageWidgets.elementAt(_index),
And finally, within the navigation bar (which is the CurvedNavigationBar() package from pub.dev) I give it a key and the index:
key: _navigationKey,
index: _index,
And this controls it perfectly, showing the correct tab.
Sub-pages of a TabBarView cannot be navigated using Navigator.
You can use TabController to go to your desired tab page after awaiting Navigator.push():
await Navigator.of(context, rootNavigator: true)
.push(
platformPageRoute(
context: context,
builder: (context) => Page1(),
),
);
tabController.animateTo(<index of tab>);
I have a problem with a BottomNavigationBar. My app has two screens and one of them has a bottom sheet that doesn't hide on disposing of the first screen when I'm tapping on the icon of the second screen. BottomNavigationBar works normally and screens switch, but how to hide the bottom sheet of the first screen when shown the second screen I have no ideas. Could you help me solve that?
Calling:
void _showForm(int id) async {
showBottomSheet(
context: context,
elevation: 5,
builder: (context) => BottomSheetSwitch(_refreshJournals, id));
}
Closing (inside bottom sheet):
Navigator.of(context).pop();
P.S. I couldn't solve that problem, so I change showBottomSheet to showModalBottomSheet like that was in: how to set showModalBottomSheet to full height?
You can try to provide a key to the stateful widget of BottomSheet. And access that key's context just use the Navigator's pop method on tap or change of the screen. It might help you.
You can use it like
Navigator.of(key.currentContext).pop();
Else call the function of your BottomSheet using the key. Like as below
Declare GlobalKey with state in your currentWidget.
GloabalKey<BottomSheetWidgetState> closeKey = GloabalKey();
Declare one function closeBottomSheet in your bottomsheet widget state.
void closeBottomSheet(){ Navigator.of(conext).pop(); }
Call the function using the key.
closeKey.currentState.closeBottomSheet();
And done.
I am working on a location based app and in the settings page I have an option the allows users to change their location radius using the slider package. Such that when the slider is moved, more or less content gets shown in the home screen.
I tried sending the data using setstate but it seems not to be working for integer values
Am encountering the following problems
1.When the slider is moved, the changes dont reflect on the home page, I know this since ive inserted a text widget in the home screen to display the slider value.
2.On the settings page where the slider is, whenever I slide it, it updates but when I navigate to another screen then back to the settings screen, the slider resets to the minimum value.
How can I solve the above problems?
Here is my slider code on the settings page
Slider(
value: radius,
onChanged: (newRadius) async{
setState(() {
tasks = newRadius.toDouble();
String step2 = radius.toStringAsFixed(2);
twodecimalradius = double.parse(step2); //converting the slider value to 2 decimal places
});
print(twodecimalradius);
await Home(twodecimalradius:twodecimalradius); //passing the slider value to homepage
await Home.staticGlobalKey.currentState.getposts();//a function in the homepage that gets called whenever the slider value is changed
},
min: 10.0,
max: 500.0,
//activeColor: Colors.red[400],
)
And in my homepage this is the code that is supposed to display the slider value in a text widget
class Home extends StatefulWidget {
var twodecimalradius;
static final GlobalKey<_HomeState> staticGlobalKey =
new GlobalKey<_HomeState>();
Home({
this.twodecimalradius,
}): super(key: Home.staticGlobalKey);
Widget build(BuildContext context) {
return Scaffold(body:Text(widget.twodecimalradius))
}
I'm curious is there an easy way to add such functionality to my Flutter ListViews?
For example, I would like to trigger some animation on another widget when the user drags "over the edge" on the ListView with all of it's items scrolled to the top or bottom, that is when the curvy blue line indicating that we're at the end of the list appears.
It obviously has an event firing up to show those lines anyway.
It's not hard actually, you can use a custom ScrollController with a listener:
declare it, then in initState put:
_myController = ScrollController();
_myController.addListener(_myScrollListener);
and the function itself can be something like this:
_myScrollListener(){
if (_myController.offset >= _controller.position.maxScrollExtent && !_myController.position.outOfRange) {
print("List end");
}
if (_myController.offset <= _controller.position.minScrollExtent && !_myController.position.outOfRange) {
print("List top");
}
Use the controller with your ListView, add what you need to the listener.