How to preserve selections of user during screen transitions in Flutter - flutter

I have a bottom navigation bar which includes home button, screen1 button and screen2 button. User is making some selections on home screen.
After that he taps the screen1 button on bottom navigation bar and goes to screen 1.
Then in order to return home screen, he taps home button. But home screen rebuild and all information has gone.
I tried AutomaticKeepAliveClientMixin but it is not working. My code sample is below. Is there any mistake here?
class _HomeScreenState extends State<HomeScreen>
with AutomaticKeepAliveClientMixin {
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
super.build(context);
return Scaffold(

Related

Refresh homescreen in flutter when user comes back to the app after leaving the application..App is still in RAM of the phone

I have a single screen application which .For now, When a user has left the application for less than a minute or so, the app is still on the phones ram and the user can pick from where he left.
I want a feature where, when the user comes back to the app screen after leaving the app, the screen can refresh because contents change..
Assuming that you are using a stateful widget add a widgetbinding observer to it
class _yourClassState extends State<YourClass>
with WidgetsBindingObserver {
// Here you can override initstate, build etc
}
Now widgetBindingObserver will also let you override didChangeAppLifeCycleState which will allow you to capture the state of the app.
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.resumed:
print("app in resumed from background"); //you can add your codes here
break;
case AppLifecycleState.inactive:
print("app is in inactive state");
break;
case AppLifecycleState.paused:
print("app is in paused state");
break;
case AppLifecycleState.detached:
print("app has been removed");
break;
}
}

Lock landscape, enable only on specific views

I want to force portrait mode in my app, I am using the settings below. However I have a view, that I must enable landscape on, is there way for this to only work on all views except specific one that I want? I don't want to paste this in every widget throughout the app.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
On the view I want landscape, I have an if that checks if the view is landscape or portrait, but it wont render to landscape with settings above.
Let's say your app is completely in "Portrait". What you had will work. Now, you want a specific screen to be either portrait/landscape or only landscape. Then you can configure separately for that one screen (say Screen2).
You are in Screen1 and want to go to Screen2 (which should be in landscape).
// screen2
#override
void initState(){
super.initState();
setLandscape(); // set to portrait
}
#override
dispose(){
setPortrait(); // set to portrait
super.dispose();
}
// function changes to landscape
setLandscape() {
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeRight,
DeviceOrientation.landscapeLeft,
// below 2 lines are based on your requirement
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
}
// function changes to portrait
setPortrait() {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
}
If you don't have to navigate to new screen, this would work fine
But problem would be when navigating to the Screen3 from Screen2 where Screen3 should be portrait (not landscape). You can change it when navigating and revert it back to landscape when popping.
// screen2
// navigates safely to next screen changing to portrait and
// reverts to landscape when popped to this screen
navigateTo(<navigate screen>) {
setPortrait();
Navigator.of(context)
.push(<navigate screen>)
.then(() => setLandscape()); // then completes when pop() returns to this page
}

hiding Tabbar during navigation

My main screen has got AppBar and BottomNavigationBar. if I navigate to screen 2 BottomNavigationBar is staying. It is perfect for me.
But I would like to remove the Tabbar for screen 2. Because the content of Screen 2 is irrelevant with the TabbarMenu.
I am going to screen 2 from screen 1 return Page2 like this:
Widget showPage(int selectedPage) {
if (selectedPage == 0) {
return const NewsView(id: '1');
} else if (selectedPage == 1) {
return const Settings();
any help?

Flutter TabBar how to detect or listen for the being of drag or swipe?

I have a tab bar with a controller that has a Listener to handle the on tab and user drag/swipe the issue is that I need to know right at the being of the drag so I can start to make the active icon changes with the current version the active icon only changes at the end of the swipe so there is a delay.
Here is my code
In the init state
// For tab selection
tabController = TabController(vsync: this, length: 4);
tabController.addListener(_handleTabSelection);
// Handles the tab section with on tab and drag
void _handleTabSelection() {
setState(() {
_selectedIndex = tabController.index;
});
}

Dismiss keyboard on swipe back gesture in Flutter app

I am trying to dismiss the keyboard when the user swipes from the edge to pop route.
Currently the keyboard doesn't dismiss until the route is completely gone popped, messing up some of the other pages layout until it dismisses
I did try to use a WillPopScope to determine when the user was going to pop the route, but unfortunately this disables the swipe to pop functionality from iOS or the CupertinoPageRoute.
I just want to find out if there's anyway I can determine when the user swipes from the edge to pop or taps the back button on the appBar and dismiss the keyboard as they do so.
If possible, I am trying to dismiss keyboard as soon as they start swiping to pop, as it happens in many apps.
I am attaching attaching a gif showing the effect I'm trying to achieve.
As suggested by Ovidiu
class DismissKeyboardNavigationObserver extends NavigatorObserver {
#override
void didStartUserGesture(Route route, Route previousRoute) {
SystemChannels.textInput.invokeMethod('TextInput.hide');
super.didStartUserGesture(route, previousRoute);
}
}
and in your Material App
MaterialApp(
navigatorObservers: [DismissKeyboardNavigationObserver()],
)
You need to create a custom class extending NavigatorObserver, and pass an instance of it to the navigatorObservers property of your MaterialApp or CupertinoApp.
Within that custom class, you can override didStartUserGesture and didStopUserGesture, which will be called when the swipe gesture starts/ends. This should allow you to achieve the behavior you are looking for. Note that didStartUserGesture indicates the current route as well as the previous route, based on which you could add logic to determine whether the keyboard should be dismissed or not.
This should come naturally and you shouldn't be directly concerned with that because actually, when you pop a route with the keyboard on, it should dismiss properly.
However, if you want to detect when the user starts swiping and dismiss the keyboard along with it and then pop the current route, you can easily achieve it by wrapping your screen widget with a GestureDetector like so:
Widget build(BuildContext context) {
double dragStart = 0.0;
return GestureDetector(
onHorizontalDragStart: (details) => dragStart = details.globalPosition.dx,
onHorizontalDragUpdate: (details) {
final double screenWidth = MediaQuery.of(context).size.width;
// Here I considered a back swipe only when the user swipes until half of the screen width, but you can tweak it to your needs.
if (dragStart <= screenWidth * 0.05 && details.globalPosition.dx >= screenWidth) {
FocusScope.of(context).unfocus();
}
child: // Your other widgets...
},
this is something i wrote to handle this issue. doesnt use any external packages, you would just wrap your content in the main function at the top.
Widget swipeOffKeyboard(BuildContext context, {Widget? child}) {
return Listener(
onPointerMove: (PointerMoveEvent pointer) {
disKeyboard(pointer, context);
},
child: child, // your content should go here
);
}
void disKeyboard(PointerMoveEvent pointer, BuildContext context) {
double insets = MediaQuery.of(context).viewInsets.bottom;
double screenHeight = MediaQuery.of(context).size.height;
double position = pointer.position.dy;
double keyboardHeight = screenHeight - insets;
if (position > keyboardHeight && insets > 0) FocusManager.instance.primaryFocus?.unfocus();
}