I am writing an app in flutter and need to send the users location as stream to my backend.After using the singleton as suggested in comment the logout works and it stops the stream,but after loging back streaming is not working.
I initialize my location stream by calling it in main:
Main:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
LocationService().locationStream;
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Semdatex Patient Portal',
debugShowCheckedModeBanner: false,
//theme for all screens
theme: ThemeData(
primarySwatch: Colors.blue,
primaryColor: Colors.blue[100],
),
//all possible routes within the app
initialRoute: '/loginScreen',
routes: {
'/': (context) => const InitialSite(),
'/chat': (context) => const Chat(),
'/chatPage': (context) => const ChatPage(),
'/multipleChoice': (context) => const MultipleChoice(),
'/scale': (context) => const Scale(),
'/endScreen': (context) => const EndScreen(),
'/singleChoiceNew': (context) => const SelectionWindow(),
'/loginScreen': (context) => const LoginScreen(),
'/registerScreen': (context) => const RegisterScreen(),
},
);
}
}
LocationService:
import 'dart:async';
import 'package:location/location.dart';
import 'package:sdx_patient_portal/Datamodel/user_location.dart';
class LocationService {
static final LocationService _instance = LocationService._internal();
factory LocationService() => _instance;
LocationService._internal() {
getLocationOnchange();
}
var location = Location();
final StreamController<UserLocation> _locationController =
StreamController<UserLocation>.broadcast();
StreamSubscription<LocationData>? listen;
Stream<UserLocation> get locationStream => _locationController.stream;
void getLocationOnchange() async {
location.requestService().then((value) => {
location.requestPermission().then((permissionStatus) {
if (permissionStatus == PermissionStatus.granted) {
location.enableBackgroundMode(enable: true);
location.changeSettings(
interval: 10000, accuracy: LocationAccuracy.high);
listen = location.onLocationChanged.listen((locationData) {
_locationController.add(UserLocation(
locationData.latitude,
locationData.longitude,
locationData.altitude,
locationData.time));
});
}
})
});
}
StreamSubscription<LocationData>? getListener(){
return listen;
}
void dipose(){
listener?.cancel();
listener = null;
}
}
Here is the log in part in which I call the post after user has been loged in successfully:
class LoginScreen extends StatefulWidget {
const LoginScreen({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() => _State();
}
class _State extends State<LoginScreen> {
TextEditingController nameController = TextEditingController();
TextEditingController passwordController = TextEditingController();
final _formKey = GlobalKey<FormState>();
User user = User("", "");
String url = "http://10.0.2.2:9173/mob/login";
Future save() async {
var res = await http.post(Uri.parse(url),
headers: {'Content-Type': 'application/json'},
body: json.encode({'email': user.email, 'password': user.password}));
if (res.body.isEmpty) {
print('login wasn\'t successfull');
_showMyDialog();
} else {
var data = json.decode(res.body);
if (data["patientNumber"] == null) {
_showMyDialog();
} else {
await savePatientNumber(data["patientNumber"], data["lastAccess"]);
final prefs = await SharedPreferences.getInstance();
await prefs.setBool('loggedIn', true);
//LocationService().getListener()?.resume();
LocationService().locationStream;
startLocationStream();
Navigator.pushNamed(context, '/');
}
}
}
I call the following POST after log in:
postLocation() async {
var patient = await getPatient();
var patNumber = patient[0]["PatNum"];
var l = (LocationService().locationStream);
const String url = "http://10.0.2.2:9173/mob/location";
UserLocation locationData;
l.listen((event) {
locationData = UserLocation(
event.latitude, event.longitude, event.altitude, event.time);
http.post(Uri.parse(url),
headers: {'Content-Type': 'application/json'},
body: json.encode({
'latitude': locationData.latitude,
'altitude': locationData.altitude,
'longitude': locationData.longitude,
'time': locationData.time,
'patientNumber': patNumber
}));
});
}
Then I call cancle the listener by signing out:
void signOut(context) async {
LocationService().dipose();
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (context) => const LoginScreen()),
(route) => false);
}
Hoe can I get controll over the state of the app and rerun the services after re-login?
It is hard to follow as part of the code missing, but something like this could work.
Call startListeningLocation when a user logs in and stopListeningLocation when logs out.
class LocationService {
static final LocationService _instance = LocationService._internal();
factory LocationService() => _instance;
LocationService._internal();
final _location = Location();
final StreamController<UserLocation> _locationController =
StreamController<UserLocation>.broadcast();
StreamSubscription<LocationData>? _locationSubscription;
Stream<UserLocation> get locationStream => _locationController.stream;
void startListeningLocation() {
_location.requestService().then((value) => {
_location.requestPermission().then((permissionStatus) {
if (permissionStatus == PermissionStatus.granted) {
_location.enableBackgroundMode(enable: true);
_location.changeSettings(
interval: 10000, accuracy: LocationAccuracy.high);
_locationSubscription = _location.onLocationChanged.listen((locationData) {
_locationController.add(UserLocation(
locationData.latitude,
locationData.longitude,
locationData.altitude,
locationData.time));
});
}
})
});
}
void stopListeningLocation() {
_locationSubscription?.cancel();
}
}
Related
The problem is that Hive is acting unexpectedly, and when the app closes or I restart it all, the data in the box is cleared.
main.dart:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(statusBarColor: Colors.transparent));
await Firebase.initializeApp();
await Hive.initFlutter();
Hive.registerAdapter(CredentialsModelAdapter());
Hive.registerAdapter(DoctorModelAdapter());
Hive.registerAdapter(DuserModelAdapter());
Hive.registerAdapter(DoctorAppointmentsAdapter());
Hive.registerAdapter(AppointmentStatusesAdapter());
Hive.registerAdapter(AccountTypeAdapter());
Hive.registerAdapter(UserAdapter());
await Hive.openBox<CredentialsModel>("cred");
await Hive.openBox<DuserModel>("doctor");
runApp(ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
MyApp({Key? key}) : super(key: key);
final _appRouter = app_router.AppRouter();
#override
Widget build(BuildContext context) {
return MaterialApp.router(
title: "x",
debugShowCheckedModeBanner: false,
routerDelegate: _appRouter.delegate(),
routeInformationParser: _appRouter.defaultRouteParser(),
);
}
}
Here is where I fetch the data from the api and store it in box:
#override
Future<Either<ApiFailures, dynamic>> signInWithEmailAndPassword(
{required String email, required String password}) async {
late Box<CredentialsModel> credentials;
var result;
try {
final response = await http.get(Uri.parse(
"xxxxxxxx"));
if (response.statusCode == 200) {
result = await json.decode(response.body);
if (result["AZSVR"] == "FAILED") {
return const Left(ApiFailures.authFailed());
} else {
var content = CredentialsModel.fromJson(result);
credentials = Hive.box("cred");
credentials.put('cred', content);
return right(result["api_token"]);
}
}
} on SocketException catch (e) {
return const Left(ApiFailures.noConnection());
} on HttpException {
return const Left(ApiFailures.notfound());
} catch (_) {
return const Left(ApiFailures.notfound());
}
return Right(result["api_token"]);
}
Where I call the box:
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:vwelfare/application/provider/doctor.repository.provider.dart';
import 'package:vwelfare/domain/models/doctor/duser.dart';
import '../../domain/models/credentials/credentials.dart';
class MyWidget extends HookConsumerWidget {
const MyWidget({super.key});
#override
Widget build(BuildContext context, WidgetRef ref) {
final Box<CredentialsModel> credBox = Hive.box("cred");
final Box<DuserModel> doctorBox = Hive.box("doctor");
final controller = useTextEditingController();
final uid = useState(0);
final cred = useState(const CredentialsModel());
return Scaffold(
body: ValueListenableBuilder(
valueListenable: credBox.listenable(),
builder: (context, Box<CredentialsModel> box, _) {
final cred = box.get("cred");
print(cred!.api_token);
final doctor = ref.watch(getDoctor(cred.api_token!));
return doctor.when(
data: (data) => data.fold(
(l) => ValueListenableBuilder(
valueListenable: doctorBox.listenable(),
builder: (context, Box<DuserModel> box, _) {
final model = box.get("doctor");
final doctor = model!.User;
if (doctor != null) {
return Center(
child: Text("${doctor.address}"),
);
} else {
return const Center(
child: Text("CONTACT US"),
);
}
}),
(r) => Center(child: Text("${r.User!.name}"))),
loading: () => const CircularProgressIndicator(),
error: (error, stackTrace) {
print(error);
return Center(
child: Text("$error hello"),
);
});
},
),
);
}
}
I don't know if I am doing something wrong but I followed the docs as they say:
1- registered the adapter
2- opened the box
3- called it in a widget
What am I doing wrong?
I'm learning proper error handling, I feel THIS is the best possible method of handling errors I've found. Problem is that the project is old as hell and not updated for null safety.
Project works well with old version. Tell me how I can get over this compile time error:
The argument type 'Either<Failure, dynamic>' can't be assigned to the parameter type 'Either<Failure, Post>'
This occurs due to labeling _post in PostChangeNotifier class as "late"...
import 'dart:convert';
import 'dart:io';
import 'package:dartz/dartz.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return ProviderScope(
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.orange,
),
home: const HomePage(),
),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blueGrey,
body: Flex(
direction: Axis.horizontal,
children: [Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Consumer(
builder: (ctx, ref, child) {
if (ref.read(notifier).state == NotifierState.initial) {
return const Text('Press the button');
} else if (ref.read(notifier).state == NotifierState.loading) {
return const CircularProgressIndicator();
} else {
return ref.read(notifier).post.fold(
(failure) => Text(failure.toString()),
(post) => Text(post.toString()),
);
}
},
),
Consumer(
builder: (ctx, ref,child) {
return ElevatedButton(
onPressed: () {
ref.read(notifier).getOnePost();
},
child: const Text('Get Post'));
},
),
]),
),]
),
);
}
}
class PostService {
final httpClient = FakeHttpClient();
Future<Post?> getOnePost() async {
try {
final responseBody = await httpClient.getResponseBody();
return Post.fromJson(responseBody);
} on SocketException {
throw Failure('No Internet connection 😑');
} on HttpException {
throw Failure("Couldn't find the post 😱");
} on FormatException {
throw Failure("Bad response format 👎");
}
}
}
class FakeHttpClient {
Future<String> getResponseBody() async {
await Future.delayed(const Duration(milliseconds: 500));
//! No Internet Connection
// throw SocketException('No Internet');
//! 404
// throw HttpException('404');
//! Invalid JSON (throws FormatException)
// return 'abcd';
return '{"userId":1,"id":1,"title":"nice title","body":"cool body"}';
}
}
enum NotifierState { initial, loading, loaded }
final notifier = ChangeNotifierProvider((ref) => PostChangeNotifier());
class PostChangeNotifier extends ChangeNotifier {
final _postService = PostService();
NotifierState _state = NotifierState.initial;
NotifierState get state => _state;
void _setState(NotifierState state) {
_state = state;
notifyListeners();
}
late Either<Failure, Post> _post;
Either<Failure, Post> get post => _post;
void _setPost(Either<Failure, Post> post) {
_post = post;
notifyListeners();
}
void getOnePost() async {
_setState(NotifierState.loading);
await Task(() => _postService.getOnePost())
.attempt()
.mapLeftToFailure()
.run()
.then((value) => _setPost(value)); /// error occurs here
_setState(NotifierState.loaded);
}
}
extension TaskX<T extends Either<Object, U>, U> on Task<T> {
Task<Either<Failure, U>> mapLeftToFailure() {
return map(
(either) => either.leftMap((obj) {
try {
return obj as Failure;
} catch (e) {
throw obj;
}
}),
);
}
}
class Post {
final int id;
final int userId;
final String title;
final String body;
Post({
required this.id,
required this.userId,
required this.title,
required this.body,
});
static Post? fromMap(Map<String, dynamic> map) {
return Post(
id: map['id'],
userId: map['userId'],
title: map['title'],
body: map['body'],
);
}
static Post? fromJson(String source) => fromMap(json.decode(source));
#override
String toString() {
return 'Post id: $id, userId: $userId, title: $title, body: $body';
}
}
class Failure {
// Use something like "int code;" if you want to translate error messages
final String message;
Failure(this.message);
#override
String toString() => message;
}
You have to typed your Task.
If you don't, your extension TaskX transform the Task<dynamic> into Task<Either<Failure, dynamic>>.
Two solutions :
Type the Task :
await Task<Either<Failure, Post>>(() =>
_postService.getOnePost())
.attempt()
.mapLeftToFailure()
.run()
.then((value) => _setPost(value));
Cast the return value :
await Task(() => _postService.getOnePost())
.attempt()
.mapLeftToFailure()
.run()
.then((Either<Failure, Post > value) => _setPost(value));
Figured this out after beating my brains out- of course over something very simple. The errors were a bit vague and I had trouble tracking this down, a little interesting using the Either...
Post "right side" of either needed to be turned into an optional value everywhere due to the need for late applied to the _post in PostChangeNotifier:
class PostChangeNotifier extends ChangeNotifier {
final _postService = PostService();
NotifierState _state = NotifierState.initial;
NotifierState get state => _state;
void _setState(NotifierState state) {
_state = state;
notifyListeners();
}
late Either<Failure, Post?> _post;
Either<Failure, Post?> get post => _post;
// Set post
void _setPost(Either<Failure, Post?> post) {
_post = post;
notifyListeners();
}
// Set one post
void getOnePost() async {
_setState(NotifierState.loading);
await Task(() => _postService.getOnePost())
.attempt()
.mapLeftToFailure()
.run()
.then((value) => _setPost(value as Either<Failure, Post?>));
_setState(NotifierState.loaded);
}
}
The code flow doesn't even enter the onIceCandidate function while answering the SDP for webRTC connection. The webRTC is used for Voice calling for VOIP in android and I have also setted up TURN server with viagene website.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'WebRTC lets learn together'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
CollectionReference firebaseInstance =
FirebaseFirestore.instance.collection("dmeet");
RTCPeerConnection _peerConnection;
MediaStream _localStream;
RTCVideoRenderer _remoteRenderer = RTCVideoRenderer();
var docId = TextEditingController();
var l;
var document;
_createOfferSdp() async {
RTCSessionDescription description =
await _peerConnection.createOffer({'offerToReceiveAudio': 1});
Map<String, dynamic> session = {"sdp": description.sdp};
document = firebaseInstance.doc();
document.collection("sdp").doc("offersdp").set(session);
await _peerConnection.setLocalDescription(description);
document.collection("icecandidate").snapshots().listen((result) async {
dynamic candidate = new RTCIceCandidate(
result['candidate'], result['sdpMid'], result['sdpMlineIndex']);
await _peerConnection.addCandidate(candidate);
});
print(session);
_peerConnection.onIceCandidate = (event) {
if (event.candidate != null) {
Map<String, dynamic> icecandidate = {
"candidate": event.candidate,
"sdpMid": event.sdpMid,
"sdpMlineIndex": event.sdpMlineIndex
};
document.collection("candidate").doc().set(icecandidate);
}
};
}
bool remotesaved = false;
_createAnswerSdp() async {
_peerConnection.onIceCandidate = (event) {
print("Candiate ${event.candidate}");
if (event.candidate != null) {
// Map<String, dynamic> icecandidate = {
// "candidate": event.candidate,
// "sdpMid": event.sdpMid,
// "sdpMlineIndex": event.sdpMlineIndex
// };
// document.collection("candidate").doc().set(icecandidate);
print("Candidate: ${event.candidate}");
}
};
firebaseInstance
.doc(docId.text)
.collection("sdp")
.doc("offersdp")
.get()
.then((value) async {
var remoteSession = value.data()["sdp"];
RTCSessionDescription description1 =
RTCSessionDescription(remoteSession, "offer");
await _peerConnection
.setRemoteDescription(description1)
.then((value) async {
RTCSessionDescription description2 =
await _peerConnection.createAnswer({'offerToReceiveAudio': 1});
Map<String, dynamic> session = {"sdp": description2.sdp};
firebaseInstance
.doc(docId.text)
.collection("sdp")
.doc("answersdp")
.set(session);
final iceCandidate = await firebaseInstance
.doc(docId.text)
.collection("candidate")
.get();
iceCandidate.docs.forEach((element) async {
print("Candidate ${element.data()["candidate"]}");
dynamic candidate = RTCIceCandidate(element.data()['candidate'],
element.data()['sdpMid'], element.data()['sdpMlineIndex']);
await _peerConnection.addCandidate(candidate);
});
});
});
}
showAlertDialog(BuildContext context) {
// set up the buttons
Widget cancelButton = FlatButton(
child: Text("Cancel"),
onPressed: () {},
);
Widget continueButton = FlatButton(
child: Text("Continue"),
onPressed: _createAnswerSdp,
);
// set up the AlertDialog
AlertDialog alert = AlertDialog(
title: Text("AlertDialog"),
content: TextField(
controller: docId,
),
actions: [
cancelButton,
continueButton,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
initRenderer() async {
await _remoteRenderer.initialize();
}
#override
void initState() {
_createPeerConnection().then((pc) {
_peerConnection = pc;
});
initRenderer();
// _localStream.initialize();
super.initState();
}
#override
void dispose() {
_remoteRenderer.dispose();
super.dispose();
}
_getUserMedia() async {
final Map<String, dynamic> mediaConstraints = {
'audio': true,
'video': false,
};
MediaStream stream = await navigator.getUserMedia(mediaConstraints);
// _localStream = stream;
// _peerConnection.addStream(stream);
return stream;
}
_createPeerConnection() async {
Map<String, dynamic> configuration = {
"iceServers": [
{"url": "stun:stun.l.google.com:19302"},
{
"url": "turn:numb.viagenie.ca",
"username": "******#gmail.com",
"credential": "*****",
}
]
};
final Map<String, dynamic> offerSdpConstraints = {
"mandatory": {
"OfferToReceiveAudio": true,
"OfferToReceiveVideo": false,
},
"optional": [],
};
_localStream = await _getUserMedia();
RTCPeerConnection pc =
await createPeerConnection(configuration, offerSdpConstraints);
pc.addStream(_localStream);
pc.onIceCandidate = (e) {
if (e.candidate != null) {
l = json.encode({
'candidate': e.candidate.toString(),
'sdpMid': e.sdpMid.toString(),
'sdpMlineIndex': e.sdpMlineIndex,
});
print("Her $l");
}
};
pc.onAddStream = (stream) {
print('addStream: ' + stream.id);
_remoteRenderer.srcObject = stream;
};
return pc;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: Row(
children: [
Flexible(child: RTCVideoView(_remoteRenderer)),
ElevatedButton(
child: Text("Create"),
onPressed: _createOfferSdp,
),
ElevatedButton(
onPressed: () {
showAlertDialog(context);
},
child: Text("Join"),
)
],
),
),
),
);
}
}
The Line that does not even entered is the function _createAnwerSdp() and next line to it!
The createAnswerSdp function is used for answering the call while getting the ice candidate.
What may be cause for the issue?
So, I can clearly see that there you haven't set any local description for the remote user who is going to answer this call.
_peerConnection.setLocalDescription(description2);
Hope this might help!
So, I was trying to write a webRTC mobile app. Caught some error.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:sdp_transform/sdp_transform.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'WebRTC lets learn together'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
CollectionReference firebaseInstance =
FirebaseFirestore.instance.collection("dmeet");
RTCPeerConnection _peerConnection;
MediaStream _localStream;
RTCVideoRenderer _remoteRenderer = RTCVideoRenderer();
var docId = TextEditingController();
var l;
_createOfferSdp() async {
RTCSessionDescription description =
await _peerConnection.createOffer({'offerToReceiveAudio': 1});
var session = parse(description.sdp);
Map<String, dynamic> sdp = {
"sdp": session.toString(),
};
var k = firebaseInstance.doc().collection("sdp").doc("offersdp").set(sdp);
print("K val: $k");
_peerConnection.setLocalDescription(description);
// print(session);
}
bool remotesaved = false;
_createAnswerSdp() async {
firebaseInstance
.doc(docId.text)
.collection("sdp")
.doc("offersdp")
.get()
.then((value) async {
print("here");
var temp = (value.data()["sdp"]).toString();
// var session = await jsonDecode(temp.toString());
// print("here3");
// print("session: $session");
// String sdp = write(session, null);
print(temp);
if (temp != null) {
RTCSessionDescription description1 =
RTCSessionDescription(temp, "offer");
await _peerConnection.setRemoteDescription(description1).then((value) {
setState(() {
remotesaved = true;
});
});
}
});
if (remotesaved) {
RTCSessionDescription description = await _peerConnection
.createAnswer({'offerToReceiveVideo': 0, 'offerToReceiveAudio': 1});
var session = parse(description.sdp);
firebaseInstance
.doc(docId.text)
.collection("sdp")
.doc("answersdp")
.set(session);
if (l != null) {
firebaseInstance
.doc(docId.text)
.collection("icecandidate")
.doc("answerice")
.set(l);
}
}
}
showAlertDialog(BuildContext context) {
// set up the buttons
Widget cancelButton = FlatButton(
child: Text("Cancel"),
onPressed: () {},
);
Widget continueButton = FlatButton(
child: Text("Continue"),
onPressed: _createAnswerSdp,
);
// set up the AlertDialog
AlertDialog alert = AlertDialog(
title: Text("AlertDialog"),
content: TextField(
controller: docId,
),
actions: [
cancelButton,
continueButton,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
#override
void initState() {
_createPeerConnection().then((pc) {
_peerConnection = pc;
});
super.initState();
}
#override
void dispose() {
super.dispose();
}
_getUserMedia() async {
final Map<String, dynamic> mediaConstraints = {
'audio': true,
'video': false,
};
MediaStream stream = await navigator.getUserMedia(mediaConstraints);
// _localStream = stream;
// _peerConnection.addStream(stream);
return stream;
}
_createPeerConnection() async {
Map<String, dynamic> configuration = {
"iceServers": [
{"url": "stun:stun.l.google.com:19302"},
]
};
final Map<String, dynamic> offerSdpConstraints = {
"mandatory": {
"OfferToReceiveAudio": true,
"OfferToReceiveVideo": false,
},
"optional": [],
};
_localStream = await _getUserMedia();
RTCPeerConnection pc =
await createPeerConnection(configuration, offerSdpConstraints);
// if (pc != null) print(pc);
pc.addStream(_localStream);
pc.onIceCandidate = (e) {
if (e.candidate != null) {
l = json.encode({
'candidate': e.candidate.toString(),
'sdpMid': e.sdpMid.toString(),
'sdpMlineIndex': e.sdpMlineIndex,
});
print(l);
}
};
pc.onIceConnectionState = (e) {
print(e);
};
pc.onAddStream = (stream) {
print('addStream: ' + stream.id);
_remoteRenderer.srcObject = stream;
};
return pc;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: Row(
children: [
ElevatedButton(
child: Text("Create"),
onPressed: _createOfferSdp,
),
ElevatedButton(
onPressed: () {
showAlertDialog(context);
},
child: Text("Join"),
)
],
),
),
),
);
}
}
So, In my code from a mobile an offer will be created and stored in firestore.
dmeet->(AUTO ID)->sdp->offersdp->sdp:sdpvaluesStored
so after that I given the documentId manually in join button and when I try to fetch the offersdp and to set the remote description. I came up with some error
D/FlutterWebRTCPlugin( 7232): peerConnectionSetRemoteDescription(): WEBRTC_SET_REMOTE_DESCRIPTION_ERROR: SessionDescription is NULL.
E/flutter ( 7232): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: Unable to RTCPeerConnection::setRemoteDescription: peerConnectionSetRemoteDescription(): WEBRTC_SET_REMOTE_DESCRIPTION_ERROR: SessionDescription is NULL.
E/flutter ( 7232): #0 RTCPeerConnectionNative.setRemoteDescription
package:flutter_webrtc/…/native/rtc_peerconnection_impl.dart:328
E/flutter ( 7232): <asynchronous suspension>
E/flutter ( 7232): #1 _MyHomePageState._createAnswerSdp.<anonymous closure>.<anonymous closure> (package:flutter_voip_calling/main.dart)
I'm using the tutorial code where it uses the same concept as that I will paste the sdp in textbox and set the remote description and the tutorial code is here
I want to store value of share preference in to class variable but it doesn't working at all, the value doesn't store to variable.
here is my code, basically i want to store to variable _uid but when i accessing it inside my UI it printing ""..
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class User with ChangeNotifier {
FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
Firestore _firestore = Firestore.instance;
bool _loggedIn = false;
bool get loggedIn => _loggedIn;
String _uid = "";
String get uid => _uid;
User() {
getPrefState().then((val) {
// do some operation
_uid = val.toString() ?? "test";
});
//init();
}
Future init() async {
//var data;
//_uid = getPrefState();
/*SharedPreferences.getInstance().then((value) => {
_uid = value.getString("uid") ?? "d",
data = "dsdasd",
});*/
/*try {
SharedPreferences prefs = await SharedPreferences.getInstance();
_uid = data;
} catch (err) {
//pass.
}*/
//var uid = prefs.getString("uid") ?? "d";
/*if (uid != null) {
_loggedIn = true;
}*/
//_uid = "dsd";
//notifyListeners();
}
Future<String> getPrefState() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
//Return String
String stringValue = prefs.getString('uid') ?? "test";
return stringValue;
}
Future<void> saveId(uid) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString("uid", uid);
}
Future<void> saveUserInDocument(String uid, String name, String sap) {
_firestore.collection("users").document(uid).setData({
"name": name,
"sap": sap,
'role': "student",
});
}
Future<FirebaseUser> getCurrentUser() async {
return await _firebaseAuth.currentUser();
}
// user signup.
Future<void> signup(
String email, String password, String name, String sap) async {
FirebaseUser user = (await _firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password))
.user;
if (user != null) {
saveUserInDocument(user.uid, name, sap);
saveId(user.uid);
_loggedIn = true;
notifyListeners();
}
}
/// user login
Future<void> login(String email, String password) async {
FirebaseUser user = (await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password))
.user;
//if (user != null) {
saveId(user.uid);
_loggedIn = true;
notifyListeners();
//}
}
/// User logout
Future<void> logout() async {
_firebaseAuth.signOut();
saveId(null);
_loggedIn = false;
notifyListeners();
}
/// reset user password
Future<void> resetPassword(String email) async {
await _firebaseAuth.sendPasswordResetEmail(email: email);
}
}
Here is my UI class where i want to use it
user.uid
import "package:flutter/material.dart";
import 'package:riphahwebresources/data/User.dart';
import 'package:riphahwebresources/pages/auth/login_ui.dart';
import 'package:shared_preferences/shared_preferences.dart';
class WebResourceAppDrawer extends StatefulWidget {
#override
_WebResourceAppDrawerState createState() => _WebResourceAppDrawerState();
}
class _WebResourceAppDrawerState extends State<WebResourceAppDrawer> {
User user = User();
#override
Widget build(BuildContext context) {
List<Widget> children = [];
children.add(
ListTile(
leading: Icon(Icons.home),
title: Text("Home"),
),
);
if (user.loggedIn) {
children.add(ListTile(
leading: Icon(Icons.people),
title: Text("Profile"),
onTap: () => {
Navigator.push(
context, MaterialPageRoute(builder: (context) => LoginUi()))
},
));
children.add(ListTile(
leading: Icon(Icons.people),
title: Text("Logout"),
onTap: () => {
Navigator.push(
context, MaterialPageRoute(builder: (context) => LoginUi()))
},
));
} else {
children.add(ListTile(
leading: Icon(Icons.people),
title: Text(user.uid),
onTap: () => {
Navigator.push(
context, MaterialPageRoute(builder: (context) => LoginUi()))
},
));
}
return Drawer(
child: ListView(
padding: const EdgeInsets.all(8),
children: <Widget>[
DrawerHeader(
child: Text("Menu"),
),
...children,
],
),
);
}
}
You can see from method init i try different things but idk nothing works for me.
Thanks you so much.
When using ChangeNotifier, you need to call notifyListeners to propagate the change to your UI class:
notifyListeners(). Call this method any time the model changes in a way that might change your app’s UI.
Flutter Docs on State Management
As a reminder, you need the following 3 things to get State Management working:
Setup a class that extends ChangeNotifier (which you have done, but just replace the with with extends)
Add a ChangeNotifierProvider above the widget where you require the value (i.e. above your WebResourceAppDrawer UI widget)
Now access User by wrapping your UI widget with Consumer<User>
Here's what a complete, minimal example would look like:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Center(
child:
ChangeNotifierProvider(create: (_) => User(), child: UI()))),
// 'UI' can now access the newly created User() since it's a
// child of ChangeNotifierProvider
);
}
}
class UI extends StatefulWidget {
#override
_UIState createState() => _UIState();
}
class _UIState extends State<UI> {
#override
Widget build(BuildContext context) {
return Container(
child: Consumer<User>(
builder: (context, user, child) => Text("User ${user.uid}")),
// This is how user can be accessed within UI
);
}
}
class User extends ChangeNotifier {
String _uid = "(empty)";
String get uid => _uid;
User() {
getPrefState().then((val) {
_uid = val;
notifyListeners(); // this call triggers a rebuild of UI
});
}
getPrefState() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString('uid');
}
}