Returns a Future<Dynamic> instead of Bool - flutter

I want to return a bool in this method but it return a Future
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 0, right: 0, top: 110, bottom: 5),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
QuestionCards(),
cla().then((value) => { //this can't be add like this
YoutubeViewer(
videoId: 'hVQUbKs6qN8', topic: 'topic1'),
}
)
],
).
),
],
),
);
}
Future<bool> cla() async {
bool d = false;
try {
final result = await InternetAddress.lookup('google.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
print('connected');
return Future<bool>.value(true);
}
} on SocketException catch (_) {
print('not connected');
return Future<bool>.value(false);
}
}
If someone can tell me that what need to be changed in this
It would be really helpful
Thank you

Return bool using Future object
return Future<bool>.value(true);
and modify method like
Future<bool> cla() async{
Use like:
cla().then((value) {
// value will provide you true or false
});

Future<bool> cla() async {
bool d=false;
try {
final result = await InternetAddress.lookup('google.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
print('connected');
d= true;
}
} on SocketException catch (_) {
print('not connected');
d= false;
}
return d;
}
#override
void initState() {
super.initState();
// calling cla function
setState(() async {
var value = await getWeather();
if(value){
...
}
});
}
The observation you found is expected. Since the operation inside cla() function is async and the method is marked as async to return the future result. So you. will get future and to get the result form future you have to call await on it as shown above.

Future<bool> cla() async{
// function body goes here..
}
you can explicitly define return type of the method.

Related

Unhandled Exception: type 'Null' is not a subtype of type 'LocationDto'

I am using [background_locator_2][1] plugin, However when I run it with some modification I get this error
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'Null' is not a subtype of type 'LocationDto'
This is the code i am using
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ReceivePort port = ReceivePort();
String logStr = '';
bool? isRunning;
LocationDto? lastLocation;
#override
void initState() {
super.initState();
if (IsolateNameServer.lookupPortByName(
LocationServiceRepository.isolateName) !=
null) {
IsolateNameServer.removePortNameMapping(
LocationServiceRepository.isolateName);
}
IsolateNameServer.registerPortWithName(
port.sendPort, LocationServiceRepository.isolateName);
port.listen(
(dynamic data) async {
await updateUI(data);
},
);
initPlatformState();
}
#override
void dispose() {
super.dispose();
}
Future<void> updateUI(LocationDto data) async {
final log = await FileManager.readLogFile();
await _updateNotificationText(data);
setState(() {
lastLocation = data;
logStr = log;
});
}
Future<void> _updateNotificationText(LocationDto data) async {
if (data == null) {
return;
}
await BackgroundLocator.updateNotificationText(
title: "new location received",
msg: "${DateTime.now()}",
bigMsg: "${data.latitude}, ${data.longitude}");
}
Future<void> initPlatformState() async {
print('Initializing...');
await BackgroundLocator.initialize();
logStr = await FileManager.readLogFile();
print('Initialization done');
final _isRunning = await BackgroundLocator.isServiceRunning();
setState(() {
isRunning = _isRunning;
});
print('Running ${isRunning.toString()}');
}
#override
Widget build(BuildContext context) {
final start = SizedBox(
width: double.maxFinite,
child: ElevatedButton(
child: Text('Start'),
onPressed: () {
_onStart();
},
),
);
final stop = SizedBox(
width: double.maxFinite,
child: ElevatedButton(
child: Text('Stop'),
onPressed: () {
onStop();
},
),
);
final clear = SizedBox(
width: double.maxFinite,
child: ElevatedButton(
child: Text('Clear Log'),
onPressed: () {
FileManager.clearLogFile();
setState(() {
logStr = '';
});
},
),
);
String msgStatus = "-";
if (isRunning != null) {
if (isRunning!) {
msgStatus = 'Is running';
} else {
msgStatus = 'Is not running';
}
}
final status = Text("Status: $msgStatus");
final log = Text(
logStr,
);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter background Locator'),
),
body: Container(
width: double.maxFinite,
padding: const EdgeInsets.all(22),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[start, stop, clear, status, log],
),
),
),
),
);
}
void onStop() async {
await BackgroundLocator.unRegisterLocationUpdate();
final _isRunning = await BackgroundLocator.isServiceRunning();
setState(() {
isRunning = _isRunning;
});
}
void _onStart() async {
//if (await isLocationAlwaysGranted()) {
await _startLocator();
final _isRunning = await BackgroundLocator.isServiceRunning();
setState(() {
isRunning = _isRunning;
lastLocation = null;
});
// } else {
// show error
}
}
Future<bool> isLocationAlwaysGranted() async =>
await Permission.locationAlways.isGranted;
/// Tries to ask for "location always" permissions from the user.
/// Returns `true` if successful, `false` othervise.
Future<bool> askForLocationAlwaysPermission() async {
bool granted = await Permission.locationAlways.isGranted;
if (!granted) {
granted =
await Permission.locationAlways.request() == PermissionStatus.granted;
}
return granted;
}
Future<void> _startLocator() async {
Map<String, dynamic> data = {'countInit': 1};
return await BackgroundLocator.registerLocationUpdate(
LocationCallbackHandler.callback,
initCallback: LocationCallbackHandler.initCallback,
initDataCallback: data,
disposeCallback: LocationCallbackHandler.disposeCallback,
iosSettings: IOSSettings(
accuracy: LocationAccuracy.NAVIGATION,
distanceFilter: 0,
stopWithTerminate: true),
autoStop: false,
androidSettings: AndroidSettings(
accuracy: LocationAccuracy.NAVIGATION,
interval: 5,
distanceFilter: 0,
client: LocationClient.google,
androidNotificationSettings: AndroidNotificationSettings(
notificationChannelName: 'Location tracking',
notificationTitle: 'Start Location Tracking',
notificationMsg: 'Track location in background',
notificationBigMsg:
'Background location is on to keep the app up-tp-date with your location. This is required for main features to work properly when the app is not running.',
notificationIconColor: Colors.grey,
notificationTapCallback:
LocationCallbackHandler.notificationCallback)));
}
The error is in this line under initState when I start or stop the plugin.
port.listen(
(dynamic data) async {
await updateUI(data);
},
);
The original code didn't have null safety so i tried to modify it. However it is pretty evident my knowledge is limited.
[1]: https://pub.dev/packages/background_locator_2
the data you are listening to from the port (dynamic data) can be null and you are sending it to the function updateUI which does not accept nullable type.
you can either check if the data is not null before calling await updateUI(data); or you can make the function updateUI accepts null, i.e (Future<void> updateUI(LocationDto? data) async {) and handle the case that the data is nullable inside the function

Change Notifier Provider with async function

I'm trying to use provider with an async function where I'm changing a value of variable and as soon as the value changes, I want all listeners to be notified.
I'm sending a post request and waiting for response in the below async function. I'm waiting for the response and depending on that I want to show message on the Stateful Widget.
The provider seems to change value of the variable but doesn't change state on Text on the screen.
userloginprovider.dart
bool isLoading = false;
HttpService http = HttpService();
class UserLoginProvider with ChangeNotifier {
String loginMessage = '';
late UserAuthorizationResponse userRegistrationResponse;
Future loginUser(userData) async {
Response response;
print(loginMessage);
try {
isLoading = true;
response = await http.loginUser('api/v1/login/', userData);
isLoading = false;
if (response.statusCode == 200) {
var newReponse = response.data;
userRegistrationResponse =
UserAuthorizationResponse.fromJson(newReponse['data']);
loginMessage = newReponse['message'];
} else {
print('status code is not 200.');
}
} on Exception catch (e) {
isLoading = false;
loginMessage = e.toString().substring(11);
}
notifyListeners();
}
}
userloginscreen.dart
class _LoginPageState extends State<LoginPage> {
final UserLoginProvider userLoginProvider = UserLoginProvider();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: ChangeNotifierProvider(
create: (context) => UserLoginProvider(),
child: Consumer<UserLoginProvider>(
builder: (context, provider, child) {
return Container(
padding: const EdgeInsets.all(8.0),
width: double.infinity,
height: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(provider.loginMessage.toString()), //<-- I want to change value here.
AuthorizationButtons(
fieldName: 'Username',
textEditingController: usernameController,
),
AuthorizationButtons(
fieldName: 'Password',
textEditingController: passwordController,
),
OutlinedButton(
onPressed: () {
userData = {
'username': usernameController.text,
'password': passwordController.text,
};
userLoginProvider.loginUser(userData);
},
child: const Text('Submit'),
)
],
),
);
},
),
),
);
}
}
A new provider is created in every rebuild
body: ChangeNotifierProvider(
create: (context) => UserLoginProvider(),
Use the one in the state
body: ChangeNotifierProvider(
create: (context) => userLoginProvider,
you are notifying the listeners when it fails which is in catch block:-
on Exception catch (e) {
isLoading = false;
loginMessage = e.toString().substring(11); //here
notifyListeners();
}
}
but if the code runs without the error(exception). you are not notifying it on your code. so,if you want to notify, try something like this
try {
isLoading = true;
response = await http.loginUser('api/v1/login/', userData);
isLoading = false;
if (response.statusCode == 200) {
var newReponse = response.data;
userRegistrationResponse =
UserAuthorizationResponse.fromJson(newReponse['data']);
loginMessage = 'something'; //here
} else {
print('status code is not 200.');
}
notifyListeners();//notify the listeners here

Flutter - how to update screen with latest api response

I want to update the screen whenever I call the API. Right now I have the following
Future<String> getData() async {
var response = await http.get(
Uri.parse('https://www.api_endpoint.com'),
headers: {
'Accept':'application/json'
}
);
Timer.periodic(Duration(microseconds: 1000), (_) {
this.setState(() {
data = json.decode(response.body);
print(data); //I can see this in the console/logcat
});
});
}
#override
void initState() {
this.getData();
}
from the line above print(data); I can see the latest api responses in console/logcat but the screen doesn't update with the new values. I can't get my head around why the latest responses aren't shown on screen when this.setState() is called every second with the Timer... all feedback is welcome. Thanks
Future executes once and returns just one result. initState() executed when creating a widget, this is also usually once. For your tasks it is better to use Streams, my solution is not the best in terms of architecture, but as an example it works.
//We create a stream that will constantly read api data
Stream<String> remoteApi = (() async* {
const url = "http://jsonplaceholder.typicode.com/todos/1";
//Infinite loop is not good, but I have a simple example
while (true) {
try {
var response = await Dio().get(url);
if (response.statusCode == 200) {
//remote api data does not change, so i will add a timestamp
yield response.data.toString() +
DateTime.now().millisecondsSinceEpoch.toString();
}
//Pause of 1 second after each request
await Future.delayed(const Duration(seconds: 1));
} catch (e) {
print(e);
}
}
})();
//On the screen we are waiting for data and display it on the screen
// A new piece of data will refresh the screen
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: StreamBuilder<String>(
stream: remoteApi,
builder: (
BuildContext context,
AsyncSnapshot<String> snapshot,
) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.connectionState == ConnectionState.active ||
snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
return const Text('Error');
} else if (snapshot.hasData) {
return Center(
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Text(
snapshot.data.toString(),
textAlign: TextAlign.center,
),
),
);
} else {
return const Center(child: Text('Empty data'));
}
} else {
return Center(child: Text('State: ${snapshot.connectionState}'));
}
},
),
);
}
Or simplest solution
Future<String> remoteApi() async {
try {
const url = "http://jsonplaceholder.typicode.com/todos/1";
var response = await Dio().get(url);
if (response.statusCode == 200) {
return response.data.toString() +
DateTime.now().millisecondsSinceEpoch.toString();
} else {
throw ("Error happens");
}
} catch (e) {
throw ("Error happens");
}
}
var displayValue = "Empty data";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(child: Text(displayValue)),
Center(
child: ElevatedButton.icon(
onPressed: () async {
displayValue = await remoteApi();
setState(() {});
},
label: const Text('Get API'),
icon: const Icon(Icons.download),
),
)
],
),
));
}
Ah, you don't actually call your API every timer tick, you just decode the same body from the first call.
If you want to call your API periodically, you need to move the actual http.get call inside the timer method.
Got it using the answer found here... moved the Timer that called this.setState() to the initState method
#override
void initState() {
this.getData();
_everySecond = Timer.periodic(Duration(seconds: 5), (Timer t) {
setState(() {
getData();
});
});
}
Once I searched for how to update the state, change state, etc. found the solution quickly...

Flutter - whenComplete() not working as expected when using Providers

I'm trying to display a loading while doing an API Request and when finished to show the list with the response or a custom widget to show a message(EmptyListWidget). The problem is that the whenComplete() method is being executed before the async function is finished.
I also tried using then() and using FutureBuilder but I also can't make it work using Provider (allways returns null).
If someone could help, I would really appreciate it.. thanks :)
My List Widget:
class _AbsencesListState extends State<AbsencesList> {
bool _isLoading = false;
bool _isInit = true;
#override
void didChangeDependencies() {
super.didChangeDependencies();
if (_isInit) {
setState(() => _isLoading = true);
Provider.of<AbsencesTypes>(context, listen: false)
.getAbsencesTypes(widget.ctx)
.whenComplete(() {
setState(() => _isLoading = false);
});
_isInit = false;
}
}
#override
Widget build(BuildContext context) {
final absences = Provider.of<Absences>(context).items;
return Stack(
children: [
_isLoading
? const Center(child: CircularProgressIndicator())
: absences.length > 0
? Container()
: EmptyListWidget(ListType.InconsistenciesList),
ListView.builder(
itemBuilder: (_, index) {
return GestureDetector(
onTap: () {},
child: Card(
elevation: 2.0,
child: ListTile(
leading: CircleAvatar(
child: const Icon(Icons.sick),
backgroundColor: Theme.of(context).accentColor,
foregroundColor: Colors.white,
),
title: Padding(
padding: const EdgeInsets.only(top: 3),
child: Text(absences[index].absenceType.name),
),
subtitle: Text(
absences[index].firstDate
),
),
),
);
},
itemCount: absences.length,
)
],
);
}
}
The async function:
class AbsencesTypes with ChangeNotifier {
List<AbsenceType> _absencesTypesList = [];
List<AbsenceType> get items {
return [..._absencesTypesList];
}
void emptyAbsencesTypeList() {
_absencesTypesList.clear();
}
Future<void> getAbsencesTypes(BuildContext context) async {
SharedPreferences _prefs = await SharedPreferences.getInstance();
String token = _prefs.getString(TOKEN_KEY);
http.get(
API_URL,
headers: {"Authorization": token},
).then(
(http.Response response) async {
if (response.statusCode == 200) {
final apiResponse = json.decode(utf8.decode(response.bodyBytes));
final extractedData = apiResponse['content'];
final List<AbsenceType> loadedAbsencesTypes = [];
for (var absenceType in extractedData) {
loadedAbsencesTypes.add(
AbsenceType(
id: absenceType["id"],
name: absenceType["name"].toString(),
code: absenceType["code"].toString(),
totalAllowedDays: absenceType["totalAllowedDays"],
),
);
}
_absencesTypesList = loadedAbsencesTypes;
} else if (response.statusCode == 401) {
Utility.showToast(
AppLocalizations.of(context).translate("expired_session_string"));
Utility.sendUserToLogin(_prefs, context);
}
notifyListeners();
},
);
}
}
Your problem here is probably that you're calling http.get without awaiting for it's result.
The getAbsencesTypes returns the Future<void> as soon as the http.get method is executed, without waiting for the answer, and it results in your onComplete method to be triggered.
A simple fix would be to add the await keyword before the http.get, but you could do even better.
In your code, you're not fully using the ChangeNotifierProvider which could solve your problem. You should check the Consumer class which will be pretty useful for you here, but since it's not your initial question I won't go more in depth on this subject.

Flutter FutureBuilder Does Not Wait When App Updates

Problem My FutureBuilder waits when app first runs but doesn't wait when app updates.
When my app finishes loading and I change to a different ToggleButton, the FutureBuilder starts to rerun immediately instead of waiting for getData() and it fully completes before getData() is finished and then when getData() is finally finished, FutureBuilder runs again.
This problem does not happen when the app first runs. When the app first runs, the FutureBuilder waits for getData() to complete before running.
I need FutureBuilder to wait for getData() to finish when a different button is pressed just like it does when the app first starts up.
Note: I removed as much unnecessary code as I could for readability. I can add more code if it will help.
Code:
class PriceScreenState extends State<PriceScreen> {
String selectedCurrency = 'USD';
String selectedGraphType = "1D";
var isSelectedGraph = <bool>[true, false, false, false, false, false];
getData() async {
isWaiting = true;
try {
Map graphData = await GraphData().getGraphData(
selectedCurrency: selectedCurrency,
selectedGraphType: selectedGraphType);
isWaiting = false;
setState(() {
graphValues = graphData;
});
} catch (e) {
print(e);
}
}
#override
void initState() {
super.initState();
futureData = getData();
}
#override
Widget build(BuildContext context) {
...(other code)...
ToggleButtons( ****************TOGGLEBUTTONS***********
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0),
child: Text('1D'),
),
...(more Buttons)...
],
onPressed: (int index) {
setState(() {
for (int buttonIndex = 0;
buttonIndex < isSelectedGraph.length;
buttonIndex++) {
if (buttonIndex == index) {
isSelectedGraph[buttonIndex] = true;
selectedGraphType = graphType[buttonIndex];
} else {
isSelectedGraph[buttonIndex] = false;
}
}
});
getData();
},
isSelected: isSelectedGraph,
),
Expanded(
child: FutureBuilder( *************FUTUREBUILDER*********
future: futureData,
builder: (context, snapshot) {
if (graphValues.isEmpty) {
return new Container();
} else {
return Graph(graphValues);
}
}),
)
As you are using a FutureBuilder you don't need to call setState anymore. Here is a possible rework of your code:
Future<Map> futureData;
Future<Map> getData() async {
try {
Map graphData = await GraphData().getGraphData(
selectedCurrency: selectedCurrency,
selectedGraphType: selectedGraphType,
);
return graphData;
} catch (e) {
throw Exception(e);
}
}
#override
void initState() {
super.initState();
futureData = getData();
}
#override
Widget build(BuildContext context) {
// Only coding the FutureBuilder for the example
return FutureBuilder<Map>(
future: futureData,
builder: (context, snapshot) {
// Future is still loading
if (!snapshot.hasData)
return CircularProgressIndicator();
else if (snapshot.data.isEmpty)
return Container();
else
return Graph(snapshot.data);
},
);
}
For your FutureBuilder to work correctly you need to return a value in your getData and use the snapshot variable.