Flutter: TCP socket connection fails on real device - android

I'm posting this even though I already solved the issue, because I spent more than an hour trying to figure out what was causing it, and might help someone else.
I have a simple application that connects to a server through a TCP socket. It works fine inside the debugger with the device emulator, but when I deploy it on a real device it fails to connect immediately.
Further investigations led me to finding out that the socket was actually throwing the following exception:
SocketException: Connection failed (OS Error: Operation not permitted, errno = 1), address = 192.168.1.46, port = 40001
Code sample
Connect button opens a socket on _host:_port
Send Radar Distance button sends a message formatted like {"distance": _distance, "angle": _angle} on the socket.
Status and Message fields show info about the socket status and eventually useful infos.
main.dart
import 'package:flutter/material.dart';
import 'views/view_test.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const ViewTest(),
);
}
}
views/view_test.dart
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
const List<String> materialTypes = <String>['PLASTIC', 'GLASS'];
class ViewTest extends StatefulWidget {
const ViewTest({Key? key}) : super(key: key);
#override
State<ViewTest> createState() => _ViewTestState();
}
class _ViewTestState extends State<ViewTest> {
String _distance = "";
String _angle = "";
String _status = "";
String _message = "";
Socket? _socket;
final String _host = "192.168.1.46";
final int _port = 4001;
Future<void> _sendMessage(String message) async {
print("Sent message $message");
_socket!.write(message);
await Future.delayed(const Duration(seconds: 1));
}
void _connect(String ip, int port) async {
Socket? sock;
try {
sock =
await Socket.connect(ip, port, timeout: const Duration(seconds: 3));
_socket = sock;
setState(() {
_status = "Connected to $ip:$port.";
_message = "";
});
// listen for responses from the server
_socket!.listen(
// handle data from the server
(Uint8List data) {
final serverResponse = String.fromCharCodes(data);
setState(() {
_message = serverResponse;
});
print(serverResponse);
},
// handle errors
onError: (error) {
setState(() {
_status = "Disconnected.";
_message = "Error: $error";
});
print("Error: $error");
_socket!.destroy();
_socket = null;
},
// handle server ending connection
onDone: () {
setState(() {
_status = "Disconnected.";
_message = 'Server left.';
});
print('Server left.');
_socket!.destroy();
_socket = null;
},
);
} catch (e) {
setState(() {
_status = "Connection failed.";
_message = e.toString();
});
print("Error: ${e.toString()}");
}
}
void _disconnect() {
if (_socket != null) _socket!.destroy();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Center(child: Text("Radar Test")),
),
body: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 50.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () {
setState(() {
_status = "";
_message = "";
});
_disconnect();
_connect(_host, _port);
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
_socket == null ? 'Connect' : 'Reconnect',
style: const TextStyle(fontSize: 22.0),
),
),
),
const SizedBox(height: 50.0),
TextField(
onChanged: (text) {
_distance = text;
},
keyboardType:
const TextInputType.numberWithOptions(decimal: false),
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.allow(
RegExp(r'[0-9]+'), // this regex allows only decimal numbers
)
],
decoration: const InputDecoration(
hintText: '100',
border: UnderlineInputBorder(),
labelText: 'Distance',
),
),
TextField(
onChanged: (text) {
_angle = text;
},
keyboardType:
const TextInputType.numberWithOptions(decimal: false),
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.allow(
RegExp(r'[0-9]+'), // this regex allows only decimal numbers
)
],
decoration: const InputDecoration(
hintText: '90',
border: UnderlineInputBorder(),
labelText: 'Angle',
),
),
const SizedBox(height: 50.0),
Text("Status: $_status"),
Text("Message: $_message"),
],
),
),
),
floatingActionButton: ElevatedButton(
onPressed: _socket == null
? null
: () {
// test
_sendMessage(
'{"distance": ${_distance.isEmpty ? 100 : int.parse(_distance)}, "angle": ${_angle.isEmpty ? 90 : int.parse(_angle)}}');
},
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Text(
'Send Radar Distance',
style: TextStyle(fontSize: 22.0),
),
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
}

Turns out I had to give the application Internet permissions, as stated in Flutter docs about Networking. So I added the following line to AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>
NB: AndroidManifest.xml manifest is located in the following location:
android > app > src > main > AndroidManifest.xml

Related

Json Empty after parse even though status 200

I am trying to parse a JSON after doing a HTTP GET request for my flutter app, however when it is parsed, the body shows as empty, this is the parsing code
urlHausParseBox() {
Future<_GoneSmishinState> fetchUrlResponse() async {
String url = myController.text;
final response = await http.post(
Uri.parse("https://urlhaus-api.abuse.ch/v1/url/"),
headers: <String, String>{
'Accept': 'application/json',
},
body: (<String, String>{
'url': url,
'query_status': query_status,
'url_status' : url_status,
//'status' : status,
//'urlStatus' : urlStatus,
}));
After this I have a check for the 200 status, and when recieved will return this to use after the fact, I printed the fields 'query_status' and 'url_status' but they came up empty so I printed what I was returning here
if (response.statusCode == 200) {
print (_GoneSmishinState.fromJson(jsonDecode(response.body)));
return _GoneSmishinState.fromJson(jsonDecode(response.body));
but all that is printed out is _GoneSmishinState#23f48(lifecycle state: created, no widget, not mounted)
which is not what is supposed to be returned by the HTTP GET request
The rest of my code is below
import 'dart:convert';
import 'package:validators/validators.dart';
import 'package:flutter/material.dart';
import 'package:sms/sms.dart';
import 'dart:io';
import 'dart:developer' as developer;
import 'package:http/http.dart' as http;
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
String url = "https://urlhaus-api.abuse.ch/v1/urls/recent/"; //address for URL file
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key:key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: "Gone Smishin'",
home: GoneSmishin(),
);
}
}
class GoneSmishin extends StatefulWidget {
const GoneSmishin({Key? key}) : super(key: key);
State<GoneSmishin> createState() {
return _GoneSmishinState(url_status: '', query_status: '');
}
}
class _GoneSmishinState extends State<GoneSmishin> {
String message = "";
String word = "";
bool isOn = false;
final myController = TextEditingController();
#override
void dispose() {
myController.dispose();
super.dispose();
}
_GoneSmishinState({
required this.query_status,
required this.url_status,
});
final String query_status;
final String url_status;
factory _GoneSmishinState.fromJson(Map<String, dynamic> json) {
return _GoneSmishinState(
query_status: json["query_status"],
url_status: json["url_status"],
);
}
urlHausParseBox() {
Future<_GoneSmishinState> fetchUrlResponse() async {
String url = myController.text;
final response = await http.post(
Uri.parse("https://urlhaus-api.abuse.ch/v1/url/"),
headers: <String, String>{
'Accept': 'application/json',
},
body: (<String, String>{
'url': url,
'query_status': query_status,
'url_status' : url_status,
}));
if (response.statusCode == 200) {
print (_GoneSmishinState.fromJson(jsonDecode(response.body)));
return _GoneSmishinState.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load website');
}
}
fetchUrlResponse();
if (query_status == "ok" && url_status == "online") {
const Text ('Found in URLHause Database - Probably Smishing');
print("found");
} else if (query_status == "ok" && url_status == "offline") {
const Text ('Found in URLHaus, not online');
print("found offline");
} else {
const Text ('Found Nothing');
print("not found");
print (query_status);
print (url_status);
}
_pushInput() {
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (context) {
return Scaffold(
appBar: AppBar(
title: const Text ('Submit a Link')
),
body: (
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField (
controller: myController,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter your Link Text',
contentPadding: EdgeInsets.symmetric(
vertical: 40, horizontal: 20),
),
),
ElevatedButton(
onPressed: () {
urlHausParseBox();
},
child: const Text('Submit')
)
]
)
));
}
)
);
}
#override
var buttonText = 'OFF';
String textHolder = "App is Off";
changeTextON() {
setState(() {
textHolder = "App is ON";
});
isOn == true;
}
changeTextOFF() {
setState(() {
textHolder = "App is OFF";
});
isOn == false;
}
Widget build(BuildContext context) {
final ButtonStyle outlineButtonStyle = OutlinedButton.styleFrom(
primary: Colors.black87,
minimumSize: Size(200, 130),
padding: EdgeInsets.symmetric(horizontal: 200),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(300)),
),
).copyWith(
side: MaterialStateProperty.resolveWith<BorderSide>(
(Set<MaterialState> states) {
return BorderSide(
color: Theme.of(context).colorScheme.primary,
width: 1,
);
},
),
);
return Scaffold(
appBar: AppBar(
title: const Text("Gone Smishin'"),
actions: [
IconButton(
icon: const Icon(Icons.add_link),
onPressed: _pushInput,
tooltip: 'Submit a Link'
)
],
backgroundColor: Colors.red,
),
body: Column (
children: [
Container(
padding: EdgeInsets.fromLTRB(50, 50, 50, 50),
child: Text('$textHolder',
style: TextStyle(fontSize: 50)
),
),
Container(
//child: Text(result)
),
TextButton(
style: outlineButtonStyle,
onPressed: () {
changeTextON();
},
child: Text('ON')
),
TextButton(
style: outlineButtonStyle,
onPressed: () {
changeTextOFF();
},
child: Text("OFF"),
)
]
),
);
}
}
Change this:
_GoneSmishinState({
required this.query_status,
required this.url_status,
});
final String query_status;
final String url_status;
factory _GoneSmishinState.fromJson(Map<String, dynamic> json) {
return _GoneSmishinState(
query_status: json["query_status"],
url_status: json["url_status"],
);
}
to this:
_GoneSmishinState();
var queryStatus = '';
var urlStatus = '';
and this:
if (response.statusCode == 200) {
print (_GoneSmishinState.fromJson(jsonDecode(response.body)));
return _GoneSmishinState.fromJson(jsonDecode(response.body));
}
to:
if (response.statusCode == 200) {
setState(() {
final decoded = json.decode(response.body);
queryStatus = decoded['query_status'];
urlStatus = decoded['url_status'];
}
);
}
And, finally, patch up any unused/misnamed variables. As an aside, it's difficult to read functions declared inside other functions. Is fetchUrlResponse inside urlHausParseBox? Move it outside.

why firebase auth doesn't work on my new samsung phone?

i am beginner in flutter .
i have a probleme firebase auth with email work on all phones and emulators but on my new samsung phone it doesn't .
here is the error message
/FirebaseAuth( 2571): [FirebaseAuth:] Preparing to create service connection to fallback implementation
W/System ( 2571): Ignoring header X-Firebase-Locale because its value was null.
2
I/System.out( 2571): (HTTPLog)-Static: isSBSettingEnabled false
D/InputMethodManager( 2571): HSIFW - flag : 0
here is my auth screen where the user sign up or login.
import 'package:flutter/material.dart';
import 'package:m51/common/constants.dart';
import 'package:m51/common/loading.dart';
import 'package:m51/services/authentication.dart';
class AuthenticateScreen extends StatefulWidget {
#override
_AuthenticateScreenState createState() => _AuthenticateScreenState();
}
class _AuthenticateScreenState extends State<AuthenticateScreen> {
final AuthenticationService _auth = AuthenticationService();
final _formKey = GlobalKey<FormState>();
String error = '';
bool loading = false;
final emailController = TextEditingController();
final nameController = TextEditingController();
final passwordController = TextEditingController();
bool showSignIn = true;
#override
void dispose() {
nameController.dispose();
emailController.dispose();
passwordController.dispose();
super.dispose();
}
void toggleView() {
setState(() {
_formKey.currentState?.reset();
error = '';
emailController.text = '';
nameController.text = '';
passwordController.text = '';
showSignIn = !showSignIn;
});
}
#override
Widget build(BuildContext context) {
return loading
? Loading()
: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.blueGrey,
elevation: 0.0,
title: Text(showSignIn ? 'Sign in to Water Social' : 'Register to Water Social'),
actions: <Widget>[
TextButton.icon(
icon: Icon(
Icons.person,
color: Colors.white,
),
label: Text(showSignIn ? "Register" : 'Sign In',
style: TextStyle(color: Colors.white)),
onPressed: () => toggleView(),
),
],
),
body: Container(
padding: EdgeInsets.symmetric(vertical: 15.0, horizontal: 30.0),
child: Form(
key: _formKey,
child: Column(
children: [
!showSignIn
? TextFormField(
controller: nameController,
decoration: textInputDecoration.copyWith(hintText: 'name'),
validator: (value) =>
value == null || value.isEmpty ? "Enter a name" : null,
)
: Container(),
!showSignIn ? SizedBox(height: 10.0) : Container(),
TextFormField(
controller: emailController,
decoration: textInputDecoration.copyWith(hintText: 'email'),
validator: (value) =>
value == null || value.isEmpty ? "Enter an email" : null,
),
SizedBox(height: 10.0),
TextFormField(
controller: passwordController,
decoration: textInputDecoration.copyWith(hintText: 'password'),
obscureText: true,
validator: (value) => value != null && value.length < 6
? "Enter a password with at least 6 characters"
: null,
),
SizedBox(height: 10.0),
ElevatedButton(
child: Text(
showSignIn ? "Sign In" : "Register",
style: TextStyle(color: Colors.white),
),
onPressed: () async {
if (_formKey.currentState?.validate() == true) {
setState(() => loading = true);
var password = passwordController.value.text;
var email = emailController.value.text;
var name = nameController.value.text;
dynamic result = showSignIn
? await _auth.signInWithEmailAndPassword(email, password)
: await _auth.registerWithEmailAndPassword(name, email, password);
if (result == null) {
print ('Please supply a valid email');
}
}
}
),
SizedBox(height: 10.0),
Text(
error,
style: TextStyle(color: Colors.red, fontSize: 15.0),
)
],
),
),
),
);
}
}
i also have that message
I/flutter ( 4866): [firebase_auth/invalid-email] The email address is
badly formatted.
the service code
import 'package:firebase_auth/firebase_auth.dart';
import 'package:urjob/models/user.dart';
import 'package:urjob/services/database.dart';
class AuthenticationService {
final FirebaseAuth _auth = FirebaseAuth.instance;
AppUser? _userFromFirebaseUser(User? user) {
return user != null ? AppUser(user.uid) : null;
}
Stream<AppUser?> get user {
return _auth.authStateChanges().map(_userFromFirebaseUser);
}
Future signInWithEmailAndPassword(String email, String password) async {
try {
UserCredential result =
await _auth.signInWithEmailAndPassword(email: email, password: password);
User? user = result.user;
return _userFromFirebaseUser(user);
} catch (exception) {
print(exception.toString());
return null;
}
}
Future registerWithEmailAndPassword(String name, String email, String password, String profilepic) async {
try {
UserCredential result =
await _auth.createUserWithEmailAndPassword(email: email, password: password);
User? user = result.user;
if (user == null) {
throw Exception("No user found");
} else {
await DatabaseService(user.uid).saveUser(name,profilepic,email);
return _userFromFirebaseUser(user);
}
} catch (exception) {
print(exception.toString());
return null;
}
}
Future signOut() async {
try {
return await _auth.signOut();
} catch (exception) {
print(exception.toString());
return null;
}
}
Future sendreset(String email) async {
try {
return await _auth.sendPasswordResetEmail(email: email);
} catch (exception) {
print(exception.toString());
return null;
}
}
}
Thank you for your help
i found the solution
we need in ma case to add .trim() functions to erase white spaces
https://github.com/FirebaseExtended/flutterfire/issues/1760
Trim text gotten from textfields.
emailController.text.trim(),
passwordController.text.trim()

Auto detect firebase Phone Auth sms code in Flutter

Please read properly and then comment, hope this help full for software community.
I unable to auto detect firebase phone auth sms, when i test in android it's work without doing anything, but when i tried it in flutter sms not detected automatically, require manually type sms code, I did not find any documentation to do this process automatically.
I wan't to know how to detect automatically that sms code from Firebase phone auth
Future<void> verifyPhone() async {
starTimer();
phoneNumber = contryCode+mobile_controller.text;
final PhoneCodeAutoRetrievalTimeout autoReRetrive = (String verId) {
this.verificationId = verId;
};
final PhoneCodeSent smsCodesent = (String verId, [int forceCodeResent]) {
this.verificationId = verId;
pageController.animateToPage(2, duration: Duration(milliseconds: 300), curve: Curves.easeInBack);
/*smsCodeDialog(context).then((value) {
print("VERIFY PHONE SIGN IN");
});
*/
};
final PhoneVerificationCompleted phoneVerificationCompleted =
(PhoneAuthCredential creditional) {
print("VERIFY PHONE AUTH SUCESS");
this.mPhoneCreditional=creditional;
//_controller.animateToPage(3, duration: Duration(milliseconds: 300), curve: Curves.easeIn);
loginIn(creditional);
};
final PhoneVerificationFailed phoneVerificationFailed =
(FirebaseAuthException excepton) {
Fluttertoast.showToast(
msg: excepton.message,
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0
);
print("VERIFY PHONE AUTH FAILED " + excepton.message);
};
FirebaseAuth mAuth=await FirebaseAuth.instance;
mAuth.setLanguageCode("en");
await mAuth.verifyPhoneNumber(
phoneNumber: phoneNumber,
timeout: const Duration(seconds: 45),
verificationCompleted: phoneVerificationCompleted,
verificationFailed: phoneVerificationFailed,
codeSent: smsCodesent,
codeAutoRetrievalTimeout: autoReRetrive);
}
Make sure you have added the debugging, release, sha1, sha256 keys to your firebase project.
Make sure you have enabled appcheck enabled on your firebase project.
Make sure you have enabled the Android Device Verification api on your google cloud console.
The below code is slightly modified from the example code given in firebase auth plugin, for my projects the detecion is working well.
Some devices may fail to detect the otp, but most of the devices will login automatically.
The code which detects the sms is highlighted in sendOtpFn()
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:projdebug/Widgets/Widgets.dart';
class LoginPage extends StatefulWidget {
const LoginPage({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() => LoginPageState();
}
class LoginPageState extends State<LoginPage> {
final FirebaseAuth _auth = FirebaseAuth.instance;
final _phoneNumberController = TextEditingController();
final _smsController = TextEditingController();
String _message = '';
late String _verificationId;
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(onPressed: () {
setState(() {});
}),
body: Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
alignment: Alignment.center,
child: const Text(
'Test sign in with phone number',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
TextFormField(
controller: _phoneNumberController,
decoration: const InputDecoration(
labelText: 'Phone number (+x xxx-xxx-xxxx)',
),
validator: (String? value) {
if (value!.isEmpty) {
return 'Phone number (+x xxx-xxx-xxxx)';
}
return null;
},
),
Container(
padding: const EdgeInsets.symmetric(vertical: 16),
alignment: Alignment.center,
child: Button(
icon: Icons.send,
buttonColor: Colors.deepOrangeAccent[700]!,
text: 'Send otp',
onPressed: sendOtpFn,
),
),
TextField(
controller: _smsController,
decoration:
const InputDecoration(labelText: 'Verification code'),
),
Container(
padding: const EdgeInsets.only(top: 16),
alignment: Alignment.center,
child: Button(
icon: Icons.done,
buttonColor: Colors.deepOrangeAccent[400]!,
onPressed: manualSignInFn,
text: 'Confirm otp',
),
),
CrossFade(
show: _auth.currentUser?.uid != null,
child: Button(
icon: Icons.logout,
buttonColor: Colors.deepOrangeAccent[400]!,
onPressed: () async{
await _auth.signOut();
setState(() {});
},
text: 'LogOut',
),
),
Visibility(
visible: _message.isNotEmpty,
child: Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
_message,
style: const TextStyle(color: Colors.red),
),
),
)
],
),
),
),
);
}
// Example code of how to verify phone number
Future<void> sendOtpFn() async {
setState(() {
_message = '';
});
///This is the code snippet which [detects the code from sms automatically]
PhoneVerificationCompleted verificationCompleted =
(PhoneAuthCredential phoneAuthCredential) async {
setState(() {
_message = phoneAuthCredential.smsCode ?? "";
_smsController.text = _message;
});
await _auth.signInWithCredential(phoneAuthCredential);
Widgets.showToast(
'Phone number automatically verified and user signed in: $phoneAuthCredential');
setState(() {});
};
PhoneVerificationFailed verificationFailed =
(FirebaseAuthException authException) {
setState(() {
_message =
'Phone number verification failed. Code: ${authException.code}. '
'Message: ${authException.message}';
});
};
PhoneCodeSent codeSent =
(String verificationId, [int? forceResendingToken]) async {
Widgets.showToast('Please check your phone for the verification code.');
_verificationId = verificationId;
};
PhoneCodeAutoRetrievalTimeout codeAutoRetrievalTimeout =
(String verificationId) {
_verificationId = verificationId;
};
try {
await _auth.verifyPhoneNumber(
phoneNumber: _phoneNumberController.text,
timeout: const Duration(seconds: 120),
verificationCompleted: verificationCompleted,
verificationFailed: verificationFailed,
codeSent: codeSent,
codeAutoRetrievalTimeout: codeAutoRetrievalTimeout);
} catch (e) {
Widgets.showToast('Time out to Verify Phone Number: $e');
}
}
///Code which will be executed only when a confirm otp button is pressed, ie both phoneNumber & otp is typed by the user manually.
Future<void> manualSignInFn() async {
try {
final PhoneAuthCredential credential = PhoneAuthProvider.credential(
verificationId: _verificationId,
smsCode: _smsController.text,
);
final User user = (await _auth.signInWithCredential(credential)).user!;
Widgets.showToast('Successfully signed in UID: ${user.uid}');
} catch (e) {
print(e);
Widgets.showToast('Failed to sign in');
}
}
}

how to change a text widget's text and color/button's widget text and color dynamically?

Background: I am creating one app, where the internet connection status should be shown to the user dynamically. The result is already showing but I want the background color to be changed dynamically. I tried to implement it in setState() but the text color is not getting changed. and I don't know how to implement setState() inside button without onPressed.
main code:
import 'dart:async';
import 'dart:io';
import 'package:connectivity/connectivity.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:quote_app_one/components/TextStyles_Icons.dart';
import 'package:quote_app_one/utils/HexColor.dart';
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
Color _newAppBgColor = HexColor("#111328");
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) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
systemNavigationBarColor: _newAppBgColor, // navigation bar color
));
String string;
switch (_source.keys.toList()[0]) {
case ConnectivityResult.none:
string = "Offline";
break;
case ConnectivityResult.mobile:
string = "Online";
break;
case ConnectivityResult.wifi:
string = "Online";
}
//This is the setState() I have implemented
String cData = string;
setState(() {
cData = string;
if(cData == "Online"){
return Text("Online",
style: TextStyle(
color: Colors.green
),);
}
else{
return Text("Offline",
style: TextStyle(
color: Colors.red
),);
}
});
return Scaffold(
appBar: AppBar(
title: homeScreenAppBarText,
backgroundColor: _newAppBgColor,
),
backgroundColor: _newAppBgColor,
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(10, 10, 0, 0),
child: cStatusText,
),
Text( cData,
// style: TextStyle(
// color: Colors.white
// ),
)
],
),
),
new Container(
child: Row(
children: [
// custom widgets
],
),
)
],
),
),
);
}
}
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();
}
connectivity_services code:
import 'package:flutter/material.dart';
import 'package:connectivity/connectivity.dart';
class ConnectivityChangeNotifier extends ChangeNotifier {
ConnectivityChangeNotifier() {
Connectivity().onConnectivityChanged.listen((ConnectivityResult result) {
resultHandler(result);
});
}
ConnectivityResult _connectivityResult = ConnectivityResult.none;
String _pageText =
'Currently connected to no network. Please connect to a wifi network!';
ConnectivityResult get connectivity => _connectivityResult;
String get pageText => _pageText;
void resultHandler(ConnectivityResult result) {
_connectivityResult = result;
if (result == ConnectivityResult.none) {
_pageText =
'Currently connected to no network. Please connect to a wifi network!';
} else if (result == ConnectivityResult.mobile) {
_pageText =
'Currently connected to a celluar network. Please connect to a wifi network!';
} else if (result == ConnectivityResult.wifi) {
_pageText = 'Connected to a wifi network!';
}
notifyListeners();
}
void initialLoad() async {
ConnectivityResult connectivityResult =
await (Connectivity().checkConnectivity());
resultHandler(connectivityResult);
}
}
In the image, my code is displaying as offline.. but I want that offline beside status as shown in the image and the background color should change dynamically when the status is online. How can I implement this?
You're almost there, you just need to put all that functionality in the stream listener you have setup and not in the build method.
Since you already have a listener following changes in connectivity status, that listener just needs to update a Color variable. Here's a simplified example.
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
Color _newAppBgColor = HexColor("#111328");
// these 2 get updated on connectivity changes
Color statusColor = Colors.red;
String cData = 'Offline';
Map _source = {ConnectivityResult.none: false};
MyConnectivity _connectivity = MyConnectivity.instance;
#override
void initState() {
super.initState();
_connectivity.initialise();
_connectivity.myStream.listen((source) {
setState(() {
_source = source;
_updateStatusText(); // moved from build method to listener
});
});
}
void _updateStatusText() {
switch (_source.keys.toList()[0]) {
case ConnectivityResult.none:
cData = "Offline";
statusColor = Colors.red;
break;
case ConnectivityResult.mobile:
cData = "Online";
statusColor = Colors.green;
break;
case ConnectivityResult.wifi:
cData = "Online";
statusColor = Colors.green;
}
}
#override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
systemNavigationBarColor: _newAppBgColor, // navigation bar color
));
return Scaffold(
appBar: AppBar(
// title: homeScreenAppBarText, // didn't have the origin of homeScreenAppBarText to test this
title: Text('Demo'),
backgroundColor: _newAppBgColor,
),
backgroundColor: _newAppBgColor,
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(10, 10, 0, 0),
// child: cStatusText, // didn't have the origin of cStatusText to test this
child: Text('status',
style: TextStyle(
color:
statusColor)),
),
Text(
cData, // this updates on change in connectivity status
style: TextStyle(
color:
statusColor, // this updates on change in connectivity status
),
)
],
),
),
new Container(
child: Row(
children: [
// custom widgets
],
),
)
],
),
),
);
}
}

Flutter with Firebase Google sign in: Failed assertion network_image_io.dart

I'm trying to do Firebase authentication and Google sign in using Flutter but I'm getting this error message:
'package:flutter/src/painting/_network_image_io.dart': Failed assertion: line 23 pos 14: 'url != null': is not true.
I can't understand what is wrong, can you help me?
login.dart code snippet
-> calls signInWithGoogle() method from sign_in.dart and return FirstScreen
Widget _signInButton() {
return OutlineButton(
splashColor: Colors.grey,
onPressed: () {
signInWithGoogle().whenComplete(() {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) {
return FirstScreen();
},
),
);
});
},
sign_in.dart code snippet:
-> Here I authenticate the user and try to get logged user name, email and image
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
String name;
String email;
String imageUrl;
Future<String> signInWithGoogle() async {
final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
final AuthResult authResult = await _auth.signInWithCredential(credential);
final FirebaseUser user = authResult.user;
// Checking if email and name is null
assert(user.email != null);
assert(user.displayName != null);
assert(user.photoUrl != null);
name = user.displayName;
email = user.email;
imageUrl = user.photoUrl;
// Only taking the first part of the name, i.e., First Name
if (name.contains(" ")) {
name = name.substring(0, name.indexOf(" "));
}
assert(!user.isAnonymous);
assert(await user.getIdToken() != null);
final FirebaseUser currentUser = await _auth.currentUser();
assert(user.uid == currentUser.uid);
log('data: $user');
return 'signInWithGoogle succeeded: $user';
}
void signOutGoogle() async {
await googleSignIn.signOut();
print("User Sign Out");
}
FirstScreen code snippet:
-> Here I try to show user data (name, email and image) based on what I got on previously code
children: <Widget>[
CircleAvatar(
backgroundImage: NetworkImage(
imageUrl,
),
radius: 60,
backgroundColor: Colors.transparent,
),
SizedBox(height: 40),
Text(
'NAME',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Colors.black54),
),
Text(
name,
style: TextStyle(
fontSize: 25,
color: Colors.deepPurple,
fontWeight: FontWeight.bold),
),
SizedBox(height: 20),
Text(
'EMAIL',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Colors.black54),
),
Text(
email,
style: TextStyle(
fontSize: 25,
color: Colors.deepPurple,
fontWeight: FontWeight.bold),
),
SizedBox(height: 40)
EDIT: aditional info -> console log shows "Unhandled Exception: PlatformException(sign_in_failed, com.google.android.gms.common.api.ApiException: 10: , null)"
After wasting a day to find my way around the Google SignIn package with various fixes and troubleshooting, I realised there is web package Google has made for web sign in which uses the OAuthClient verification and configuration as per their changing policy with Google Sign in.
Sadly it isn't straight forward and quick as it was before. But following the steps mentioned in the pub.dev.
https://pub.dev/packages/google_sign_in_web
Attaching my main.dart working POC snippets here.
import 'dart:async';
import 'dart:convert' show json;
import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:http/http.dart' as http;
GoogleSignIn _googleSignIn = GoogleSignIn(
// Optional clientId
// clientId: '479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com',
scopes: <String>[
'email',
'https://www.googleapis.com/auth/contacts.readonly',
],
);
void main() {
runApp(
const MaterialApp(
title: 'Google Sign In',
home: SignInDemo(),
),
);
}
class SignInDemo extends StatefulWidget {
const SignInDemo({Key? key}) : super(key: key);
#override
State createState() => SignInDemoState();
}
class SignInDemoState extends State<SignInDemo> {
GoogleSignInAccount? _currentUser;
String _contactText = '';
#override
void initState() {
super.initState();
_googleSignIn.onCurrentUserChanged.listen((GoogleSignInAccount? account) {
setState(() {
_currentUser = account;
});
if (_currentUser != null) {
_handleGetContact(_currentUser!);
}
});
_googleSignIn.signInSilently();
}
Future<void> _handleGetContact(GoogleSignInAccount user) async {
setState(() {
_contactText = 'Loading contact info...';
});
final http.Response response = await http.get(
Uri.parse('https://people.googleapis.com/v1/people/me/connections'
'?requestMask.includeField=person.names'),
headers: await user.authHeaders,
);
if (response.statusCode != 200) {
setState(() {
_contactText = 'People API gave a ${response.statusCode} '
'response. Check logs for details.';
});
print('People API ${response.statusCode} response: ${response.body}');
return;
}
final Map<String, dynamic> data =
json.decode(response.body) as Map<String, dynamic>;
final String? namedContact = _pickFirstNamedContact(data);
setState(() {
if (namedContact != null) {
_contactText = 'I see you know $namedContact!';
} else {
_contactText = 'No contacts to display.';
}
});
}
String? _pickFirstNamedContact(Map<String, dynamic> data) {
final List<dynamic>? connections = data['connections'] as List<dynamic>?;
final Map<String, dynamic>? contact = connections?.firstWhere(
(dynamic contact) => (contact as Map<Object?, dynamic>)['names'] != null,
orElse: () => null,
) as Map<String, dynamic>?;
if (contact != null) {
final List<dynamic> names = contact['names'] as List<dynamic>;
final Map<String, dynamic>? name = names.firstWhere(
(dynamic name) =>
(name as Map<Object?, dynamic>)['displayName'] != null,
orElse: () => null,
) as Map<String, dynamic>?;
if (name != null) {
return name['displayName'] as String?;
}
}
return null;
}
Future<void> _handleSignIn() async {
try {
await _googleSignIn.signIn();
} catch (error) {
print(error);
}
}
Future<void> _handleSignOut() => _googleSignIn.disconnect();
Widget _buildBody() {
final GoogleSignInAccount? user = _currentUser;
if (user != null) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
ListTile(
leading: GoogleUserCircleAvatar(
identity: user,
),
title: Text(user.displayName ?? ''),
subtitle: Text(user.email),
),
const Text('Signed in successfully.'),
Text(_contactText),
ElevatedButton(
onPressed: _handleSignOut,
child: const Text('SIGN OUT'),
),
ElevatedButton(
child: const Text('REFRESH'),
onPressed: () => _handleGetContact(user),
),
],
);
} else {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
const Text('You are not currently signed in.'),
ElevatedButton(
onPressed: _handleSignIn,
child: const Text('SIGN IN'),
),
],
);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Google Sign In'),
),
body: ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: _buildBody(),
));
}
}
I found that the error I was getting was due to the lack of some OAuth Credentials settings. I found the solution here

Categories

Resources