Edit: I think i figured one part out - the child components (markers+modal) doesn't re-render the parent (map screen) when updated... It's funny though that it works in iOs.
Edit pt. 2 : Solved it! I lifted the function that opens to modal to parent and used lifted states to access it. Then it worked. Funny that it works without that in iOS but not in android, should be the same.
Question:
Im creating a game where you can open clues if you are in the right geographical place. It works in IOS, but in Android the modal does not open. It should open even if user is in correct geo place or not. If i set state "setModalVisible" to true I can see the modal. I can also see that the state changes to true when a clue/marker is clicked. Why doesnt the modal show? I have noticed that the "while" loop does not run (only when in android), but I still get the same error after I remove the while-loop and only have the location as await. Please help me!
This is the component in which the markers and the modal is:
...import ...
export function MapMarkersClues({ open }) {
///////////////
/// STATES ///
//////////////
const [isLoading, setIsLoading] = useState(true);
const [isLoadingLocation, setisLoadingLocation] = useState(true);
const [modalVisible, setModalVisible] = useState(false);
const [modalUpdate, setModalUpdate] = useState(false);
const [modalText, setModalText] = useState(
"Du måste gå närmare för att öppna ledtråden"
);
const [modalImage, setModalImage] = useState(Images.gameone.LockedClue);
const [modalImageTxt, setModalImageTxt] = useState("");
const [openedClue, setopenedClue] = useState(false);
const [markers, setMarkers] = useState([]);
const [markermodal, setmarkerModal] = useState([]);
const [CLUES, setClues] = useState(false);
const [cluesChanged, setCluesChanged] = useState(false);
////////////////////
/// STORAGE GET ///
//////////////////
const getData = async () => {
while (isLoading === true) {
try {
const jsonValue = await AsyncStorage.getItem("@clues_game_one");
setClues(JSON.parse(jsonValue) || CluesData);
if (CLUES != null && CLUES != false) {
setIsLoading(false);
}
} catch (e) {
console.log("error occured in getData:" + e);
}
}
};
useEffect(() => {
getData();
});
/////////////////
/// FUNCTIONS ///
/////////////////
// Getting Location when clue is opened
async function getLocation() {
while (isLoadingLocation === true) {
try {
setModalText("Ledtråden laddas...");
setModalImageTxt("");
setModalImage(Images.gameone.Otto);
const Loc = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.Low,
});
if (Loc != undefined) {
console.log(modalVisible);
setisLoadingLocation(false);
return Loc;
}
} catch (e) {
console.log(e);
}
}
}
// I can see here that the state changes as expected
useEffect(() => {
console.log(modalVisible);
}, [modalVisible]);
// Checking if clue should open or not and returning correct values.
const openClue = async (item) => {
setModalVisible(true);
if (item.icon === "unlock") {
setModalImage(Images.gameone[item.img]);
setModalImageTxt(item.imgTitle);
setModalText(item.text);
} else {
const Loc = await getLocation();
if (Loc != undefined) {
if (
Loc.coords.latitude >= item.lat - 0.00025 &&
Loc.coords.latitude <= item.lat + 0.00025 &&
Loc.coords.longitude >= item.long - 0.00025 &&
Loc.coords.longitude <= item.long + 0.00025
) {
setModalImage(Images.gameone[item.img]);
setModalImageTxt(item.imgTitle);
setModalText(item.text);
item.icon = "unlock";
const storeData = async (value) => {
try {
const jsonValue = JSON.stringify(value);
await AsyncStorage.setItem("@clues_game_one", jsonValue);
} catch (e) {
console.log(e);
}
};
storeData(CLUES);
setCluesChanged(true);
} else if (item.icon == "lock") {
setModalText("Du måste gå närmare för att öppna ledtråden");
setModalImageTxt("");
setModalImage(Images.gameone.LockedClue);
} else if (item.icon == "info-circle") {
setModalText("Du måste gå närmare för att läsa faktatexten");
setModalImageTxt("");
setModalImage(Images.gameone.LockedClue);
}
setModalUpdate(true);
}
}
};
// closes modal and changes states back to default.
const closeClue = () => {
console.log("close it");
setModalVisible(false);
setModalUpdate(false);
setisLoadingLocation(true);
if (cluesChanged) {
setopenedClue(!openedClue);
setCluesChanged(false);
}
};
//mapping out map-markers
useEffect(() => {
if (isLoading === false) {
setMarkers(
CLUES?.map((mark) => {
return (
<Marker
key={mark.key}
keyExtractor={(mark) => mark.key.toString()}
coordinate={{
latitude: mark.lat,
longitude: mark.long,
}}
onPress={() => openClue(mark)}
>
<FontAwesome name={mark.icon} size={29} color={mark.color} />
</Marker>
);
})
);
}
}, [openedClue, isLoading]);
//MODAL
useEffect(() => {
setmarkerModal(() => {
return (
<View style={styles.centeredView}>
<Modal
animationType='slide'
transparent={true}
visible={modalVisible}
onRequestClose={() => {
setModalVisible(!modalVisible);
}}
>
<View style={styles.centeredView}>
<View style={styles.modalView}>
<Pressable
style={[styles.button, styles.buttonClose]}
onPress={() => closeClue()}
>
<Text style={styles.textStyle}>✕</Text>
</Pressable>
<Image style={styles.img} source={modalImage} />
<Text style={styles.imgText}>{modalImageTxt}</Text>
<Text style={styles.modalText}>{modalText}</Text>
</View>
</View>
</Modal>
</View>
);
});
}, [modalVisible, modalUpdate]);
// returning Modal + Markers
if (open) {
return <View>{markermodal}</View>;
}
if (!open) {
return <>{markers}</>;
}
}
const styles = StyleSheet.create({
centeredView: {
flex: 1,
justifyContent: "flex-start",
alignItems: "center",
marginTop: 190,
marginLeft: 50,
marginRight: 50,
},
modalView: {
backgroundColor: "black",
borderRadius: 10,
padding: 35,
alignItems: "center",
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
button: {
borderRadius: 20,
padding: 8,
elevation: 2,
},
buttonClose: {
backgroundColor: "white",
},
textStyle: {
color: "black",
fontWeight: "bold",
textAlign: "center",
},
modalText: {
marginTop: 15,
color: "white",
textAlign: "center",
fontSize: 20,
},
img: {
width: 100,
height: 100,
marginTop: 25,
borderRadius: 3,
},
imgText: {
color: "white",
textAlign: "center",
},
});
And this is the screen with the map in which i use the components and decides what to render with the open prop.
import....
const MapScreen = ({ navigation }) => {
const [userLocation, setuserLocation] = useState({
latitude: 59.31858196433116,
longitude: 18.06945029502431,
latitudeDelta: 0.02,
longitudeDelta: 0.02,
});
const _onMapReady = useCallback(async () => {
const { granted } = await Location.requestForegroundPermissionsAsync();
if (!granted) {
throw new Error("Location permission not granted");
}
const {
coords: { latitude, longitude },
} = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.High,
timeInterval: 500,
distanceInterval: 10,
});
setuserLocation({
...userLocation,
latitude: latitude,
longitude: longitude,
});
}, [userLocation]);
useEffect(() => {
_onMapReady();
}, [_onMapReady]);
return (
<SafeAreaProvider style={styles.black}>
<View style={styles.container}>
<Timer timer={true} />
<MapView
provider={MapView.PROVIDER_GOOGLE}
style={styles.map}
initialRegion={userLocation}
showsIndoors={true}
zoomControlEnabled={true}
zoomEnabled={true}
zoomTapEnabled={true}
showsBuildings={true}
toolbarEnabled={false}
showsUserLocation={true}
onMapReady={_onMapReady}
showsMyLocationButton={true}
customMapStyle={mapStyle}
loadingEnabled={true}
followUserLocation={false}
>
<MapMarkersClues open={false} trackViewChanges={false} />
</MapView>
<MapMarkersClues open={true} styles={viewStyles} />
</View>
</SafeAreaProvider>
);
};
export default MapScreen;
const percent = Dimensions.get("window").height / 100;
const viewStyles = StyleSheet.create({
container: {
backgroundColor: "green",
width: 30,
height: 5,
borderColor: "red",
borderStyle: "solid",
borderWidth: 3,
position: "absolute",
zIndex: 100000,
},
});
const markerStyles = StyleSheet.create({
container: {
border: "2px solid red",
position: "absolute",
zIndex: 1,
},
});
const styles = StyleSheet.create({
container: {
position: "relative",
backgroundColor: "black",
bottom: 0,
zIndex: -10,
},
map: {
zIndex: 0,
width: Dimensions.get("window").width,
height: percent * 76,
},
black: {
backgroundColor: "black",
},
});
Ive searched and searched but I cannot understand the problem. Any suggestions?