Currently learning Flutter and got this error while trying to detect the location of my device:
A value of type 'StreamSubscription' can't be assigned
to a variable of type 'StreamSubscription>'
I was following an online tutorial but somehow got this error.
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:location/location.dart';
import 'dart:async';
import 'package:flutter/services.dart';
class MainPage extends StatefulWidget {
#override
State<StatefulWidget> createState() => AppState();
}
class AppState extends State<MainPage> {
Map<String,double> currentLocation = new Map();
StreamSubscription<Map<String,double>> locationSubscription;
var location = new Location();
String error;
void initState() {
super.initState();
currentLocation['latitude'] = 0.0;
currentLocation['longitude'] = 0.0;
initPlatformState();
locationSubscription =
location.onLocationChanged().listen((Map<String,double> result) {
setState(() {
currentLocation = result;
});
});
}
void initPlatformState() async{
Map<String,double> myLocation;
try {
myLocation = await location.getLocation();
error="";
} on PlatformException catch(e) {
if(e.code == 'PERMISSION_DENIED')
error = "permission denied";
else if(e.code == "PERMISSION_DENIED_NEVER_ASK")
error = "permission denied";
myLocation = null;
}
setState(() {
currentLocation = myLocation;
});
}
#override
Widget build (BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text(""),
automaticallyImplyLeading: false,
),
body: Container(
child: FlutterMap(
options: MapOptions(
),
layers: [
TileLayerOptions(
),
]
),
)
);
}
}
I would be very grateful for any advice. Here is the video I followed: https://www.youtube.com/watch?v=K4nYTayjofY&t=321s
Looks like the tutorial was done using an older version of the location plugin, since v2.0.0 they changed the api to return structured data rather than a map.
https://github.com/Lyokone/flutterlocation/blob/master/CHANGELOG.md
So you'd need to either change all your Map<String, double> types to LocationData or set your plugin version to ^1.4.0.
I tried many ways till I found this way thanks to a kind person who helped on another flutter facebook group.Make sure in your pubspec.yaml you update location to the latest version
dependencies:
location: ^2.3.5
Then change it to the following code:
LocationData _currentLocation;
StreamSubscription<LocationData> _locationSubscription;
var _locationService = new Location();
String error;
void initState() {
super.initState();
initPlatformState();
_locationSubscription = _locationService
.onLocationChanged()
.listen((LocationData currentLocation) async {
setState(() {
_currentLocation = currentLocation;
});
});
}
void initPlatformState() async {
try {
_currentLocation = await _locationService.getLocation();
} on PlatformException catch (e) {
if (e.code == 'PERMISSION_DENIED') {
error = 'Permission denied';
}else if(e.code == "PERMISSION_DENIED_NEVER_ASK"){
error = 'Permission denied';
}
_currentLocation = null;
}
You may access longitude and latitude as
_currentLocation.longitude and _currentLocation.latitude
these will return double values. Also, there are more options available at
https://pub.dev/packages/location#-readme-tab-
Related
This error does not occur on Android or web but only on IOS. It seem very trivial but I can't figure out what's wrong.
import 'dart:developer';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'dart:convert';
import 'package:qr_code_scanner/qr_code_scanner.dart';
class ScanQrPage extends StatefulWidget {
#override
_ScanQrPageState createState() => _ScanQrPageState();
}
class _ScanQrPageState extends State<ScanQrPage> {
final qrKey = GlobalKey();
late QRViewController qrViewController;
late Barcode barcode;
// In order to get hot reload to work we need to pause the camera if the platform
// is android, or resume the camera if the platform is iOS.
#override
void reassemble() {
super.reassemble();
if (Platform.isAndroid) {
qrViewController.pauseCamera();
} else if (Platform.isIOS) {
qrViewController.resumeCamera();
}
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {
Navigator.of(context).pop("");
return new Future(() => true);
},
child: Scaffold(
body: Stack(
children: [
buildQrView(context),
],
),
),
);
}
Widget buildQrView(BuildContext context) {
return QRView(
onQRViewCreated: onQRViewCreated,
key: qrKey,
overlay: QrScannerOverlayShape(
cutOutSize: MediaQuery.of(context).size.width * 0.8),
onPermissionSet: (ctrl, p) => _onPermissionSet(context, ctrl, p),
);
}
void _onPermissionSet(BuildContext context, QRViewController ctrl, bool p) {
log('${DateTime.now().toIso8601String()}_onPermissionSet $p');
if (!p) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('no Permission')),
);
}
}
#override
void dispose() {
qrViewController.dispose();
super.dispose();
}
void onQRViewCreated(QRViewController qrViewController) {
setState(() {
this.qrViewController = qrViewController;
});
qrViewController.scannedDataStream.listen((event) {
setState(() {
this.barcode = event;
if (Platform.isAndroid) {
qrViewController.pauseCamera();
} else if (Platform.isIOS) {
qrViewController.resumeCamera();
}
String rawData = event.code;
Uri data = Uri.dataFromString(rawData);
String para1 = data.queryParameters["buy"] ??
""; //get parameter with attribute "para1"
Codec<String, String> stringToBase64 = utf8.fuse(base64);
if (para1 != "") {
placer = stringToBase64.decode(para1);
}
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
Navigator.pop(context, placer);
});
});
});
}
}
I've tried all the solutions with the same error found on stackoverflow (addPostFrameCallback and Future(Duration.zero)) but none of them are exactly the same and does not seem to fix my problem.
I don't think I have having the same issue as any of the other questions.
The exception is happening on the Navigator.pop(context, placer);
Does anyone have any idea how to overcome this?
Why does this only happen on IOS?
I have implemented a simple application using Flutter and FirebaseAuth where I want a user to sign in giving an email and a password, this application works as intended in the iOS simulators however, when I try side loading the application on to a physical iOS device I get several errors and the signing in process fails and the app doesn't continue there onwards. I've shown the code, the errors that arises and I have listed the steps that I've taken so far to mitigate this of which none has worked.
Code
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'screens/other/LoadingScreen.dart';
import 'screens/other/ErrorScreen.dart';
import 'screens/other/SignupScreen.dart';
import 'screens/other/HomeScreen.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
// This widget is the root of your application.
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<CovidHound> {
bool _initialized = false;
bool _error = false;
String _email = "";
String _password = "";
void initializeFlutterFire() async {
try {
await Firebase.initializeApp();
print("Init firebase");
setState(() {
_initialized = true;
});
} catch (e) {
print("Error init firebase:${e}");
setState(() {
_error = true;
});
}
}
Future<void> onTapSignIn() async {
try {
await FirebaseAuth.instance
.signInWithEmailAndPassword(email: _email, password: _password);
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
print('No user found for that email.');
} else if (e.code == 'wrong-password') {
print('Wrong password provided for that user.');
}
} catch (e) {
print("Error signing in: $e");
}
if (FirebaseAuth.instance.currentUser != null) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomeScreen(),
fullscreenDialog: true,
),
);
}
}
#override
void initState() {
super.initState();
initializeFlutterFire();
}
#override
Widget build(BuildContext context) {
if(_error) {
return ErrorScreen();
}
if (!_initialized) {
return LoadingScreen();
}
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
children: [
TextField(
decoration: InputDecoration(hintText: "Email"),
onChanged: (value) {
_email = value;
},
),
TextField(
decoration: InputDecoration(hintText: "Password"),
onChanged: (value) {
_password = value;
},
),
TextButton(
onPressed: () {
onTapSignIn();
},
child: Text("Sign In"),
),
],
),
),
),
);
}
}
Errors
So far I have tried the following,
Properly configuring Firebase according to the documentation.
Cleaning Xcode workspace and builds using flutter clean.
Updating iOS and Xcode to latest versions.
Upgrading Flutter.
Adding permissions for Privacy - Local Network Usage Description in the info.plist as demonstrated in ( https://flutter.dev/docs/development/add-to-app/ios/project-setup#local-network-privacy-permissions )
Currently, you do not await your initializeFlutterFire() function, which could lead to your error message, because the subsequent code is executed before the initializing of Firebase.
Move your initializeFlutterFire() outside the MyApp or it's State class, then try to change the return type to Future<void>, then call this function in main() (instead of in initState()) for example:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await initializeFlutterFire();
runApp(MyApp());
}
Firebase (FlutterFire) requires you to initialise the plugin before you start your App's instance to avoid errors like this.
My Flutter app is crashing on loading
It operates a FutureBuilder and I believe this to be where the issue comes from.
My app makes an API Call and returns the data to a map marker.
When i have the FutureBuilder return a list view it works fine.
However, when i change it to return a Stack containing my Map SDK and the buttons to call the API it crashes on start up.
Relevant code is below, thank you!
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Future<Stations> stations;
BuildContext _context;
MapMarkerExample _mapMarkerExample;
#override
void initState() {
stations = API_Call().fetchStations();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Example 1'),
),
body: Container(
child: FutureBuilder<Stations>(
future: stations,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text("Error");
}
if (snapshot.connectionState == ConnectionState.done) {
return
Stack(
children: [
HereMap(onMapCreated: _onMapCreated),
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
button('Stations Near Me', _anchoredMapMarkersButtonClicked),
button('Clear', _clearButtonClicked),
],
),
],
),
],
);
}
return Text("Loading");
}
)
)
);
}
api_call.dart
class API_Call {
Future<Stations> fetchStations() async {
var client = http.Client();
final response = await client.get(
'https://transit.hereapi.com/v8/stations?in=x,-x&return=transport&apiKey=API_KEY');
if (response.statusCode == 200) {
return Stations.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load stations');
}
}
}
api_manager.dart
typedef ShowDialogFunction = void Function(String title, String message);
class MapMarkerExample{
void showAnchoredMapMarkers() {
print('step5');
GeoCoordinates geoCoordinates = _callGeoCoordinates();
// use the coords .. to add a marker
_addCircleMapMarker(geoCoordinates, 0);
_addPOIMapMarker(geoCoordinates, 1);
print('step6');
}
GeoCoordinates _callGeoCoordinates() {
print('step7');
var stations;
Future<Stations> fetchStations() async {
stations = await API_Call().fetchStations();
for (Station stations in stations) {
GeoCoordinates geoCoordinates = GeoCoordinates (stations.place.location.lat, stations.place.location.lng);
// use the coords .. to add a marker
_addCircleMapMarker(geoCoordinates, 0);
_addPOIMapMarker(geoCoordinates, 1);
}
}
}
HereMapController _hereMapController;
List<MapMarker> _mapMarkerList = [];
MapImage _poiMapImage;
MapImage _circleMapImage;
ShowDialogFunction _showDialog;
List<MapMarker3D> _mapMarker3DList = [];
MapMarkerExample(ShowDialogFunction showDialogCallback, HereMapController hereMapController) {
_showDialog = showDialogCallback;
_hereMapController = hereMapController;
double distanceToEarthInMeters = 8000;
_hereMapController.camera.lookAtPointWithDistance(
GeoCoordinates(x, -x), distanceToEarthInMeters);
// Setting a tap handler to pick markers from map.
_setTapGestureHandler();
_showDialog("Note", "Tap markers for more.");
}
void clearMap() {
for (var mapMarker in _mapMarkerList) {
_hereMapController.mapScene.removeMapMarker(mapMarker);
}
_mapMarkerList.clear();
for (var mapMarker3D in _mapMarker3DList) {
_hereMapController.mapScene.removeMapMarker3d(mapMarker3D);
}
_mapMarker3DList.clear();
}
Future<void> _addPOIMapMarker(GeoCoordinates geoCoordinates, int drawOrder) async {
// Reuse existing MapImage for new map markers.
if (_poiMapImage == null) {
Uint8List imagePixelData = await _loadFileAsUint8List('assets/poi.png');
_poiMapImage = MapImage.withPixelDataAndImageFormat(imagePixelData, ImageFormat.png);
}
Anchor2D anchor2D = Anchor2D.withHorizontalAndVertical(0.5, 1);
MapMarker mapMarker = MapMarker.withAnchor(geoCoordinates, _poiMapImage, anchor2D);
mapMarker.drawOrder = drawOrder;
Metadata metadata = new Metadata();
metadata.setString("key_poi", "Next Departures");
mapMarker.metadata = metadata;
_hereMapController.mapScene.addMapMarker(mapMarker);
_mapMarkerList.add(mapMarker);
}
Future<void> _addCircleMapMarker(GeoCoordinates geoCoordinates, int drawOrder) async {
// Reuse existing MapImage for new map markers.
if (_circleMapImage == null) {
Uint8List imagePixelData = await _loadFileAsUint8List('assets/circle.png');
_circleMapImage = MapImage.withPixelDataAndImageFormat(imagePixelData, ImageFormat.png);
}
MapMarker mapMarker = MapMarker(geoCoordinates, _circleMapImage);
mapMarker.drawOrder = drawOrder;
_hereMapController.mapScene.addMapMarker(mapMarker);
_mapMarkerList.add(mapMarker);
}
Future<Uint8List> _loadFileAsUint8List(String assetPathToFile) async {
// The path refers to the assets directory as specified in pubspec.yaml.
ByteData fileData = await rootBundle.load(assetPathToFile);
return Uint8List.view(fileData.buffer);
}
void _setTapGestureHandler() {
_hereMapController.gestures.tapListener = TapListener.fromLambdas(lambda_onTap: (Point2D touchPoint) {
_pickMapMarker(touchPoint);
});
}
void _pickMapMarker(Point2D touchPoint) {
double radiusInPixel = 2;
_hereMapController.pickMapItems(touchPoint, radiusInPixel, (pickMapItemsResult) {
// Note that 3D map markers can't be picked yet. Only marker, polgon and polyline map items are pickable.
List<MapMarker> mapMarkerList = pickMapItemsResult.markers;
if (mapMarkerList.length == 0) {
print("No map markers found.");
return;
}
});
}
}
In api_manager.dart, this looks very suspicous, and you aren't returning anything from this function, it could also explain the error saying future not complete
Future<Stations> fetchStations() async {
stations = await API_Call().fetchStations();
for (Station stations in stations) {
GeoCoordinates geoCoordinates = GeoCoordinates (stations.place.location.lat, stations.place.location.lng);
// use the coords .. to add a marker
_addPOIMapMarker(geoCoordinates, 1);
}
// GeoCoordinates geoCoordinates = stations.coordinates;
// _addPOIMapMarker(geoCoordinates, 1);
}
}
You have to return a Stations object from it, try after your for loop something like return stations;, it could fix your problem, if the error changes, it's also a good start.
Also change your line in future builder to this:
if (snapshot.connectionState == ConnectionState.done && snapshot.hasData)
And for the meantime, remove this _setTapGestureHandler(). The crash is most likely caused by some memory leak, and from the code posted, it could be explained by listeners.
When my app requests location permission the entire app stops until the dialog is complete (e.g. if the permission dialog pops up during a page transition the transition will freeze mid transition until the dialog is resolved).
Literally it causes execution to pause.
Using: flutter_riverpod, location.
The offending code:
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/all.dart';
import 'package:location/location.dart';
class MapApiBloc extends StateNotifier<MapApiState> {
// Instantiating a location service
static Location _location = Location();
// This will subscribe to the user's location
StreamSubscription<LocationData> _streamSubscription;
// Permissions stuff
bool _serviceEnabled;
PermissionStatus _permissionGranted;
// Initial (empty) state
static const MapApiState _initialState = MapApiState(userLocation: null);
MapApiBloc() : super(_initialState) {
init();
}
// This runs when you initialize the class
init() async {
// Checks if user toggled on location service
_serviceEnabled = await _location.serviceEnabled();
if (!_serviceEnabled) {
_serviceEnabled = await _location.requestService();
if (!_serviceEnabled) {
return;
}
}
// Asks for permission
_permissionGranted = await _location.hasPermission();
if (_permissionGranted == PermissionStatus.denied) {
_permissionGranted = await _location.requestPermission();
if (_permissionGranted != PermissionStatus.granted) {
return;
}
}
// Starts the subscription
_streamSubscription = _location.onLocationChanged.listen((event) {
state = MapApiState(userLocation: event);
});
}
#override
void dispose() {
_streamSubscription.cancel();
super.dispose();
}
}
class MapApiState {
final LocationData userLocation;
const MapApiState({#required this.userLocation});
}
final mapApiProvider = StateNotifierProvider<MapApiBloc>((ref) {
return MapApiBloc();
});
UI Code:
class ViewNearbyMapPage extends StatefulWidget {
#override
_ViewNearbyMapPageState createState() => _ViewNearbyMapPageState();
}
class _ViewNearbyMapPageState extends State<ViewNearbyMapPage> {
Completer<GoogleMapController> _controller = Completer();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Search Sellers")),
body: Consumer(
builder: (context, watch, child) {
var location = watch(mapApiProvider.state);
if (location.userLocation?.latitude == null) {
return Center(child: CircularProgressIndicator());
}
CameraPosition _myPosition = CameraPosition(
target: LatLng(location.userLocation.latitude,
location.userLocation.longitude),
zoom: 14.4746,
);
return GoogleMap(
initialCameraPosition: _myPosition,
onMapCreated: (controller) {
_controller.complete(controller);
},
);
},
),
);
}
}
I use this code for check internet. and I wrap this function into initState also. Snack bar always displays when internet not available. But after connecting to the internet, the snack bar is not disappeared. I can't use connectivity plugin because they said on Android, the plugin does not guarantee connection to the Internet.
checking1(TextEditingController usernameController, BuildContext context,
String _url, GlobalKey<ScaffoldState> _scaffoldKey) async {
try {
final result = await InternetAddress.lookup('google.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
usernameController.text == '' ?
showDialog(...some code...) :
usernameValidation(usernameController.text, context, _url);
}
}
on SocketException
catch (_) {
_showSnackBar(_scaffoldKey);
}
}
Full example demonstrating a listener of the internet connectivity and its source.
Original post
import 'dart:async';
import 'dart:io';
import 'package:connectivity/connectivity.dart';
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: HomePage()));
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Map _source = {ConnectivityResult.none: false};
MyConnectivity _connectivity = MyConnectivity.instance;
#override
void initState() {
super.initState();
_connectivity.initialise();
_connectivity.myStream.listen((source) {
setState(() => _source = source);
});
}
#override
Widget build(BuildContext context) {
String status = "Offline";
switch (_source.keys.toList()[0]) {
case ConnectivityResult.none:
status = "Offline";
break;
case ConnectivityResult.mobile:
status = "Mobile: Online";
break;
case ConnectivityResult.wifi:
status = "WiFi: Online";
break;
case ConnectivityResult.ethernet:
status = "Ethernet: Online";
break;
}
return Scaffold(
appBar: AppBar(title: Text("Internet")),
body: Center(child: Text(status)),
);
}
#override
void dispose() {
_connectivity.disposeStream();
super.dispose();
}
}
class MyConnectivity {
MyConnectivity._internal();
static final MyConnectivity _instance = MyConnectivity._internal();
static MyConnectivity get instance => _instance;
Connectivity connectivity = Connectivity();
StreamController controller = StreamController.broadcast();
Stream get myStream => controller.stream;
void initialise() async {
ConnectivityResult result = await connectivity.checkConnectivity();
_checkStatus(result);
connectivity.onConnectivityChanged.listen((result) {
_checkStatus(result);
});
}
void _checkStatus(ConnectivityResult result) async {
bool isOnline = false;
try {
final result = await InternetAddress.lookup('example.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
isOnline = true;
} else {
isOnline = false;
}
} on SocketException catch (_) {
isOnline = false;
}
controller.sink.add({result: isOnline});
}
void disposeStream() => controller.close();
}
Another option also can be this package: https://pub.dartlang.org/packages/flutter_offline that deal with this issue really straightforward.
You need first to import the package 'package:flutter_offline/flutter_offline.dart';
After that you include the OfflineBuilder on Widget build(BuildContext context) and it will read all all stream changes from ConnectivityResult continuously.
Like the example on the link or like the following one
#override
Widget build(BuildContext context) {
return OfflineBuilder(
debounceDuration: Duration.zero,
connectivityBuilder: (
BuildContext context,
ConnectivityResult connectivity,
Widget child,
) {
if (connectivity == ConnectivityResult.none) {
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
),
body: Center(child: Text('Please check your internet connection!')),
);
}
return child;
},
child: Scaffold(
resizeToAvoidBottomPadding: false,
appBar: AppBar(
title: Text("Home")
),
body: new Column(
children: <Widget>[
new Container(
decoration: new BoxDecoration(color: Theme.of(context).cardColor),
child: _buildTxtSearchBox(),
),
new Divider(height: 10.0),
new FloatingActionButton.extended(
icon: Icon(Icons.camera_alt),
),
new Container(
...
),
],
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
drawer: MenuDrawer(),
)
);
}
The connectivity package will do what you want. It has an onConnectivityChanged stream which you can subscribe to. This will notify your app when the connectivity state changes. But just because your device is connected to a network doesn't mean it can access your server and be connected. So a DNS lookup would be a good idea before then updating the internal state of your application.
https://pub.dartlang.org/documentation/connectivity/latest/connectivity/Connectivity-class.html
I find this to be reliable & more convincing :
Future<bool> connectivityChecker() async {
var connected = false;
print("Checking internet...");
try {
final result = await InternetAddress.lookup('google.com');
final result2 = await InternetAddress.lookup('facebook.com');
final result3 = await InternetAddress.lookup('microsoft.com');
if ((result.isNotEmpty && result[0].rawAddress.isNotEmpty) ||
(result2.isNotEmpty && result2[0].rawAddress.isNotEmpty) ||
(result3.isNotEmpty && result3[0].rawAddress.isNotEmpty)) {
print('connected..');
connected = true;
} else {
print("not connected from else..");
connected = false;
}
} on SocketException catch (_) {
print('not connected...');
connected = false;
}
return connected;
}
Based on the bool value of connected returned, I'd run a timer based loop to check for internet again & again till its connected. Open to any suggestions