Flutter sending integer values from one page to another - flutter

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))
}

Related

PageView jumpTo without dismissing animation / input of user

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);

How to prevent subtree with callback from rerendering in Flutter?

I'm new to Flutter and have some performance concerns. For my app, I have created a custom sidebar menu, inspired by
For this purpose, I created a stateful top-level widget that acts as the parent screen. It contains a Stack widget with the navigation screen on the button, and a content screen on top. The user should be able to open/close the menu in two ways:
By pressing the hamburger menu icon at the top left of the content screen (either when it is fully opened, or moved to the side as in the first pic)
By swiping right when the menu is open, and left when the menu is closed.
To satisfy point 2, I added a GestureDetector on the parent screen, such that the swipes are detected in the entire screen, which animates the content screen to the side/back in full view. To satisfy point 1, I pass an onPress callBack to the content screen (which passes it to the hamburger iconButton), which also does the top level animation. However, reading the documentation (stateful performance considerations), it seems that such a top-level stateful widget can be harmful for performance, as the rebuild passes down. I can't make my content screen a const widget (which is a proposed solution) because of the callback. This is obviously suboptimal, since in the content screen, only the icon has an animated change when the menu opens (the icon changes from a hamburger to an arrow).
How can I minimize the number of rerenders in the subtree? Is there a way to pass the screen as a const widget, even though it has a callback? Or is the current approach satisfactory?
The code, as I have it currently, is as follows:
class ParentScreen extends StatefulWidget {
const ParentScreen({Key? key}) : super(key: key);
#override
_ParentScreenState createState() => _ParentScreenState();
}
class _ParentScreenState extends State<ParentScreen> {
bool isMenuOpen = false;
double xOffset = 0;
double yOffset = 0;
double rotationAngle = 0;
double scaleFactor = 1;
double toRadians(double degrees) => degrees * math.pi / 180.0;
void animateMenu() {
setState(() {
...
isMenuOpen = !isMenuOpen;
});
}
#override
Widget build(BuildContext context) {
return SafeArea(
// Detect user swipe to navigate between the screens
child: GestureDetector(
onHorizontalDragEnd: (DragEndDetails details) {
if (details.primaryVelocity != null) {
if (details.primaryVelocity! > 0) {
// Right swipe, close menu if open
if (isMenuOpen) animateMenu();
} else if (details.primaryVelocity! < 0) {
// Left swipe, open menu if closed
if (!isMenuOpen) animateMenu();
}
}
},
child: Scaffold(
body: Stack(
children: <Widget>[
const DrawerScreen(), // Screen with navigation information
AnimatedContainer(
transform: Matrix4.translationValues(xOffset, yOffset, 0)
..scale(scaleFactor)
..rotateZ(toRadians(rotationAngle)),
duration: const Duration(milliseconds: 300),
child: HomeScreen( // Screen with custom content
onMenuPress: animateMenu,
),
),
],
),
),
),
);
}
}
Well, you don't need to make the parent widget a stateful widget.
First make the actual menu including it's animation it's on widget which draws over everything else.. (Similar to what you said in a Stack widget).
Then create a object (typically called a BLoC in flutter-world) which lives outside the widget tree - either a ChangeNotifier or a Stream and inject it into the stateless widgets (easiest is by using the provider package, but you can also use an InheritedWidget.
When you want to show the menu you would just change the state of this external object which will notify the menu widget to expand.

Flutter PopupMenuButton - don't close the menu when selected

I have a list of checkbox items (PopupMenuItem) inside my popup menu, which is triggered by a popupMenuButton. I want the user to be able to select a number of checkboxes, but as soon as one item is selected it closes the window.
Is there any way to prevent this? I need it to stay open, or alternatively force it open again immediately.
(I tried creating my own PopupItem class to override the "handleTap()", but I need to update the state of the parent menu view, which I can't call from another class. So Ive removed that again.)
class TopicsNotificationMenu extends StatefulWidget {
List<Topic> topics = [];
TopicsNotificationMenu(this.topics);
#override
_TopicsNotificationMenuState createState() =>
_TopicsNotificationMenuState();
}
class _TopicsNotificationMenuState extends State<TopicsNotificationMenu> {
_TopicsNotificationMenuState();
_updateTopics(_tp){
setState(() {
if(_tp.value == true){
_tp.value = false;
}else{
_tp.value = true;
_registerTopic(_tp.name);
}
});
}
#override
Widget build(BuildContext context) {
return PopupMenuButton(
onSelected: (value) {
_updateTopics(value);
},
itemBuilder: (BuildContext context) {
return widget.topics.map((var tp) {
var _icon = (tp.value == true) ? Icons.check_box : Icons.check_box_outline_blank;
return PopupMenuItem(
value: tp,
child: ListTile(
leading: Icon(_icon),
title: Text(tp.name),
),
);
}).toList();
});
}
You can try to reopen it each time after the user's selection. I made an example here
Alternatively, I would advise creating your own widget with the desired behaviour.
I had to create my own widget for this. In summary, I wanted a floating list in the top-right of my screen with a list of checkboxed items. When I press the items they become checked/unchecked but the window remains open until I click off it. I used the following widget tree:
An OverlayEntry widget so that I could place it anywhere floating above the app
-> added the SafeArea widget so that my padding would include the notification bar at the top of the phone
-> a Gesture Detector, so that on tapping off it I could close it
-> a column with CrossAxisAlignment.end so that it was placed in the top-right
-> a container widget with some padding
-> a Material for elevation shading and to contain a list
-> The Listview
-> The List Tiles with icon and Text for each item. The icon was either the ticked or unticked graphic, depending on it's value (which is stored as a boolean in an array)
onTap of a ListTile it updated the state of the widget and dispayed it again. There is no visual blinking, it is instant.
onTap of the Gesture Detector it simply removes the widget.

Flutter ListView resetting to top of list

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

flutter bloc - get dynamic data in bloc from new page

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