setState is not calling when Navigating to another page - flutter

I am checking the connectivity using Connectivity().checkConnectivity() it is working but the setState() is not calling when I navigate to this page.
To navigate to main page to another page I am using Navigator.push(context, MaterialPageRoute(builder: (context) => search()));
and here is my next page
class search extends StatefulWidget {
#override
_searchState createState() => _searchState();
}
class _searchState extends State<search> {
List<filter> _list = [];
List<filter> to_display = [];
bool isoffline;
Future<void> connectivity() async {
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.mobile) {
// I am connected to a mobile network.
setState(() {
isoffline = true;
});
print("connected");
} else if (connectivityResult == ConnectivityResult.wifi) {
setState(() {
isoffline = true;
});
// I am connected to a wifi network.
print("connected");
} else {
setState(() {
isoffline = false;
});
print(
"dissconneteddsfffffffffffffffffffffffffffffffffffffffffffffffffffffffdsfsd");
}
}
Future _future;
#override
void initState() {
mylist().then((value) {
setState(() {
_list = to_display = value;
});
});
connectivity();
print("value${isoffline}"); // this value is on null
_future = data();
super.initState();
}
print("value${isoffline}"); // this value is on null

The function connectivity is an asynchronous function. You are calling the function and immediately checking the boolean value, that's why you are getting null. You have to wait till the execution, either by using await or then syntax.
connectivity().then((value) {
print("value ${isoffline}");
});

In your code connectivity(); method is async which mean it will require some time to complete.
Now when init method is called it executes connectivity(); and then immediately executes print("value${isoffline}"); now the value for isoffline initially is null, hence null printed
solution is either you put print("value${isoffline}"); in connectivity(); method or try to add it after build method.
#override
Widget build(BuildContext context) {
connectivity();
print("value${isoffline}");
return YourWidget();
}

Related

how to await for network connectivity status in flutter

I have used connectivity_plus and internet_connection_checker packages to check the internet connectivity.
The problem occured is , the app works perfectly fine as expected when the app start's with internet on state. But when the app is opened with internet off, the dialog isn't shown !!
I assume this is happening because the build method is called before the stream of internet is listened.
Code :
class _HomePageState extends State<HomePage> {
late StreamSubscription subscription;
bool isDeviceConnected = false;
bool isAlertSet = false;
#override
void initState() {
getConnectivity();
super.initState();
}
getConnectivity() {
subscription = Connectivity().onConnectivityChanged.listen(
(ConnectivityResult result) async {
isDeviceConnected = await InternetConnectionChecker().hasConnection;
if (!isDeviceConnected && isAlertSet == false) {
showDialogBox();
setState(() {
isAlertSet = true;
});
}
},
);
}
#override
void dispose() {
subscription.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
...
);
}
showDialogBox() => showDialog(/* no internet dialog */)
Extending the question: Is it assured that this works for all the pages ?
if yes, how ?
if not , how to overcome this?
First of all you need to listen for internet connectivity in your app first screen which is probably app.dart
GlobalKey<NavigatorState> navigatorKey = GlobalKey();
final noInternet = NoInternetDialog();
class TestApp extends StatefulWidget {
#override
State<TestApp> createState() => _TestAppState();
}
class _TestAppState extends State<TestApp> {
#override
void initState() {
super.initState();
checkInternetConnectivity();
}
#override
Widget build(BuildContext context) {
return MaterialApp(...);
}
Future<void> checkInternetConnectivity() async {
Connectivity().onConnectivityChanged.getInternetStatus().listen((event)
{
if (event == InternetConnectionStatus.disconnected) {
if (!noInternet.isShowing) {
noInternet.showNoInternet();
}
}
});
}
}
Make the screen stateful in which you are calling MaterialApp and in initState of that class check for your internet connection, like above
You are saying how can I show dialog when internet connection changes for that you have to create a Generic class or extension which you can on connectivity change. You have to pass context to that dialogue using NavigatorKey
class NoInternetDialog {
bool _isShowing = false;
NoInternetDialog();
void dismiss() {
navigatorKey.currentState?.pop();
}
bool get isShowing => _isShowing;
set setIsShowing(bool value) {
_isShowing = value;
}
Future showNoInternet() {
return showDialog(
context: navigatorKey.currentState!.overlay!.context,
barrierDismissible: true,
barrierColor: Colors.white.withOpacity(0),
builder: (ctx) {
setIsShowing = true;
return AlertDialog(
elevation: 0,
backgroundColor: Colors.transparent,
insetPadding: EdgeInsets.all(3.0.h),
content: Container(...),
);
},
);
}
}
Use checkConnectivity to check current status. Only changes are exposed to the stream.
final connectivityResult = await Connectivity().checkConnectivity();

How to use SharedPreference Globally?

I pass the value of both userProfileID And ,userstype to Profilepage() as shown below but when i go to profile page and I try to print the value of both this variable I will get Null value, I think they Actually not passed from this _HomePageState, anyone help?
Here is Where I pass the Value of both
userProfileID as a UserID and userstype as UserTypes inside initState() below,
and both UserID and UserTypes are Obtained from SharedPrefrence
(I call GetData to obtain the value of UserID and UserTypes from SharedPreference )
class _HomePageState extends State<HomePage> {
String UserID;
String UserTypes;
List<Widget>_children;
bool isSignedIn= false;
int _CurrentIndex=0;
void initState(){
super.initState();
GetData();
_children=[
TimeLinePage(UsersIdTimeline:UserID,UsersTypeTimeline:UserTypes),
SearchPage(searchUserSID: UserID,searchUsertype:UserTypes), //search(),
UploadPage(uploadUserSID:UserID,uploadUsertype: UserTypes),
NotificationsPage(NotifyUserSID: UserID,NotifyUsertype:UserTypes),
ProfilePage(userProfileID:UserID,userstype:UserTypes),
];
if(FirebaseAuth.instance.currentUser!=null){
setState(() {
isSignedIn= true;
});
}else{
setState(() {
isSignedIn= false;
});
}
}
#override
Widget build(BuildContext context) {
if(isSignedIn){
return buildHomeScreen();
} else{
return buildSignedInScreen();
}
}
void GetData()async {
SharedPreferences preferences= await SharedPreferences.getInstance();
setState(() {
UserID=preferences.get('UserId');
UserTypes=preferences.get('UserType');
});
}
}
here is buildHomeScreen
class _HomePageState extends State<HomePage> {
// ignore: non_constant_identifier_names
String UserID;
String UserTypes;
List<Widget>_children;
List<Widget>_agentchildren;
bool isSignedIn= false;
// ignore: non_constant_identifier_names
int _CurrentIndex=0;
int _agentCurrentIndex=0;
void initState(){
super.initState();
GetData();
_children=[
TimeLinePage(UsersIdTimeline:UserID,UsersTypeTimeline:UserTypes),
SearchPage(searchUserSID: UserID,searchUsertype:UserTypes),
UploadPage(uploadUserSID:UserID,uploadUsertype:UserTypes),
NotificationsPage(NotifyUserSID: UserID,NotifyUsertype:UserTypes),
ProfilePage(userProfileID:UserID,userstype:UserTypes),
];
if(FirebaseAuth.instance.currentUser!=null){
setState(() {
isSignedIn= true;
});
}else{
setState(() {
isSignedIn= false;
});
}
}
#override
Widget build(BuildContext context) {
if(isSignedIn){
if(UserTypes=='agent'){
return buildagentScreen();
} else if(UserTypes== 'Signupuser'||
UserTypes== 'owner'||
UserTypes== 'seller'
){
return buildHomeScreen();
}else{
return buildSignedInScreen();
}
}
}
Here is My ProfilePage(), if I try to get the value of both
( String userstype; String userProfileID;) in this page
i get Null value but I alredy pass the value of them from the above _HomePageState()
class ProfilePage extends StatefulWidget {
String userstype;
String userProfileID;
ProfilePage({this.userProfileID, this.userstype});
#override
_ProfilePageState createState() => _ProfilePageState();
}
class _ProfilePageState extends State<ProfilePage> {
final String CurrentOnlineUserID=curentuser?.uid;
bool loading =false;
int countPost=0;
String postOrientation="grid";
List<Post> PostList=[];
void initState(){
getAllProfilePost();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar:header(context,strTitle:"profile"),
body:ListView(
children:<Widget>[
TopView(),
]
),
);
}
Use shared preferences globally for your application.
import 'dart:async' show Future;
import 'package:shared_preferences/shared_preferences.dart';
class PreferenceUtils {
static Future<SharedPreferences> get _instance async => _prefsInstance ??= await SharedPreferences.getInstance();
static SharedPreferences _prefsInstance;
// call this method from iniState() function of mainApp().
static Future<SharedPreferences> init() async {
_prefsInstance = await _instance;
return _prefsInstance;
}
static String getString(String key, [String defValue]) {
return _prefsInstance.getString(key) ?? defValue ?? "";
}
static Future<bool> setString(String key, String value) async {
var prefs = await _instance;
return prefs?.setString(key, value) ?? Future.value(false);
}
}
Please follow this link
https://stackoverflow.com/a/61046061/8218866
When the _HomePageState is created, the initState is called, and so this line:
_children=[
[...],
ProfilePage(userProfileID:UserID,userstype:UserTypes),
];
This line is creating the object of a ProfilePage with the desired UserID's and UserTypes. This ProfilePage object will be put inside the _children list.
When you do this:
setState(() {
UserID=preferences.get('UserId');
UserTypes=preferences.get('UserType');
});
You are 1) calling the build method again, and 2) updating the value of UserID and UserTypes. You did not change the value of any itens inside the _childrens list. Or the list itself. Hence the misbehavior you noticed.
There are many ways to solve this, but the essence would be to move this list declaration inside the build method. An example:
#override
Widget build(BuildContext context) {
_children = [.....]
if(isSignedIn){
[...]
Doing this way would not be a pretty way to do it, because you are creating lots of new (and useless) objets every time the build method is called. Maybe in a small application this wouldn't be a problem, and for didactic reasons I chose to show this way in this answer.
The more correct way would be to :
Switch and instantiate inside buildHomeScreen the body object:
Scaffold buildHomeScreen(){
Widget body;
switch (_currentIndex) {
case 0:
body =
TimeLinePage(UsersIdTimeline:UserID,UsersTypeTimeline:UserTypes);
break;
case 1:
body = ...;
break;
}
return Scaffold(
...
body: body,
...
)
}
Which should give you the same result.

How to Navigate one of the two screens to users correctly in Flutter?

Recently I am developing first ever app in Flutter and I know the basics only.
So here's the problem which I am facing currently.
I have to Navigate One of the two screens, either Register or Home page according to old/new user, So what I did is here.
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'Register.dart';
import 'HomePage.dart';
class Authenticate extends StatefulWidget {
#override
_AuthenticateState createState() => _AuthenticateState();
}
class _AuthenticateState extends State<Authenticate> {
String _userName = "";
#override
void initState() {
super.initState();
_getUserName();
}
_getUserName() async {
SharedPreferences pref = await SharedPreferences.getInstance();
setState(() {
_userName = pref.getString('userName') ?? "";
});
}
Widget build(BuildContext context) {
if (_userName == "") {
print('name : $_userName , NEW user'); // ERROR - why it's printing on console for old User ?
return Register();
} else {
return HomePage(_userName);
}
}
}
So the problem is, even if I am opening app through old user, it is printing the debug code written for new user and there's around 0.3 sec screen visibility of Register screen.
So what should I do to fix this?
The print is happening because this line of code:
SharedPreferences pref = await SharedPreferences.getInstance();
is asynchronous, which means that it might invoke in some later point in time. Because of that the build method is called first, then your _getUserName method finished, which causes a setState and invokes a build method again.
You might want to show some kind of loader screen until sharedPrefernces is initialized and then decide wheter to show Register or Home page.
Example code:
class _AuthenticateState extends State<Authenticate> {
String _userName = "";
bool _isLoading = true;
#override
void initState() {
super.initState();
_getUserName();
}
_getUserName() async {
SharedPreferences pref = await SharedPreferences.getInstance();
setState(() {
_userName = pref.getString('userName') ?? "";
_isLoading = false;
});
}
Widget build(BuildContext context) {
if (_isLoading) {
return Center(child: CupertinoActivityIndicator());
} else if (_userName == "") {
print('name : $_userName , NEW user'); // ERROR - why it's printing on console for old User ?
return Register();
} else {
return HomePage(_userName);
}
}
}
Try this:
class _AuthenticateState extends State<Authenticate> {
String _userName;
#override
void initState() {
super.initState();
_getUserName();
}
_getUserName() async {
SharedPreferences pref = await SharedPreferences.getInstance();
setState(() {
_userName = pref.getString('userName') ?? '';
});
}
Widget build(BuildContext context) {
if (_userName == null) {
return Center(child: CircularProgressIndicator());
} else if (_userName == '') {
print('name : $_userName , NEW user');
return Register();
} else {
return HomePage(_userName);
}
}
}
In this case, _userName is null initially and will only be assigned a value after the _getUserName() returns either an empty String or an old userName. When null, the widget will build a progress indicator instead. If you don't want that, just return a Container().

Pass Widgets in Connectivity check in flutter

I want to implement internet connectivity check into my app and I used official connectivity plugin and it is working great for displaying String Value but instead of showing string value in screen I want to display different widgets for connected and disconnected.
Here What I am Using
//
Widget result;
//
body: Container(
alignment: Alignment.center,
child: result != null ?
result : Text("unknown", style :
TextStyle(fontSize: 30,fontWeight: FontWeight.bold),
),
void checkStatus(){
Connectivity().onConnectivityChanged.listen((ConnectivityResult result) {
if(
result == ConnectivityResult.mobile ||
result == ConnectivityResult.wifi){
Text("Connected", style:TextStyle(color:Colors.red));
} else {
Text("No InterNet", style:TextStyle(color:Colors.red));
}
});
}
#override
void initState() {
super.initState();
checkStatus();
}
And I am Getting 'unknown' value
try this
class Sample extends StatefulWidget {
#override
_SampleState createState() => _SampleState();
}
class _SampleState extends State<Sample> {
Widget result;
#override
void initState() {
super.initState();
checkStatus();
}
void checkStatus() async {
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.mobile) {
result = Text("Connected to Mobile Network");
setState(() {});
} else if (connectivityResult == ConnectivityResult.wifi) {
result = Text("Connected to WiFi");
print("Connected to WiFi");
setState(() {});
} else {
result = Text("Unable to connect. Please Check Internet Connection");
setState(() {});
print("Unable to connect. Please Check Internet Connection");
}
}
#override
Widget build(BuildContext context) {
return Center(child: result);
}
}
Try this:
Use this package for checking Internet:
data_connection_checker:
And, Inside your stateful class create stream listener i.e and a boolean value.
StreamSubscription<DataConnectionStatus> listener; bool isConnected = true;
and Inside initState:
#override
void initState() {
super.initState();
listener = DataConnectionChecker().onStatusChange.listen((status) {
switch (status) {
case DataConnectionStatus.connected:
print('Data connection is available. $status');
setState(() {
isConnected = true;
});
break;
case DataConnectionStatus.disconnected:
print('You are disconnected from the internet. $status');
setState(() {
isConnected = false;
});
break;
}
});
}
Done, This will keep listening to changes in your internet status, Thus you can prompt user as you like. Cheers, Feel free to ask if confusion and if it helps upvote :D

getting code to run upon widget creation flutter

I have a flutter camera app and am able to get a recorded video to play. The problem is, I am only able to get it to play when a button is pressed. How do I get the code to run when the widget(screen) is created instead of when the button is pressed so I don't have to press a button to get it to play? Here is my code:
Here is the code for when the button is pressed:
//raised button
RaisedButton(
onPressed: () {stopButtonPressed();},
//stopButtonPressed
void stopButtonPressed() {
print('stopButtonPressed hit');
stopVideoRecording().then((_) {
print('StopVideoRecording complete');
});
}
//stopVideoRecording
Future<void> stopVideoRecording() async {
print('stopVideoRecording hit');
await _startVideoPlayer();
}
//_startVideoPlayer
Future<void> _startVideoPlayer() async {
print('_startVideoPlayer hit');
print(Provider.of<SendDataModel>(context, listen: false).displayImageVideo());
final VideoPlayerController vcontroller =
VideoPlayerController.file(File(Provider.of<SendDataModel>(context, listen: false).displayImageVideo()));
videoPlayerListener = () {
if (videoController != null && videoController.value.size != null) {
if (mounted) setState(() {});
videoController.removeListener(videoPlayerListener);
}
};
vcontroller.addListener(videoPlayerListener);
await vcontroller.setLooping(true);
await vcontroller.initialize();
await videoController?.dispose();
if (mounted) {
setState(() {
//saveImagePath = null;
videoController = vcontroller;
});
}
await vcontroller.play();
} //startVideoPlayer
Thanks!
You can call the function from initState(). initState() is called only once when the StatefulWidget is inserted into the Widget tree, so it's a good place to initialize variables or do what you're trying to do.
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
super.initState();
// Do anything you need done here
_startVideoPlayer();
// If you want a slight delay, use Future.delayed
Future.delayed(Duration(seconds: 1), (){
_startVideoPlayer();
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
// rest of app