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);
Related
I am using GetX in my project. Lets suppose I have two screens. Each screen have a button.
The controller of first screen have observable list of objects. When a button in first screen is pressed, one of the object list is sent to second screen.
In second screen user changes the value of the data received and when save button is pressed, I want to update the observable list in first screen as well.
I know I can use Get.back() to send updated data back to first screen. I could do following in second screen
Get.back(result: [
{"updatedObject": listDetails}
]);
And in first screen I could do
Get.to(() => SecondScreen(), arguments: [
{"list": listDetails[index]}
]).then((result) {
// result[0]["updatedObject"] // use this to update list observable
});
My Question.
Can I update list in first screen without navigating back to first screen when Save button is pressed.
I am pretty new with GetX and i am trying to learn it. Thanks
As long that when you navigate to the second screen using Get.to() then you can always access the data in the controller of the first screen using Get.find().
Here's a sample I tweaked it a little bit:
Get.to(() => SecondScreen(), arguments:
{"index": index},
)
On the controller on the second screen you can access(view/update) the data using the index.
#override
void onInit() {
index = Get.arguments['index'];
// View
name = Get.find<FirstScreenController>().listDetails[index].name;
print(name);
// Update
Get.find<FirstScreenController>().listDetails[index].name = "John";
}
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 working on an app that gets the user to take a list of measurements. I use ListView to display a list of measurements. When the the user clicks on a list item it takes them to a new page, they enter the measurement, hit save and then in the save method I do Navigator.pop(context) back to the list. It all works but there is a usability problem.
If you tap a list tile and then use the app bar to go back it returns to the same scroll position in the ListView. If you enter some data then hit Save it returns to the top of the list. Even though I'm using Navigator.pop(context) in the save method. You can imagine returning to the top each time is pretty painful when the list of requirement measurements is quite long.
I guess is maybe its something to do with the fact that in the Save method I also update the model with which the list is built on so its kind of no longer the same list??
EDIT
I'm still not getting there and now I have an issue where the itemScrollController is not attached when I want to call it. Some code will hopefully help:
class ListContents extends StatefulWidget {
ListContents({
Key key,
}) : super(key: key);
#override
_ListContentsState createState() => _ListContentsState();
}
class _ListContentsState extends State<ListContents> {
ItemScrollController itemScrollController;
#override
void initState() {
super.initState();
itemScrollController = ItemScrollController();
}
I set up the scroll controller in init state. I set up a button to test this. I can run this when the page loads:
void jump(jumpIndex) {
itemScrollController.jumpTo(index: jumpIndex);
}
When I click that button it will jump to what ever index is passed.
I need to do this jumpTo when popping back from the previous page. I have this code in the list tiles. This loads an input page. - I was hoping to run the jumpTo method after it's popped.
onTapped: () async {
await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
ChangeNotifierProvider<MeasureInstance>.value(
value: _instance,
child: InputOne(
index: index,
),
),
),
);
await _database.setInstance(_instance);
print(itemScrollController.isAttached);
itemScrollController.jumpTo(index: index);
},
When I pop back to the list page I see 'false' printed in the console and then an error that _jumpTo was called on null
I have a main widget that nest home page widget and setting page widget. inside setting, will have a nest pageView widget
main:
home
setting
pageView
I have a main_bloc to house some interactive data selected_page_index, to set the initialPage of the pageView when navigate to the setting page. Below are my code for the setting page:
class _SettingPageState extends State<SettingPage> {
MainBloc _mainBloc;
PageController facilityBookingPageviewController;
#override
void initState() {
super.initState();
_mainBloc = BlocProvider.of<MainBloc>(context);
print(_mainBloc.selected_page_index); ///<-- this give me the correct value
facilityBookingPageviewController = PageController(
initialPage: _mainBloc.selected_page_index,
);
}
I can print() out the correct value in the initState(), but somehow the initialPage will only get the _mainBloc.selected_page_index original preset value.
I believe I done something wrong in terms of timing. How to properly do this?
The controller.currentPage returns a double value. For example, when the page is being swiped the value goes from 1 to 2 gradually and does not instantly jump to 2. If use it jump second page directly. currentPage could use instead of initialPage if your _mainBloc.selected_page_index value is a double. Try this and let me know if it is worked for you
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();