Flutter| Dio : Invalid argument(s) (value): Must not be null - flutter

i am using shared preference to pass data from one screen to other, it was working perfectly few days ago, but now it is sending this error
Invalid argument(s) (value): Must not be null
i'm calling login api, it is running sucessfully, i am getting api data on its response but it goes to catch part after print the data, it is not navigating to other screen, here is the login function code
SharedPreferences myPrefs;
Future login() async {
Dio dio = new Dio();
try {
data = {
'username':"Munib khatri",
'password':"Munib123",
'date': formattedDate
};
await dio
.post(localhostUrlLogin, data: json.encode(data),)
.then((onResponse) async {
print(onResponse.data);
String name = (onResponse.data['User']['username']);
String email = (onResponse.data['User']['email']);
String designation = (onResponse.data['User']['Designation']);
String token = (onResponse.data['AccessToken']);
//here i am calling another api
data={
"token":token,
"username":name,
"leave":"Approved",
"type":"maternity"
};
dio
.post(localHostUrlleaveCount, data: json.encode(data))
.then((onResponse) async {
int sickCount=(onResponse.data['sick']);
int annualCount=(onResponse.data['annual']);
await myPrefs.setInt('sickCount', sickCount);
await myPrefs.setInt('annualCount', annualCount);
}).catchError((onerror){
});
await myPrefs.setString('name', name);
await myPrefs.setString('email', email);
await myPrefs.setString('designation', designation);
Navigator.push(
context, new MaterialPageRoute(builder: (context) => Navigation()));
});
} catch (e) {
print(e.toString());
}
}
Navigation:
class Navigation extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
debugShowCheckedModeBanner: false,
theme: new ThemeData(primaryColor: Colors.blue),
home: EmployeeNavigation(),
);
}
}
int _selectedTab = 0;
final _pageOptions = [Home(), location(), Profile()];
String getname = "";
String getemail = "";
String getdesignation = "";
String getaccesstoken = "";
// ignore: must_be_immutable
class EmployeeNavigation extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return EmployeeNavigationState();
}
}
class EmployeeNavigationState extends State<EmployeeNavigation> {
var email;
var designation;
var date;
bool valuefirst = false;
String backtext = "";
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
if (_selectedTab == 0) {
return true;
}
setState(() {
_selectedTab = 0;
});
return false;
},
child: Scaffold(
drawer: NavigationDrawerWidget(), //this is a drawer file
body: _pageOptions[_selectedTab],
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.blue[50],
type: BottomNavigationBarType.fixed,
currentIndex: _selectedTab,
onTap: (value) {
print(value);
setState(() {
_selectedTab = value;
});
},
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: "Home"),
BottomNavigationBarItem(
icon: Icon(Icons.location_on), label: "Location"),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: "Profile",
),
],
)));
}
}
i am getting response of both api, but can't navigate to other screen.

myPrefs doesn't seem to be initialized !!!
Try:
Future login() async {
Dio dio = new Dio();
var myPrefs = await SharedPreferences.getInstance();
...
}

Related

Debugging authStateChanges() stream leading to deauthentication due to async call not finishing on time

The previous developer has implemented an authentication mechanism that's very hit or miss when it comes to successfully authenticating users. I've been trying to debug the issues, but this last one persists.
The logic for the authentication is the following:
Users opens the app and is navigated to the splash screen
The splash screen's cubit makes an async request to get an AuthModel that contains the Firebase user's UUID and if successful, emits an "already logged in" state
The splash screen then reacts to the state and navigates the user to the main screen
The main screen seems to listen for event emissions by the AppBloc & MainBloc in order to determine if the user can be shown the actual content.
The issue is that although steps (1), (2) & (3) seem to initially work, after running the app in debug mode a lot, the AppBloc's on<AppEventAuthModelChanged$>(_onUserChanged); runs after receiving a new AuthModel emission and inside the _onUserChanged function, we land on the return emit(const AppState.unauthenticated()) statement. After observing the debugger, this is caused because the _authRepository.token != null expression is false at that point (but becomes true later).
This token is retrieved via the await _connectToServer(firebaseUser: firebaseUser); call inside the Auth Repository Impl's_ensureUser() function that runs based on the authStateChanges() stream via the getter.
Now, I have observed that the _ensureUser() is run multiple times and always seems to emit a valid Firebase user. However, the call to the _connectToServer() seems to take too long to finish (in order to initialize the token field), and thus the AppBloc's logic in the _onUserChanged call fails. What would be a valid approach so that the call has finished in order for us to be sure that the token was either retrieved or that something actually went wrong?
Is the code unsalvageable?
Splash_component.dart for the splash_screen.dart:
class SplashComponent extends StatefulWidget {
const SplashComponent({Key? key}) : super(key: key);
#override
State<SplashComponent> createState() => _SplashComponentState();
}
class _SplashComponentState extends State<SplashComponent> {
SplashCubit get _cubit => context.read<SplashCubit>();
bool _isTextVisible = false;
#override
void initState() {
super.initState();
_cubit.init();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.red,
body: BlocConsumer<SplashCubit, SplashState>(
listener: (BuildContext context, SplashState state) {
state.maybeWhen(
newUser: () async {
setState(() => _isTextVisible = true);
//await Future<void>.delayed(const Duration(seconds: 1));
NavigatorUtils.replaceToAuthScreen(context);
},
alreadyLoggedIn: () async {
setState(() => _isTextVisible = true);
//await Future<void>.delayed(const Duration(seconds: 1));
NavigatorUtils.replaceToMainScreen(context);
},
orElse: () {},
);
},
builder: (BuildContext context, SplashState state) {
return state.maybeWhen(
orElse: () {
return Padding(
padding: const EdgeInsets.all(40),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset('assets/images/blob_logo_white_splash.png'),
const SizedBox(height: 24),
AnimatedOpacity(
duration: const Duration(milliseconds: 500),
opacity: _isTextVisible ? 1.0 : 0.0,
child: Text(
'Carry your art with you',
style: montserratRegular22.copyWith(
color: AppColors.white),
),
),
],
),
);
},
);
},
),
);
}
}
Splash_cubit.dart:
class SplashCubit extends Cubit<SplashState> {
SplashCubit({required AuthRepository authRepository})
: _authRepository = authRepository,
super(const SplashState.loading());
final AuthRepository _authRepository;
Future<void> init() async {
try {
final AuthModel? authModel = await _authRepository.authModel.first;
if (authModel != null) {
return emit(const SplashState.alreadyLoggedIn());
}
return emit(const SplashState.newUser());
} catch (_) {
return emit(const SplashState.newUser());
}
}
}
Main_component.dart for the main_screen.dart:
const double _iconSize = 24;
class MainComponent extends StatelessWidget {
const MainComponent({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return BlocListener<AppBloc, AppState>(
listenWhen: (AppState previous, AppState current) {
return previous is AppStateAuthenticated$ &&
current is AppStateUnauthenticated$;
},
listener: (BuildContext context, AppState state) {
state.maybeWhen(
unauthenticated: () => NavigatorUtils.replaceToAuthScreen(context),
orElse: () {},
);
},
child: BlocBuilder<MainCubit, MainState>(
builder: (BuildContext context, MainState state) {
final MainCubit cubit = context.read<MainCubit>();
final AppState appState = context.watch<AppBloc>().state;
final TabType tab = state.tab;
return Scaffold(
body: appState.maybeWhen(
authenticated: (_) {
return SafeArea(
child: IndexedStack(
index: tab.index,
children: const <Widget>[
AlbumScreen(),
CameraScreen(),
ExploreScreen(),
],
),
);
},
unauthenticated: () {
NavigatorUtils.replaceToAuthScreen(context);
return const SizedBox.shrink();
},
orElse: () => const SizedBox.shrink(),
),
bottomNavigationBar: BottomNavigationBar(
onTap: (int index) => cubit.changeTab(TabType.values[index]),
currentIndex: cubit.state.tab.index,
elevation: 0,
selectedItemColor: AppColors.red,
selectedFontSize: 12,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Image.asset(TabType.album.asset,
color: AppColors.grey,
height: _iconSize,
width: _iconSize),
activeIcon: Image.asset(TabType.album.asset,
color: AppColors.red,
height: _iconSize,
width: _iconSize),
label: 'ALBUM',
),
BottomNavigationBarItem(
icon: Image.asset(
TabType.camera.asset,
color: AppColors.grey,
height: _iconSize,
width: _iconSize,
),
activeIcon: Image.asset(
TabType.camera.asset,
color: AppColors.red,
height: _iconSize,
width: _iconSize,
),
label: 'CAMERA',
),
BottomNavigationBarItem(
icon: Image.asset(
TabType.explore.asset,
color: AppColors.grey,
height: _iconSize,
width: _iconSize,
),
activeIcon: Image.asset(
TabType.explore.asset,
color: AppColors.red,
height: _iconSize,
width: _iconSize,
),
label: 'EXPLORE',
),
],
),
);
},
),
);
}
}
app_bloc.dart:
class AppBloc extends Bloc<AppEvent, AppState> {
AppBloc({
required AuthRepository authRepository,
required UserRepository userRepository,
required ArtworkRepository artworkRepository,
required ArtistRepository artistRepository,
required GalleryRepository galleryRepository,
required VenueRepository venueRepository,
}) : _authRepository = authRepository,
_userRepository = userRepository,
_artworkRepository = artworkRepository,
_artistRepository = artistRepository,
_galleryRepository = galleryRepository,
_venueRepository = venueRepository,
super(const AppState.initial()) {
on<AppEventAuthModelChanged$>(_onUserChanged);
on<AppEventLogout$>(_onLogout);
_authModelSubscription = _authRepository.authModel.listen(
(AuthModel? authModel) => add(AppEvent.authModelChanged(authModel)),
);
}
final AuthRepository _authRepository;
final UserRepository _userRepository;
final ArtworkRepository _artworkRepository;
final ArtistRepository _artistRepository;
final GalleryRepository _galleryRepository;
final VenueRepository _venueRepository;
late StreamSubscription<AuthModel?> _authModelSubscription;
Future<void> _onUserChanged(
AppEventAuthModelChanged$ event, Emitter<AppState> emit) async {
final AuthModel? authModel = event.authModel;
if (authModel != null &&
state is! AppStateUnauthenticated$ &&
_authRepository.token != null) {
final String token = _authRepository.token!;
_userRepository.token = token;
_artworkRepository.token = token;
_artistRepository.token = token;
_galleryRepository.token = token;
_venueRepository.token = token;
await _artworkRepository.getSavedArtworks();
final User user = await _userRepository.getUserDetails();
return emit(AppState.authenticated(user: user));
} else {
_authRepository.token = null;
_artworkRepository.token = null;
_artistRepository.token = null;
return emit(const AppState.unauthenticated());
}
}
Future<void> _onLogout(AppEventLogout$ event, Emitter<AppState> emit) async {
unawaited(_authRepository.logOut());
emit(const AppState.unauthenticated());
}
#override
Future<void> close() {
_authModelSubscription.cancel();
return super.close();
}
}
auth_repository_impl.dart:
class AuthRepositoryImpl implements AuthRepository {
AuthRepositoryImpl({required DioClient dioClient}) : _client = dioClient;
final DioClient _client;
final auth.FirebaseAuth _auth = auth.FirebaseAuth.instance;
#override
String? token;
bool isFetchingToken = false;
#override
Stream<AuthModel?> get authModel {
return MergeStream<auth.User?>(
<Stream<auth.User?>>[
_auth.authStateChanges(),
],
)
.startWith(_auth.currentUser) //
.switchMap<AuthModel?>(_ensureUser)
.share()
.distinct();
}
Stream<AuthModel?> _ensureUser(auth.User? firebaseUser) async* {
if (firebaseUser == null) {
yield* Stream<AuthModel?>.value(null);
return;
}
if (token == null && !isFetchingToken) {
await _connectToServer(firebaseUser: firebaseUser);
}
yield* Stream<AuthModel?>.value(AuthModel(uid: firebaseUser.uid));
}
Future<void> _connectToServer(
{auth.UserCredential? userCredential, auth.User? firebaseUser}) async {
try {
String? firebaseToken;
isFetchingToken = true;
if (userCredential != null) {
firebaseToken = await userCredential.user?.getIdToken();
} else {
firebaseToken = await firebaseUser?.getIdToken();
}
final String email =
userCredential?.user?.email ?? firebaseUser?.email ?? '';
final String baseUrl = getIt<AppRepository>().env == Env.staging
? stagingUrl
: productionUrl;
final ApiResponse response = await _client.httpCall(
baseUrl: baseUrl,
path: '/mobile/login',
httpMethod: HttpMethod.POST,
queryParameters: <String, dynamic>{
'email': email,
'token': firebaseToken,
},
);
final Map<String, dynamic> data = response.data;
token = data['message'];
isFetchingToken = false;
} on DioError catch (ex) {
if (ex.type == DioErrorType.connectTimeout) {
throw Exception('Connection Timeout Exception');
}
throw Exception(ex.message);
}
}
#override
Future<void> logOut() async => await _auth.signOut();
#override
Future<void> registerWithEmailAndPassword(
{required String email, required String password}) async {
final auth.UserCredential userCredential =
await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
await _connectToServer(userCredential: userCredential);
}
#override
Future<void> loginWithApple() async {
final String rawNonce = NonceGenerator.generateNonce();
final String nonce = NonceGenerator.sha256ofString(rawNonce);
final AuthorizationCredentialAppleID appleCredential =
await SignInWithApple.getAppleIDCredential(
scopes: <AppleIDAuthorizationScopes>[
AppleIDAuthorizationScopes.email,
AppleIDAuthorizationScopes.fullName
],
nonce: nonce,
);
final auth.OAuthCredential oauthCredential =
auth.OAuthProvider('apple.com').credential(
idToken: appleCredential.identityToken,
rawNonce: rawNonce,
);
final auth.UserCredential userCredential =
await auth.FirebaseAuth.instance.signInWithCredential(oauthCredential);
await _connectToServer(userCredential: userCredential);
}
#override
Future<void> loginWithEmailAndPassword(
{required String email, required String password}) async {
try {
final auth.UserCredential userCredential =
await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
await _connectToServer(userCredential: userCredential);
log('z1z:: Server token $token');
} catch (e) {
rethrow;
}
}
#override
Future<void> resetPassword({required String email}) async =>
await _auth.sendPasswordResetEmail(email: email);
}

How do i access this variable in flutter?

I am using a barcode package in flutter and it has a variable in it's class it is showing nothing when used.
this is the code of the model:
class ScanResult {
ScanResult({
this.type = ResultType.Barcode,
this.rawContent = '',
this.format = BarcodeFormat.unknown,
this.formatNote = '',
});
final ResultType type;
final String rawContent;
final BarcodeFormat format;
final String formatNote;
}
And this is the code of the screen:
class _MyAppState extends State<MyApp> {
int total = 22;
// todo scanner
ScanResult scanResult = ScanResult();
var _aspectTolerance = 0.00;
var _numberOfCameras = 0;
var _selectedCamera = -1;
var _useAutoFocus = true;
var _autoEnableFlash = false;
static final _possibleFormats = BarcodeFormat.values.toList()
..removeWhere((e) => e == BarcodeFormat.unknown);
List<BarcodeFormat> selectedFormats = [..._possibleFormats];
Future<void> _scan() async {
try {
final result = await BarcodeScanner.scan(
options: ScanOptions(
restrictFormat: selectedFormats,
useCamera: _selectedCamera,
autoEnableFlash: _autoEnableFlash,
android: AndroidOptions(
aspectTolerance: _aspectTolerance,
useAutoFocus: _useAutoFocus,
),
),
);
setState(() { scanResult = result;});
} on PlatformException catch (e) {
setState(() {
scanResult = ScanResult(
type: ResultType.Error,
format: BarcodeFormat.unknown,
rawContent: e.code == BarcodeScanner.cameraAccessDenied
? 'The user did not grant the camera permission!'
: 'Unknown error: $e',
);
});
}
}
Widget build(BuildContext context) {
final scanResult = this.scanResult;
return MaterialApp(
title: 'Material App',
home: Scaffold(
appBar: AppBar(
title: const Text('Material App Bar'),
),
body: Center(
child: Container(
child: Column(
children: [
TextButton(
onPressed: () async {
await _scan();
setState(() {
if (total.toString() == scanResult.rawContent) {
print("success");
print(scanResult.rawContent);
} else {
print("shitt");
print(total.toString());
print(scanResult.rawContent);
}
});
},
child: Text("scan")),
Text(scanResult.rawContent),
],
),
),
),
),
);
}
}
the if statement is not working, and print(scanResult.rawContent) not working, please help I don't know how to access that variables. I am new to flutter.

Why my observable variable can not update the "main.dart" in Flutter using GetX?

The following code is my auth_controller.dart code. I see the response comes from the backend works and it is what was expected and I have token and the isAuth value changes to true after I submit the form:
enum AuthMode { Signup, Login }
class AuthController extends GetxController
with GetSingleTickerProviderStateMixin {
static AuthController instance = Get.find();
Rx<dynamic>? authMode = AuthMode.Login.obs;
RxBool? isLoading = false.obs;
String? _token;
DateTime? _expiryDate;
String? _userId;
Timer? _authTimer;
final _isAuth = false.obs;
AnimationController? controller;
Animation<Offset>? slideAnimation;
Animation<double>? opacityAnimation;
// late TextEditingController passwordController;
// final key = GlobalKey<FormState>();
#override
void onInit() {
super.onInit();
controller = AnimationController(
vsync: this,
duration: const Duration(
milliseconds: 300,
),
);
slideAnimation = Tween<Offset>(
begin: const Offset(0, -1.5),
end: const Offset(0, 0),
).animate(
CurvedAnimation(
parent: controller as Animation<double>,
curve: Curves.fastOutSlowIn,
),
);
opacityAnimation = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller as Animation<double>,
curve: Curves.easeIn,
),
);
// _heightAnimation.addListener(() => setState(() {}));
// passwordController = TextEditingController();
}
#override
void onClose() {
super.onClose();
// passwordController.dispose();
}
bool get isAuth {
_isAuth.value = token != null;
return _isAuth.value;
}
String? get token {
if (_expiryDate != null &&
_expiryDate!.isAfter(DateTime.now()) &&
_token != null) {
return _token;
}
return null;
}
String? get userId {
return _userId;
}
Future<void> _authenticate(
String email, String password, String urlSegment) async {
// print('app is here!!!5555');
final host = UniversalPlatform.isAndroid ? '10.0.2.2' : '127.0.0.1';
final url = Uri.parse('http://$host:8000/api/$urlSegment');
//final url = Uri.parse('http://10.0.2.2:8000/api/$urlSegment');
//final url = Uri.parse('http://127.0.0.1:8000/api/$urlSegment');
try {
final http.Response response = await http.post(
url,
headers: {"Content-Type": "application/json"},
body: json.encode(
{
'email': email,
'password': password,
//'returnSecureToken': true,
},
),
);
print('this is responsde ');
print(json.decode(response.body));
final responseData = json.decode(response.body);
if (responseData['error'] != null) {
throw HttpException(responseData['error']['message']);
} else {
_token = responseData['idToken'];
_userId = responseData['id'];
_expiryDate = DateTime.now().add(
Duration(
seconds: responseData['expiresIn'],
),
);
}
_autoLogout();
// update();
final prefs = await SharedPreferences.getInstance();
final userData = json.encode(
{
'token': _token,
'userId': _userId,
'expiryDate': _expiryDate!.toIso8601String(),
},
);
prefs.setString('userData', userData);
// print(prefs.getString('userData'));
} catch (error) {
throw error;
}
}
Future<void> signup(String email, String password) async {
return _authenticate(email, password, 'signup');
}
Future<void> login(String email, String password) async {
return _authenticate(email, password, 'login');
}
Future<bool> tryAutoLogin() async {
final prefs = await SharedPreferences.getInstance();
if (!prefs.containsKey('userData')) {
return false;
}
final Map<String, Object> extractedUserData = Map<String, Object>.from(
json.decode(prefs.getString('userData') as String));
final expiryDate =
DateTime.parse(extractedUserData['expiryDate'] as String);
if (expiryDate.isBefore(DateTime.now())) {
return false;
}
_token = extractedUserData['token'] as String;
_userId = extractedUserData['userId'] as String;
_expiryDate = expiryDate;
// update();
_autoLogout();
return true;
}
Future<void> logout() async {
_token = null;
_userId = null;
_expiryDate = null;
if (_authTimer != null) {
_authTimer!.cancel();
_authTimer = null;
}
// update();
final prefs = await SharedPreferences.getInstance();
// prefs.remove('userData');
prefs.clear();
}
void _autoLogout() {
if (_authTimer != null) {
_authTimer!.cancel();
}
final timeToExpiry = _expiryDate!.difference(DateTime.now()).inSeconds;
_authTimer = Timer(Duration(seconds: timeToExpiry), logout);
}
}
But it will still sticks to the Authentication page and won't go to the Home page as I defined in my main.dart file:
void main() {
Get.put(MenuController());
Get.put(NavigationController());
Get.put(AuthController());
Get.put(AuthCard);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Obx(() => GetMaterialApp(
initialRoute: AuthController.instance.isAuth
? homeScreenRoute
: authenticationScreenRoute,
unknownRoute: GetPage(
name: '/not-found',
page: () => PageNotFound(),
transition: Transition.fadeIn),
getPages: [
GetPage(
name: rootRoute,
page: () {
return SiteLayout();
}),
GetPage(
name: authenticationScreenRoute,
page: () => const AuthenticationScreen()),
GetPage(name: homeScreenRoute, page: () => const HomeScreen()),
],
debugShowCheckedModeBanner: false,
title: 'BasicCode',
theme: ThemeData(
scaffoldBackgroundColor: light,
textTheme: GoogleFonts.mulishTextTheme(Theme.of(context).textTheme)
.apply(bodyColor: Colors.black),
pageTransitionsTheme: const PageTransitionsTheme(builders: {
TargetPlatform.iOS: FadeUpwardsPageTransitionsBuilder(),
TargetPlatform.android: FadeUpwardsPageTransitionsBuilder(),
}),
primarySwatch: Colors.blue,
),
));
}
}
What is the problem and how can I fix that?
Initial route is set only once when the app starts, and based on what you've shown isAuth will always return false by the time GetMaterialApp is built.
Wrapping your whole app in an Obx is not going to trigger a navigation based on what you have set for the initial route. Its way past that by the time you've run any login attempt.
You need to manually navigate to home screen after a successful login.
Instead I suggest checking for a non null token from storage before the app starts and if its null, go to your auth page and if not, go to your home page. Neither of which would require an Obx around GetMaterialApp because you need to properly initialize _isAuth and await it before runApp().
I could simply resolve the problem by adding following lines to my main.dart file:
home: Obx(() => AuthController.instance.isAuth
? const HomeScreen()
: const AuthenticationScreen()),
And removing the followings:
initialRoute
: AuthController.instance.isAuth
? homeScreenRoute
: authenticationScreenRoute,
I also deleted the Obx before the GetMaterialApp().
The new main.dart looks like this:
void main() {
Get.put(MenuController());
Get.put(NavigationController());
Get.put(AuthController());
Get.put(AuthCard);
AuthController.instance.isAuth ? runApp(MyApp()) : runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GetMaterialApp(
unknownRoute: GetPage(
name: '/not-found',
page: () => PageNotFound(),
transition: Transition.fadeIn),
getPages: [
GetPage(
name: rootRoute,
page: () {
return SiteLayout();
}),
GetPage(
name: authenticationScreenRoute,
page: () => const AuthenticationScreen()),
GetPage(name: homeScreenRoute, page: () => const HomeScreen()),
],
debugShowCheckedModeBanner: false,
title: 'BasicCode',
theme: ThemeData(
scaffoldBackgroundColor: light,
textTheme: GoogleFonts.mulishTextTheme(Theme.of(context).textTheme)
.apply(bodyColor: Colors.black),
pageTransitionsTheme: const PageTransitionsTheme(builders: {
TargetPlatform.iOS: FadeUpwardsPageTransitionsBuilder(),
TargetPlatform.android: FadeUpwardsPageTransitionsBuilder(),
}),
primarySwatch: Colors.blue,
),
home: Obx(() => AuthController.instance.isAuth
? const HomeScreen()
: const AuthenticationScreen()),
);
}
}

Code doesn't even enter the onIceCandiate() while answering the SDP for webRTC in flutter

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!

How to send dynamic url from DataProvider to PDF Flutter

I'm new to Flutter. I have DataProvider which consists of Pdf data like it's Title, pdfURL and etc and I created a ListView on that ListView I have some items.
Whenever I click on any item it should open specified pdf url on PDF VIEWER. I want to pass that data dynamically to the getFileFromUrl; how should I pass that data.
This is my DataProvider class:
class DataProvider with ChangeNotifier{
List<PdfBook> _pdfItems = [
PdfBook(
id: 'p1',
title: 'PDF Bookmark Sample',
pdfUrl: 'https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf',
avatar: 'T',
),
PdfBook(
id: 'p2',
title: 'PDF 995',
pdfUrl: 'http://www.pdf995.com/samples/pdf.pdf',
avatar: 'T2',
),
];
List<PdfBook> get pdfItems{
return [..._pdfItems];
}
PdfBook findById(String id){
return _pdfItems.firstWhere((item) => item.id == id);
}
}
This is my PdfViewState:
class PdfItem extends StatefulWidget {
#override
_PdfItemState createState() => _PdfItemState();
}
class _PdfItemState extends State<PdfItem> {
String assetPDFPath = "";
String urlPDFPath = "";
#override
void initState() {
super.initState();
getFileFromAsset("assets/mypdf.pdf").then((f) {
setState(() {
assetPDFPath = f.path;
print(assetPDFPath);
});
});
getFileFromUrl("http://www.pdf995.com/samples/pdf.pdf").then((f) {
setState(() {
urlPDFPath = f.path;
print(urlPDFPath);
});
});
}
Future<File> getFileFromAsset(String asset) async {
try {
var data = await rootBundle.load(asset);
var bytes = data.buffer.asUint8List();
var dir = await getApplicationDocumentsDirectory();
File file = File("${dir.path}/mypdf.pdf");
File assetFile = await file.writeAsBytes(bytes);
return assetFile;
} catch (e) {
throw Exception("Error opening asset file");
}
}
Future<File> getFileFromUrl(String url) async {
try {
var data = await http.get(url);
var bytes = data.bodyBytes;
var dir = await getApplicationDocumentsDirectory();
File file = File("${dir.path}/mypdfonline.pdf");
File urlFile = await file.writeAsBytes(bytes);
return urlFile;
} catch (e) {
throw Exception("Error opening url file");
}
}
#override
Widget build(BuildContext context) {
final pdf = Provider.of<PdfBook>(context, listen: false);
return ListTile(
title: Text(pdf.title),
onTap: () {
if (urlPDFPath != null) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
PdfViewPage(path: urlPDFPath)));
}
},
leading: CircleAvatar(
child: Text(pdf.avatar),
backgroundColor: Theme.of(context).accentColor,
),
trailing: Icon(Icons.arrow_right),
);
}
}
This is my ListView Class:
class PdfListView extends StatelessWidget {
#override
Widget build(BuildContext context) {
final pdfData = Provider.of<DataProvider>(context);
final pdf = pdfData.pdfItems;
return Scaffold(
appBar: AppBar(
title: Text("PDF Books"),
),
body: ListView.builder(
itemCount: pdf.length,
itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
value: pdf[i],
child: PdfItem(),
),
),
);
}
}