'Flutter : How to not rebuild screen from start in bottom navigation bar and save the last state?
How to not rebuild the screen from start in the bottom navigation bar and saving the last state of it and if the state of the screen is changed like the driver app has 4 screens and I want the screen of home that contains button Offline Now or Online Now not change the state if I move to another screen, I used a Tabviewbar
What is the solution?
Main Screen class
import 'package:drivers_app/tabsPages/earnings_tab_screen.dart';
import 'package:drivers_app/tabsPages/home_tab_screen.dart';
import 'package:drivers_app/tabsPages/profile_tab_screen.dart';
import 'package:drivers_app/tabsPages/zones_tab_page.dart';
import 'package:flutter/material.dart';
class MainScreen extends StatefulWidget
{
static const String mainScreenId = "mainScreenId";
@override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> with SingleTickerProviderStateMixin
{
TabController? tabController;
int selectedIndex = 0;
void onItemClicked(int index)
{
setState(()
{
selectedIndex = index;
tabController!.index = selectedIndex;
});
}
@override
void initState() {
// TODO: implement initState
super.initState();
tabController = TabController(length: 4, vsync: this);
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
tabController!.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold
(
body: TabBarView(
physics: NeverScrollableScrollPhysics(),
controller: tabController,
children:
[
HomeScreen(),
EarningsScreen(),
ZonesScreen(),
ProfileScreen(),
],
//index: selectedIndex,
),
bottomNavigationBar: BottomNavigationBar(
items:
[
BottomNavigationBarItem(
icon: Icon(
Icons.home,
),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(
Icons.credit_card,
),
label: 'Earnings',
),
BottomNavigationBarItem(
icon: Icon(
Icons.settings_input_antenna_rounded,
),
label: 'Zones',
),
BottomNavigationBarItem(
icon: Icon(
Icons.person,
),
label: 'Account',
),
],
unselectedItemColor: Colors.black54,
selectedItemColor: Colors.blue,
type: BottomNavigationBarType.fixed,
selectedLabelStyle: TextStyle(
fontSize: 12.0
),
showUnselectedLabels: true,
currentIndex: selectedIndex,
onTap: onItemClicked,
),
);
}
}
Home Screen Class
import 'package:drivers_app/shared/components/components.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:flutter_geofire/flutter_geofire.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
class HomeScreen extends StatefulWidget
{
static final CameraPosition _kGooglePlex = CameraPosition(
target: LatLng(37.42796133580664, -122.085749655962),
zoom: 15,
);
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
Completer<GoogleMapController> _controllerGoogleMap = Completer();
GoogleMapController? newcontrollerGoogleMap;
var geoLocator = Geolocator();
String driverStatusText = 'Offline Now - Go Online ';
Color driverStatusColor = Colors.black;
bool isDriverAvailable = false;
@override
void initState()
{
super.initState();
getCurrentDriverInfo();
}
void locatePosition() async
{
Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
currentPosition = position;
LatLng latLatPosition = LatLng(position.latitude, position.longitude);
CameraPosition cameraPosition = CameraPosition(target: latLatPosition, zoom: 14);
newcontrollerGoogleMap!.animateCamera(CameraUpdate.newCameraPosition(cameraPosition));
String Address = await AssistantMethods.searchCoordinateAddress(position, context);
print("This is your Address :: " + Address);
}
void getCurrentDriverInfo() async
{
currentfirebaseUser = await FirebaseAuth.instance.currentUser;
driversRef.child(currentfirebaseUser!.uid).once().then((DataSnapshot dataSnapshot)
{
if(dataSnapshot.value != null)
{
driversInformation = Drivers.fromSnapshot(dataSnapshot);
}
});
PushNotificationService pushNotificationService = PushNotificationService();
pushNotificationService.initialize(context);
pushNotificationService.getToken().toString();
AssistantMethods.retrieveHistoryInfo(context);
}
@override
Widget build(BuildContext context)
{
return Stack(
children:
[
GoogleMap(
mapType: MapType.normal,
myLocationButtonEnabled: true,
initialCameraPosition: HomeScreen._kGooglePlex,
myLocationEnabled: true,
onMapCreated: (GoogleMapController controller) {
_controllerGoogleMap.complete(controller);
newcontrollerGoogleMap = controller;
_determinePosition();
},
),
//online offline driver container
Container(
height: 140.0,
width: double.infinity,
color: Colors.black54,
),
Positioned(
top: 60.0,
left: 0.0,
right: 0.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children:
[
Padding(padding: EdgeInsets.symmetric(
horizontal: 16.0,
),
child: MaterialButton(
onPressed: ()
{
if(isDriverAvailable != true)
{
makeDriverOnlineNow();
getLocationLiveUpdates();
setState(()
{
driverStatusColor = Colors.green;
driverStatusText = 'Online Now ';
isDriverAvailable = true;
});
displayToast('you are Online Now', context);
}
else
{
displayToast('you are Offline Now', context);
setState(()
{
driverStatusColor = Colors.black;
driverStatusText = 'Offline Now - Go Online ';
isDriverAvailable = false;
});
makeDriverOfflineNow();
Appclosed();
}
},
color: driverStatusColor,
child: Padding(
padding: EdgeInsets.all(17.0,),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
driverStatusText,
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
Icon(
Icons.phone_android,
color: Colors.white,
size: 26.0,
),
],
),
),
),
),
],
),
),
],
);
}
Main class
import 'package:drivers_app/config_maps.dart';
import 'package:drivers_app/modules/car_info_screen.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:drivers_app/Data_Handler/app_Data.dart';
import 'package:drivers_app/modules/login_screen.dart';
import 'package:drivers_app/modules/main_screen.dart';
import 'package:provider/provider.dart';
import 'package:drivers_app/modules/register_screen.dart';
//Receive message when app is in background solution for on message
Future<void> backgroundHandler(RemoteMessage message) async
{
print(message.data.toString());
print(message.notification!.title);
}
void main() async{
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(backgroundHandler);
currentfirebaseUser = FirebaseAuth.instance.currentUser;
runApp(MyApp());
}
DatabaseReference adminRef = FirebaseDatabase.instance.reference().child("admin");
DatabaseReference driversRef = FirebaseDatabase.instance.reference().child("drivers");
DatabaseReference newRequestRef = FirebaseDatabase.instance.reference().child("Ride Request");
DatabaseReference rideRequestRef = FirebaseDatabase.instance.reference().child("drivers").child(currentfirebaseUser!.uid).child("newRide");
DatabaseReference availableDriverRef = FirebaseDatabase.instance.reference().child("availableDrivers");
DatabaseReference availableDriverRef2 = FirebaseDatabase.instance.reference().child("availableDrivers2").child(currentfirebaseUser!.uid);
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create:(context) => AppData(),
child: MaterialApp(
title: 'Driver App',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
initialRoute: FirebaseAuth.instance.currentUser == null ? LoginScreen.loginScreenId : MainScreen.mainScreenId,
routes:{
RegisterScreen.registerScreenId: (context)=> RegisterScreen(),
LoginScreen.loginScreenId: (context)=> LoginScreen(),
MainScreen.mainScreenId: (context)=> MainScreen(),
CarInfoScreen.carinfoScreen: (context)=> CarInfoScreen(),
},
),
);
}
}
Solution 1:[1]
You can use a PageView and AutomaticKeepAliveClientMixin on each page/portion (item in the PageView) and set wantToKeepAlive to true.
For example:
If we have 2 portions, HomePortion and SettingsPortion like so:
class HomePortion extends StatefulWidget {
const HomePortion({Key? key}) : super(key: key);
@override
_HomePortionState createState() => _HomePortionState();
}
class _HomePortionState extends State<HomePortion>
with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
return const Center(
child: Text("Home"),
);
}
@override
bool get wantKeepAlive => true;
}
class SettingsPortion extends StatefulWidget {
const SettingsPortion({Key? key}) : super(key: key);
@override
_SettingsPortionState createState() => _SettingsPortionState();
}
class _SettingsPortionState extends State<HomePortion>
with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
return const Center(
child: Text("Settings"),
);
}
@override
bool get wantKeepAlive => true;
}
Notice
@override bool get wantKeepAlive => true;in both portions.
We can then use them in the Page like so:
class DashboardPage extends StatefulWidget {
const DashboardPage({Key? key}) : super(key: key);
@override
_DashboardPageState createState() => _DashboardPageState();
}
class _DashboardPageState extends State<DashboardPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
onTap: (index) {
_controller.jumpToPage(index);
setState(() {
_pageIndex = index;
});
},
currentIndex: _pageIndex,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home_rounded),
),
BottomNavigationBarItem(
icon: Icon(Icons.settings_rounded),
),
],
),
body: PageView(
children: const [
HomePortion(),
SettingsPortion(),
],
),
);
}
int _pageIndex = 0;
PageController _controller = PageController();
}
Solution 2:[2]
in your code
class MainScreen extends StatefulWidget
{
static const String mainScreenId = "mainScreenId";
@override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> with SingleTickerProviderStateMixin
{
TabController? tabController;
int selectedIndex = 0;
bool online = false;
void onItemClicked(int index)
{
setState(()
{
selectedIndex = index;
tabController!.index = selectedIndex;
});
}
@override
void initState() {
// TODO: implement initState
super.initState();
tabController = TabController(length: 4, vsync: this);
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
tabController!.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold
(
appbar: AppBar(
actions:[
Text("online "),
Switch(
onChanged:(value){setState((){
online = value
});} ,
value: online
)]
),
body: TabBarView(
physics: NeverScrollableScrollPhysics(),
controller: tabController,
children:
[
HomeScreen(),
EarningsScreen(),
ZonesScreen(),
ProfileScreen(),
],
//index: selectedIndex,
),
bottomNavigationBar: BottomNavigationBar(
items:
[
BottomNavigationBarItem(
icon: Icon(
Icons.home,
),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(
Icons.credit_card,
),
label: 'Earnings',
),
BottomNavigationBarItem(
icon: Icon(
Icons.settings_input_antenna_rounded,
),
label: 'Zones',
),
BottomNavigationBarItem(
icon: Icon(
Icons.person,
),
label: 'Account',
),
],
unselectedItemColor: Colors.black54,
selectedItemColor: Colors.blue,
type: BottomNavigationBarType.fixed,
selectedLabelStyle: TextStyle(
fontSize: 12.0
),
showUnselectedLabels: true,
currentIndex: selectedIndex,
onTap: onItemClicked,
),
);
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Josteve |
| Solution 2 | ismailfarisi |
