Internet connectivity in flutter - flutter

How to implement continuous Internet connectivity check in flutter only once for whole app, I have almost complete app, now I need to add Internet connectivity check, Please help, thanks in advance
#override
Widget build(BuildContext context) {
return StreamBuilder<ConnectivityResult>(
stream: connectivityStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
final connectivityResult = snapshot.data;
if (connectivityResult == ConnectivityResult.none) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: NoInternetConnectionScreen(),
);
}
return MaterialApp(
debugShowCheckedModeBanner: false,
home: SplashScreen(),
routes: routes,
);
} else if (snapshot.hasError) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: NoInternetConnectionScreen(),
);
}
return Center(child: CircularProgressIndicator());
}
);
}

The connectivity plugin states in its docs that it only provides information if there is a network connection, but not if the network is connected to the Internet
Note that on Android, this does not guarantee connection to Internet.
For instance, the app might have wifi access but it might be a VPN or
a hotel WiFi with no access.
You can use
import 'dart:io';
...
try {
final result = await InternetAddress.lookup('google.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
print('connected');
}
} on SocketException catch (_) {
print('not connected');
}

I think that the best practice would be using connectivity plugin and wrapping your app in a stream builder
https://pub.dev/packages/connectivity
Your main screen / main page should be something like this:
class MainScreen extends StatelessWidget {
Stream connectivityStream = Connectivity().onConnectivityChanged;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppStyle.backgroundColor,
body: StreamBuilder<ConnectivityResult>(
stream: connectivityStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
final connectivityResult = snapshot.data;
if (connectivityResult == ConnectivityResult.none) {
return NoConnectionPage();
}
return HomePage();
} else if (snapshot.hasError) {
return NoConnectionPage();
// or some error page, but I think no connection page is what you
// want here
}
return Center(child: CircularProgressIndicator());
}
)
);
}
}
In NoConnectionPage() you can have a button that retries the connection with a method like this:
void _retryConnection() async {
try {
final result = await InternetAddress.lookup('example.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
print('connected');
Navigator.of(context).pop(); // going back to MainScreen()
}
} on SocketException catch (_) {
print('not connected');
}
}

Simply use the internet_connectivity_checker package like this :
class Hello extends StatelessWidget {
const Hello({super.key});
#override
Widget build(BuildContext context) {
return internetConnectivityBuilder(
(status) {
bool connected = status == ConnectivityStatus.online;
return Text(connected ? "Online" : "Offline");
},
);
}
}
Get more info about this package here.

Related

How to make Flutter Stream Builder return seamlessly

I have a little problem here where i have logged in with Google Auth using Firebase but everytime i tried to restart the app i expect the app will show the HomePage() without any problem, but i found that before it return, the app had like a bit seconds in LoginPage() before displaying HomePage(), is there any way to make it seamlessly
class AuthService extends StatelessWidget {
const AuthService({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return HomePage();
} else {
return LoginPage();
}
},
),
);
}
}
It is happening because for snapshot to reach snapshot.hasData state it takes time, and meanwhile else part is executed which is LoginPage().
How to overcome this?
Try to wrap within snapshot.connectionState == ConnectionState.active which means once stream is connected then check the condition else return CircularProgressIndicator
Code:
StreamBuilder(
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.hasData) {
return HomePage();
} else {
return LoginPage();
}
}
return const CircularProgressIndicator();
},
);

I want anonymous authenticated users to be redirected to another page

I want to redirect anonymous users to "GuestHomePage" instead of "HomePage" when they launch the app. 
In my app, authenticated users have their own documents in the "users" collection in the firestore. Therefore, I thought it would be possible to transition the user to another "GuestHomePage" if the user did not have the document in the firestore. But it didn't work. Below is my code. I would like to know what improvements you would like to see.
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
MobileAds.instance.initialize();
runApp((MyApp()));
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListenableProvider<UserState>(
create: (_) => UserState(),
builder: (context, child) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const SizedBox();
}
if (snapshot.hasData) {
String uid = FirebaseAuth.instance.currentUser!.uid;
try {
FirebaseFirestore.instance
.collection("users")
.doc(uid)
.get()
.then(
(doc) {},
);
return HomePage();
} catch (e) {
return GuestHomePage();
}
}
return const UserLogin();
},
),
theme: ThemeData(primarySwatch: Colors.teal),
);
},
);
}
}
class UserState extends ChangeNotifier {
User? user;
void setUser(User newUser) {
user = newUser;
notifyListeners();
}
}
try this one...
if (snapshot.hasData) {
String uid = FirebaseAuth.instance.currentUser!.uid;
try {
FirebaseFirestore.instance.collection("users").doc(uid).get().then((doc) {
var data = doc.data();
if (data != null) {
return HomePage();
} else {
return GuestHomePage();
}
},
);
} catch (e) {
return GuestHomePage();
}
}

Flutter + Hive Check if value is present in box in Future Builder

I am crafting a new app and saving the response from a REST API call to a Hive box, which is successful (as far as I know). What I am trying to do in my main.dart is check if the value for token is set in Hive and if it is load an alternative view other than the login view.
My main.dart
import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:path_provider/path_provider.dart' as path_provider;
import 'package:myapp/model/user_model.dart';
import 'package:myapp/views/login_view.dart';
import 'package:myapp/views/project_view.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final appDocsDir = await path_provider.getApplicationDocumentsDirectory();
Hive.init(appDocsDir.path);
Hive.registerAdapter(UserModelAdapter());
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
State<PocketPolarix> createState() => _PocketPolarixState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
final user = Hive.box('user');
return MaterialApp(
title: 'Startup Name Generator',
theme: ThemeData(
appBarTheme: const AppBarTheme(
backgroundColor: Color(0xFF2036B8),
foregroundColor: Colors.white,
),
),
home: FutureBuilder(
future: Hive.openBox('user'),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
return Text(snapshot.error.toString());
} else {
Hive.openBox('user');
if (user.get('token') != null) {
return const ProjectView();
} else {
return const LoginView();
}
}
} else {
return Scaffold();
}
},
),
);
}
#override
void dispose() {
Hive.close();
super.dispose();
}
The code is failing at the second if statement where I check to see if Hive.box('user').get('token') != null what I keep getting is the following error.
throw HiveError('Box not found. Did you forget to call Hive.openBox()?'); as you can see from the code however, I am opening the box.
I am new to Dart and Flutter and Hive for that matter so a helping hand here would be great, thanks!
Part 1 of the issue is that you were trying to access user before opening. Secondly, I believe you need to check if the snapshot.hasData before proceeding with any operation i.e.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Startup Name Generator',
theme: ThemeData(
appBarTheme: const AppBarTheme(
backgroundColor: Color(0xFF2036B8),
foregroundColor: Colors.white,
),
),
home: FutureBuilder(
future: Hive.openBox<YourUserModelBox>('user'),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
return Text(snapshot.error.toString());
} else if (snapshot.hasData) {
if (snapshot.data is YourUserModelBox) {
final user = snapshot.data as YourUserModelBox;
if (user.get('token') != null) {
return const ProjectView();
} else {
return const LoginView();
}
} else {
Scaffold();
}
}
} else {
return Scaffold();
}
},
),
);
}
The problem is with the first line of code in your build function. You're trying to get the user box from Hive without opening it first. Here is what can you do instead:
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Startup Name Generator',
theme: ThemeData(
appBarTheme: const AppBarTheme(
backgroundColor: Color(0xFF2036B8),
foregroundColor: Colors.white,
),
),
home: FutureBuilder<Box>(
future: Hive.openBox('user'),
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
return Text(snapshot.error.toString());
} else {
if (snapshot.data?.get('token') != null) {
return const ProjectView();
} else {
return const LoginView();
}
}
} else {
return Scaffold();
}
},
),
);
}

Implement verification user exist

I would like to check if the user has already filled in the registration form:
Here is my code for the connectionState:
class LandingPage extends StatelessWidget {
// final Geolocator _geolocator = Geolocator()..forceAndroidLocationManager;
#override
Widget build(BuildContext context) {
final auth = Provider.of<AuthBase>(context, listen: false);
return StreamBuilder<User>(
stream: auth.onAuthStateChanged,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
User user = snapshot.data;
if (user == null) {
return SignInPage();
} else {
// _geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.best)
MatchEngine.instance.initialise(user.uid);
return Chat();
}
} else {
return Scaffold(
body: MyAppsCircularProgressIndicator(title: "MyApp",),
);
}
},
);
}
}
this code works fine for connectionstate.
I would like to add in the first code:
if (not signed in) {
show sign in page
} else {
if (not registered)
show register page
else
show home page
}
or
StreamBuilder(
stream: auth.authStateChanges()
builder: (_, snapshot) {
// check connectionState too
if (snapshot.hasData) {
StreamBuilder(
stream: database.userData() // this is a stream you create that reads from `userData/$uid` or similar
builder: (_, snapshot) {
if (snapshot.hasData) {
return HomePage()
} else {
return RegisterPage()
}
}
)
} else {
return SignInPage()
}
}
)
I would like to add the last code to the previous one to have my connectionstate + my redirection to RegisterPage.
I tried everything but to no avail ... could someone help me? Thank you
You could use the provider package and then create a seperate file which has the following code. I personally use this and it works well.
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
if (user == null) {
return SignIn();
} else {
return Dashboard();
}
}
}
and in your main.dart file where you are building the material app. Put the wrapper (or whatever you name it) widget instead such as the following.
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return FutureBuilder(
// Initialize FlutterFire
future: Firebase.initializeApp(),
builder: (context, snapshot) {
// Check for errors
if (snapshot.hasError) {
return ErrorPage();
}
// Show Application
if (snapshot.connectionState == ConnectionState.done) {
return StreamProvider<Help4YouUser>.value(
value: AuthService().user,
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: Wrapper(),
),
);
}
// Initialization
return LoadingWidget();
},
);
}
}
Any clarification needed please comment

How to check whether there is a user logged in the app or not

In 2019, I managed to check whether a user is logged in to the app or not. Unfortunately, a lot has changed since then. I've tried searching for tutorials and guides on FlutterFire. But, I couldn't find any. I'm so confused about stream, future, and provider. Nor do I know the difference between them.
My old code (doesn't work anymore):
Future main() async {
runApp(
ChangeNotifierProvider<AuthService>(
child: MyApp(),
create: (BuildContext context) {
return AuthService();
},
),
);
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitUp]);
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'TestApp',
theme: ThemeData(primarySwatch: Colors.blue),
home: FutureBuilder<FirebaseUser>(
future: Provider.of<AuthService>(context).getUser(),
builder: (context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// log error to console
if (snapshot.error != null) {
print("error");
return Text(snapshot.error.toString());
}
// redirect to the proper page
return snapshot.hasData ? HomePage() : LoginPage();
} else {
// show loading indicator
return LoadingCircle();
}
},
),
);
}
}
class AuthService with ChangeNotifier {
final FirebaseAuth _auth = FirebaseAuth.instance;
Future<FirebaseUser> getUser() {
return _auth.currentUser();
}
Future logout() async {
var result = FirebaseAuth.instance.signOut();
notifyListeners();
return result;
}
Future<FirebaseUser> loginUser({String email, String password}) async {
try {
var result = await FirebaseAuth.instance
.signInWithEmailAndPassword(email: email, password: password);
notifyListeners();
return result.user;
} catch (e) {
throw new AuthException(e.code, e.message);
}
}
}
How do I check whether a user is logged in or not? Thank you in advance for your help.
Create a global firebase user instance and on your first screen like on Splash Screen check if the current user is null or not then depending on the outcome navigate to the screen of your choice. Here is my code you can change it a bit and try.
Future<void> tryAutoLogin() async {
firebaseUser = firebaseAuth.currentUser;
if (firebaseUser == null) return;
Response<AppUser> userDataResponse =
await DatabaseInterface.getUserData(firebaseUser?.uid);
if (userDataResponse.success) {
appUser = userDataResponse.data;
await appUser?.cacheAppUserData();
} else
return;
}
This works for FlutterFire!
Firebase Auth enables you to subscribe in realtime to this state via a Stream. Once called, the stream provides an immediate event of the user's current authentication state, and then provides subsequent events whenever the authentication state changes. To subscribe to these changes, call the authStateChanges() method on your FirebaseAuth instance:
import 'package:firebase_auth/firebase_auth.dart' as auth;
import 'package:flutter/material.dart';
import 'menu.dart';
import 'login.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:firebase_core/firebase_core.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
MyApp()
);
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitUp]);
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'TestApp',
theme: ThemeData(primarySwatch: Colors.blue),
home:
StreamBuilder<auth.User>(
stream: auth.FirebaseAuth.instance.authStateChanges(),
builder: (BuildContext context, AsyncSnapshot<auth.User> snapshot) {
if(snapshot.hasData) {
print("data exists");
return HomePage();
}
else {
return LoginPage();
}
},
)
);
}
}