I have a class Home
with a PageView as body and a BottomNavigationBar.
In this class the current user and the current location of the user is loaded.
When the user and the location is known, a global variable is set to true
On the first tab icon of the BottomNavigationBar there is a feed of nearby locations coded in class Feed
Now the issue.
When I start the app for the first time or make a hot reload geoQuery()
returns the circular spinner. When the current user is loaded it returns the text "No Data" instead of showing the events. The user needs to change the tab of BottomNavigationBar from feed to something else and back to feed to refresh the streambuilder. After that it works as expected.
When I use the streambuilder without the condition (currentLocationloaded && currentUserloaded == true) it works as expected but sometimes it throws an error as the user is not loaded fast enough.
What can I do to get it work with condition?
Update
Workflow logged in: RootPage -> Logged in? -> Home
RootPage
enum AuthStatus {
NOT_DETERMINED,
NOT_LOGGED_IN,
LOGGED_IN,
}
class RootPage extends StatefulWidget {
RootPage({this.auth});
final BaseAuth auth;
@override
State<StatefulWidget> createState() => new _RootPageState();
}
class _RootPageState extends State<RootPage> {
AuthStatus authStatus = AuthStatus.NOT_DETERMINED;
String _userID = "";
@override
void initState() {
super.initState();
widget.auth.getCurrentUser().then((user) {
setState(() {
if (user != null) {
_userID = user?.uid;
}
authStatus =
user?.uid == null ? AuthStatus.NOT_LOGGED_IN : AuthStatus.LOGGED_IN;
});
});
}
void loginCallback() {
widget.auth.getCurrentUser().then((user) {
setState(() {
_userID = user.uid.toString();
});
});
setState(() {
authStatus = AuthStatus.LOGGED_IN;
});
}
void logoutCallback() {
setState(() {
authStatus = AuthStatus.NOT_LOGGED_IN;
_userID = "";
});
}
Widget buildWaitingScreen() {
return Scaffold(
body: Container(
alignment: Alignment.center,
child: CircularProgressIndicator(),
),
);
}
@override
Widget build(BuildContext context) {
switch (authStatus) {
case AuthStatus.NOT_DETERMINED:
return buildWaitingScreen();
break;
case AuthStatus.NOT_LOGGED_IN:
return new StartPage(
auth: widget.auth,
loginCallback: loginCallback,
);
break;
case AuthStatus.LOGGED_IN:
if (_userID.length > 0 && _userID != null) {
return new Home(
userID: _userID,
auth: widget.auth,
logoutCallback: logoutCallback,
);
} else
return buildWaitingScreen();
break;
default:
return buildWaitingScreen();
}
}
}
Home
User currentUser;
bool currentUserloaded = false;
bool currentLocationloaded = false;
class Home extends StatefulWidget {
final BaseAuth auth;
final VoidCallback logoutCallback;
final String userID;
const Home({Key key, this.auth, this.logoutCallback, this.userID})
: super(key: key);
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
final _scaffoldKey = GlobalKey<ScaffoldState>();
PageController pageController;
int pageIndex = 0;
double longitude;
double latitude;
//INIT
@override
void initState() {
super.initState();
loadCurrentUser();
getCurrentLocation();
pageController = PageController();
}
//LOAD current user
loadCurrentUser() async {
print("Current User ${widget.userID}");
DocumentSnapshot doc = await userRef.document(widget.userID).get();
currentUser = User.fromDocument(doc);
setState(() {
currentUserloaded = true;
print("User loaded $currentUserloaded");
});
}
//get current location
getCurrentLocation() async {
var currentLocationCoordinates = await Geolocator()
.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
List<Placemark> place = await Geolocator().placemarkFromCoordinates(
currentLocationCoordinates.latitude,
currentLocationCoordinates.longitude);
latitude = currentLocationCoordinates.latitude;
longitude = currentLocationCoordinates.longitude;
setState(() {
currentLocationloaded = true;
print("Got location $currentLocationloaded");
});
}
//DISPOSE
@override
void dispose() {
pageController.dispose();
super.dispose();
}
//Pageview
onPageChanged(int pageIndex) {
setState(() {
this.pageIndex = pageIndex;
});
}
//On Tap of ButtomTabbar => Jump to next Page
onTap(int pageIndex) {
if (currentUserloaded && currentLocationloaded) {
pageController.jumpToPage(pageIndex);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
body: PageView(
children: <Widget>[
Feed(userID: widget.userID, latitude: latitude, longitude: longitude),
SearchView(),
ChatHome(),
Profile(
uid: currentUser?.uid,
auth: widget.auth,
logoutCallback: widget.logoutCallback),
],
controller: pageController,
onPageChanged: onPageChanged,
physics: NeverScrollableScrollPhysics(),
),
bottomNavigationBar: CupertinoTabBar(
currentIndex: pageIndex,
inactiveColor: Colors.white,
backgroundColor: Colors.blue,
activeColor: Colors.orange,
onTap: onTap,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home, size: 20),
title: Text("Home"),
),
BottomNavigationBarItem(
icon: Icon(Icons.search, size: 20),
title: Text("Search"),
),
BottomNavigationBarItem(
icon: Icon(Icons.chat, size: 20),
title: Text("chat"),
),
BottomNavigationBarItem(
icon: Icon(Icons.profil, size: 20),
title: Text("Profil"),
),
]),
);
}
}
Feed
class Feed extends StatefulWidget {
final String userID;
final double latitude;
final double longitude;
const Feed({Key key, this.userID, this.latitude, this.longitude})
: super(key: key);
@override
_FeedState createState() => _FeedState();
}
class _FeedState extends State<Feed> {
final _scaffoldKey = GlobalKey<ScaffoldState>();
List<Event> events = [];
var radius = BehaviorSubject<double>.seeded(50.0);
Stream<List<DocumentSnapshot>> stream;
Geoflutterfire geo;
@override
void initState() {
super.initState();
geo = Geoflutterfire();
GeoFirePoint center = geo.point(
latitude: widget.latitude,
longitude: widget
.longitude);
stream = radius.switchMap((rad) {
var collectionReference =
eventRef.where("event", isEqualTo: "festival");
return geo.collection(collectionRef: collectionReference).within(
center: center, radius: rad, field: 'position', strictMode: true);
});
}
//GEOQUERY
Widget geoQuery() {
if (currentLocationloaded && currentUserloaded) {
return Column(
children: <Widget>[
StreamBuilder(
stream: stream,
builder: (BuildContext context,
AsyncSnapshot<List<DocumentSnapshot>> snapshot) {
if (!snapshot.hasData) {
Text("No data");
}
events =
snapshot.data.map((doc) => Event.fromDocument(doc)).toList();
events.sort((a, b) {
var aDate = a.timestamp;
var bDate = b.timestamp;
return aDate.compareTo(bDate);
});
if (events.isEmpty) {
return Text("No events");
}
return Flexible(
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return buildEvent(index);
},
),
);
},
)
],
);
} else {
return circularProgress();
}
}
@override
Widget build(BuildContext context) {
SizeConfig().init(context);
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
centerTitle: true,
title: Text("Feed"),
backgroundColor: Colors.blue,
),
body: geoQuery(),
);
}
}
Update 2
If I use hard coded latitude and longitude for
GeoFirePoint center = geo.point(latitude: 37.773972, longitude: -122.431297);
it works!
Looks like an issue with passing the current user location.
Any suggestions?
The issue was that the location of current user was not passed on time. Just put
GeoFirePoint center = geo.point(
latitude: widget.latitude,
longitude: widget
.longitude);
stream = radius.switchMap((rad) {
var collectionReference =
eventRef.where("event", isEqualTo: "festival");
return geo.collection(collectionRef: collectionReference).within(
center: center, radius: rad, field: 'position', strictMode: true);
});
from initState to geoQuery()