as I am new to the flutter, I came across the following exception while making a test app with the HTTP module and can't seem to find a solution around. Can anyone suggest a solution?
Future addProduct(Product product) {
const url = 'wrong_url_that_throws_format_error';
return http
.post(
url,
body: json.encode({
'title': product.title,
'description': product.description,
'imageUrl': product.imageUrl,
'price': product.price,
'isFavorite': product.isFavorite,
}),
).then((response) {
// Do things if successful
}).catchError((error) {
throw error;
});
}
// And in the widget
addProduct(_editedProduct).catchError((_) {
// this return will throw exception saying:
// Unhandled Exception: type 'Future<dynamic>' is not a subtype of type 'FutureOr<Null>'
return showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('Oops'),
content: const Text('Something went wrong.'),
actions: <Widget>[
FlatButton(
child: const Text('Ok'),
onPressed: () {
Navigator.of(ctx).pop();
},
),
],
),
);
}).then((_) {
setState(() {
_isLoading = false;
});
Navigator.of(context).pop();
});
According to the error message, the returned type was Future(dynamic), while the expected returning type was a FutureOr(Null). As the message says, FutureOr<Null> isn't a subtype of Future<dynamic>.
You can fix that by specifying the returning type of showDialog, so it returns the right type. Instead of:
return showDialog(...)
use:
return showDialog<Null>(...)
Related
I am getting this error in flutter when I am making my login page, I am sure that the server is returning JSON not HTML but I am still getting the error. I have even tried using some "test" links and I am getting the same error.
E/flutter (25538): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: FormatException: Unexpected character (at character 1)
E/flutter (25538): <br />
E/flutter (25538): ^
E/flutter (25538):
This is my code
Future userLogin() async {
setState(() {
visible = true;
});
String email = emailController.text;
String password = passwordController.text;
var url = 'http://192.168.1.2/***************/*********.php';
var data = {'email': email, 'password': password};
var response = await http.post(Uri.parse(url), body: json.encode(data));
var message = jsonDecode(response.body);
if (message == 'Login Matched') {
// Hiding the CircularProgressIndicator.
setState(() {
visible = false;
});
Navigator.push(
context, MaterialPageRoute(builder: (context) => StageOne()));
} else {
setState(() {
visible = false;
});
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text(message),
actions: <Widget>[
FlatButton(
child: new Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
I was working with rest api in flutter but I get error when I want create post options. I created task with post. I want solve this error. I have given the necessary code structures in the description. I hope I was able to explain clearly
Code Here:
My BaseClientClass
Future<dynamic> post(String baseUrl, String api, dynamic payloadObj) async {
var uri = Uri.parse(baseUrl + api);
String payload = json.encode(payloadObj);
try {
var response = await http.post(uri, body: payload, headers: {
'Content-Type': 'application/json'
}).timeout(const Duration(seconds: TIME_OUT_DURATION));
return _procosessResponse(response);
} on SocketException {
throw FetchDataException('No Internet connection', uri.toString());
} on TimeoutException {
throw ApiNotRespondingException(
'API not responded in time', uri.toString());
}
}
Controller class
Future postTodo(String task, bool active) async {
Map msg = {
"task": task,
"active": active
};
// String jsonS= json.encode(msg);
var response = await baseClient.post(
"http://192.168.1.114:5000", '/api/task', msg);
if (response.statusCode == 200 || response.statusCode == 201) {
var jsonData = json.decode(response.body);
if (jsonData['success']) { // eğer succes true ise
todolist.add(TodoModel.fromJson(jsonData['data']));
// var jsonData = json.encode(response);
print(msg);
}
}
add task Widget class
class AddTaskWidget extends StatelessWidget {
TextEditingController? task;
VoidCallback? onPress;
AddTaskWidget({this.task, this.onPress});
#override
Widget build(BuildContext context) {
return Container(
child: AlertDialog(
content: TextFormField(
controller: task,
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("İptal")),
TextButton(onPressed: onPress, child: Text("Ekle"))
],
),
);
}
}
This call post function and task widget code
IconButton(
onPressed: () => showDialog(
context: context,
builder: (context) => AddTaskWidget(
task: todoController.textTaskNameController,
onPress: () async{
await todoController.postTodo(
todoController.textTaskNameController.text, true);
Navigator.pop(context);
}),
),
icon: Icon(Icons.add))
Provide the API response payload once in JSON format
Trying to display in Flutter a result I am receiving from my Node.JS server via MySQL query:
[{"NAME":"Matematicas"},
{"NAME":"Naturales"},
{"NAME":"Ciencias Sociales"},
{"NAME":"Lenguaje"},
{"NAME":"Religion"}]
This is the class I am using in Flutter to handle it:
class Subject {
final String name;
Subject({
required this.name,
});
factory Subject.fromJson(Map<String, dynamic> json) {
return Subject(name: json['NAME']);
}
}
This is the method from which I obtain the data:
Future<Subject> fetchSubject() async {
var prefs = await SharedPreferences.getInstance();
var token = prefs.getString('token');
var response = await http.get(Uri.parse('http://localhost:8000/subjects'),
headers: {'x-access-token': token!});
print(response.body);
return Subject.fromJson(jsonDecode(response.body));
}
This is my initState
void initState() {
super.initState();
futureSubject = fetchSubject();
}
This is my Widget build piece:
Widget build(BuildContext context) {
return FutureBuilder<Subject>(
future: fetchSubject(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text('Error'),
);
} else if (snapshot.hasData) {
return Scaffold(
appBar: AppBar(
title: Text('Materias'),
backgroundColor: Colors.green[300],
actions: [
Padding(
padding: EdgeInsets.only(right: 3.0),
child: IconButton(
icon: Icon(Icons.logout),
//TODO llamar funcion logout
onPressed: () {},
iconSize: 26,
),
)
],
),
body: Text(snapshot.data!.name));
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
});
}
This is what I get:
Uncaught (in promise) Error: Expected a value of type 'Map<String, dynamic>', but got one of type 'List<dynamic>'
I just want to display the information I am receiving as a List or table like fashion. Any ideas on what and how to refactor this?
Its happened because your return data is an array. Try this
final data = json.decode(response.body);
return List<Subject>.from(data.map((value) => Subject.fromJson(value)));
It looks like the fetchSubject method needs to be modified and the widget itself. The data you displayed is a List of objects, thus the error that you are trying to see type Map<String, dynamic> from jsonDecode(response.body) but it returns a List<dynamic> instead. Thus, you need to modify fetchSubject and get a List<Subject from your API not just an object. Or, you need to update an API. Just as an example (haven't tested it but should work):
Future<List<Subject>> fetchSubject() async {
var prefs = await SharedPreferences.getInstance();
var token = prefs.getString('token');
var response = await http.get(Uri.parse('http://localhost:8000/subjects'),
headers: {'x-access-token': token!});
print(response.body);
return jsonDecode(response.body).map((item) => Subject.fromJson(item));
}
and change all logic to handle a List of Subject and not just Subject. The JSON your API returns is a list (array) of objects, not just an object.
I am writing an application to connect to Proxmox in Flutter, and I need to get the various Authentication Realms. The issue I have had is that most servers are using a self-signed SSL certificate and the http import does not support that. This has forced me to use the dart:io package and its HttpClient. However using this method does not return any results, the List is null.
D/ ( 9335): HostConnection::get() New Host Connection established 0xe047c540, tid 9354
D/EGL_emulation( 9335): eglMakeCurrent: 0xe76a7ac0: ver 3 0 (tinfo 0xccd07000)
I/flutter ( 9335): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 9335): The following NoSuchMethodError was thrown building FormField<dynamic>(dirty, state:
I/flutter ( 9335): FormFieldState<dynamic>#11694):
I/flutter ( 9335): The method 'map' was called on null.
I/flutter ( 9335): Receiver: null
I/flutter ( 9335): Tried calling: map<DropdownMenuItem<String>>(Closure: (AuthRealm) => DropdownMenuItem<String>)
This is my client class:
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:Proxcontrol/Client/Objects/auth_realms.dart';
class Client {
String baseUrl;
Client(String url, String port) {
baseUrl = "https://" + url + ":" + port + "/api2/json/";
}
Future<List<AuthRealm>> getAuthRealms() async {
HttpClient client = new HttpClient();
client.badCertificateCallback =((X509Certificate cert, String host, int port) => true);
var request = await client.getUrl(Uri.parse(baseUrl + "access/domains"));
var response = await request.close();
return await response.transform(Utf8Decoder()).transform(JsonDecoder()).map((json) => AuthRealm.fromJson(json)).toList();
}
}
This is my AuthRealm object class that the request is mapped to:
class AuthRealm {
final String type;
final String realm;
final String comment;
AuthRealm({this.type, this.realm, this.comment});
factory AuthRealm.fromJson(Map<String, dynamic> json) {
return AuthRealm(
type: json['type'],
realm: json['realm'],
comment: json['comment']
);
}
}
And this is where I am trying to get the Authentication Realms. It then passes them to a new page where they are displayed in a dropdownbutton. The serverAddress and serverPort fields are populated via TextFields.
final nextButton = RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24)),
onPressed: () {
Client client = new Client(serverAddress, serverPort);
client.getAuthRealms().then((values) {
realms = values;
});
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ServerAuthLoginScreen(authRealms: realms)));
},
padding: EdgeInsets.all(10),
color: Colors.indigoAccent,
child: Text('NEXT', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
);
And finally the dropdownbutton section that is populated with the Authentication Realms upon loading that screen.
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:Proxcontrol/Client/Objects/auth_realms.dart';
class ServerAuthLoginScreen extends StatefulWidget {
final List<AuthRealm> authRealms;
const ServerAuthLoginScreen({Key key, #required this.authRealms}) : super(key: key);
#override
_ServerAuthLoginScreenState createState() => _ServerAuthLoginScreenState(authRealms);
}
class _ServerAuthLoginScreenState extends State<ServerAuthLoginScreen> {
List<AuthRealm> authRealms;
_ServerAuthLoginScreenState(this.authRealms);
String serverRealm;
#override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
double screenHeight = MediaQuery.of(context).size.height;
final realmSelector = FormField(
builder: (FormFieldState state) {
return InputDecorator(
decoration: InputDecoration(
icon: const Icon(FontAwesomeIcons.server),
labelText: 'Select an Auth Realm'),
isEmpty: serverRealm == '',
child: new DropdownButtonHideUnderline(
child: new DropdownButton(
isDense: true,
items: authRealms.map((AuthRealm value) {
return new DropdownMenuItem(
value: value.realm,
child: Text(value.realm),
);
}).toList(),
onChanged: (String value) {
setState(() {
serverRealm = value;
state.didChange(value);
});
}
)
),
);
},
);
_buildVerticalLayout() {
return ListView(
shrinkWrap: true,
children: <Widget>[
Padding(
padding: EdgeInsets.only(
left: screenWidth / 12,
right: screenWidth / 12,
top: screenHeight / 30),
child: realmSelector,
),
],
);
}
return Scaffold(
appBar: AppBar(
title: Text('Server Connection Details'),
centerTitle: true),
body: _buildVerticalLayout()
);
}
}
This is what my test proxmox server gives as a result to the GET request at the defined address:
{
"data":[
{
"type":"ad",
"realm":"CELESTIALDATA"
},
{
"type":"pam",
"comment":"Linux PAM standard authentication",
"realm":"pam"
},
{
"type":"pve",
"comment":"Proxmox VE authentication server",
"realm":"pve"
}
]
}
Can someone please help me understand what is going wrong? FYI I just started working with Dart/Flutter a few days ago so I am still learning how things function here. I come from a Java/C++/Python background.
UPDATE:
I modified my client in response to Richard's comment:
Future<List<AuthRealm>> getAuthRealms() async {
HttpClient client = new HttpClient();
client.badCertificateCallback =((X509Certificate cert, String host, int port) => true);
http.IOClient ioClient = new http.IOClient(client);
final response = await ioClient.get(baseUrl + "access/domains");
print(response.body);
final data = json.decode(response.body);
List<AuthRealm> realms = data.map((j) => AuthRealm.fromJson(j)).toList();
return realms;
}
However I am still getting an error and everything I am seeing just is not working.
I/flutter (12950): {"data":[{"type":"ad","realm":"CELESTIALDATA"},{"type":"pve","comment":"Proxmox VE authentication server","realm":"pve"},{"realm":"pam","comment":"Linux PAM standard authentication","type":"pam"}]}
E/flutter (12950): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: type '(dynamic) => AuthRealm' is not a subtype of type '(String, dynamic) => MapEntry<dynamic, dynamic>' of 'transform'
E/flutter (12950): #0 Client.getAuthRealms (package:Proxcontrol/Client/client.dart:70:35)
E/flutter (12950): <asynchronous suspension>
data is a Map, so you need to access the element in that map that's the list of realms. Use data['data'] to reference that list.
To convert that list of decoded json bits (List<Map<String, dynamic>>) to a list of AuthRealm use .map<AuthRealm>((j) => [something that constructs an AuthRealm]).toList()
This should work:
final data = json.decode(response.body);
List<AuthRealm> realms = data['data'].map<AuthRealm>((j) => AuthRealm.fromJson(j)).toList();
May be you should use setState like this
client.getAuthRealms().then((values) {
setState((){
realms = values;
});
});
in your code
final nextButton = RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24)),
onPressed: () {
Client client = new Client(serverAddress, serverPort);
client.getAuthRealms().then((values) {
setState(() {
realms = values;
});
});
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ServerAuthLoginScreen(authRealms: realms)));
},
padding: EdgeInsets.all(10),
color: Colors.indigoAccent,
child: Text('NEXT', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
);
I'm trying to make an REST app that uses "HTTP Get" communication to login in Flutter. While I had no problem importing "http/http.dart" package and
running the http class methods, I encountered a problem with exception handling in Dart/Flutter. I created a method to call http, but if
the connectivity is down for any reason, it will naturally return a "SocketException" exception. I have no problem handling the exception in the same method
who made the get request, but if I try to pass it up in the caller method stack to the parent method, I just can't catch it again. I found the
"rethrow" keyword, but so far, had no success in rethrowing the exception. Below are some methods I use in my code, both the login method and the caller method:
static Future<JsonEnvelop> loginUser(String email, String passwd) async {
List<int> content = Utf8Encoder().convert(passwd);
crypto.Digest digest = crypto.md5.convert(content);
String url = _baseUrl + _loginUrl + email + "/" + digest.toString();
http.Response response;
try {
response = await http.get(url);
} on SocketException catch(e) {
rethrow;
}
if(response != null && response.statusCode == 200) {
return JsonEnvelop.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to login');
}
}
void onVerifyCodeBtnPressed(BuildContext context) {
if (_formKey.currentState.validate()) {
String email = _emailController.text;
String passwd = _passwdController.text;
Future<JsonEnvelop> envelop;
try {
envelop = RemoteUserServices.loginUser(
email, passwd);
} on SocketException {
throw Exception('Internet is down');
}
Scaffold.of(context).showSnackBar(SnackBar(content: Text('Login to your account')));
envelop.then((JsonEnvelop envelop) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text("Login"),
content: new Text("Login Successful"),
actions: <Widget>[
new FlatButton(
child: new Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
}
);
});
} else {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text("Missing data"),
content: new Text("Type your email and password in the fields"),
actions: <Widget>[
new FlatButton(
child: new Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
}
);
}
}
What could be the problem in this situation? I hope to create a dialog box warning the user the internet is down.
try/catch with exceptions from async code works only in functions with async, otherwise you'd need to pass onError callbacks or use .catchError(...) on the returned Future which is notably more difficult to get right.
void onVerifyCodeBtnPressed(BuildContext context) async { // added async
if (_formKey.currentState.validate()) {
String email = _emailController.text;
String passwd = _passwdController.text;
Future<JsonEnvelop> envelop;
try {
envelop = await RemoteUserServices.loginUser( // added `await`
email, passwd);
} on SocketException {
throw Exception('Internet is down');
}
Instead of using rethrow or throwing a new exception. return a Future.error()
Future<bool> methodThatErrorsOnCall() {
return Future.error();
}
...
...
methodThatErrorsOnCall.catchError((e) {
print('I want to show a dialog: ${e.error}'); // callback fires.
return false; // Future completes with false
})