How to add json to an autocomplete widget in flutter - flutter

Im trying to pass the data from an API to a list so that I can view it in the Autocomplete widget but I keep getting errors. I tried below code.
This is the code I have tried which passes data to the autocomplete as instance of 'Bus'
GetBuilder<BusesListController>(
init: BusesListController(),
builder: (_) {
_.viewPartners();
return DottedBorder(
child: Padding(
padding:
const EdgeInsets.only(left: 8.0),
child: Autocomplete<Bus>(
optionsBuilder: (TextEditingValue
textEditingValue) {
List<Bus> partnercos = [];
partnercos = _.partners.value as List<Bus>;
// (_.partners).map((value) => Bus.fromJson(value as Map<String, dynamic>)).toList();
print(partnercos);
return partnercos
.where((bus) => bus.company!
.toLowerCase()
.contains(textEditingValue
.text
.toLowerCase()))
.toList();
},
)),
);
}),
I also tried passing _.partners directly but it doesn't work either
Other fix I tried is passing _.partners instead of _.partners. Value above which invokes errors in arena.dart in void _tryToResolveArena which shows that state. Members.length == 1 hence scheduleMicrotask(() => _resolveByDefault(pointer, state));
Contoller code
class BusesListController extends GetxController {
var partners = [].obs;
var isLoaded = false.obs;
final loginController = Get.put(LoginController());
Future<void> viewPartners() async {
final token = loginController.rxToken.value;
var headers = {
'Authorization': 'Bearer $token'
};
try {
var url =
Uri.parse(ApiEndPoints.baseUrl + ApiEndPoints.endpoints.listBusAdmin);
http.Response response = await http.get(url, headers: headers);
if (response.statusCode == 200) {
final json = jsonDecode(response.body);
partners. Value =
(json as List).map((json) => Bus.fromJson(json)).toList();
isLoaded.value = true;
} else {
throw jsonDecode(response.body)["Message"] ?? "Unknown Error Occured";
}
} catch (error) {
// Get.snackbar('Error', error.toString());
}
}
#override
void onInit() {
super.onInit();
viewPartners();
}
}
I am able to print the response so I know the api works but I'm having problems with passing partners list into the autocomplete

Related

How can I store the values ​of the json or request in a variable without using future builder or list builder in flutter?

I want to use the information I get from the json or request and be able to use it in a useraccountheader drawer but WITHOUT, using a list builder or future builder.
I usually use a future builder and display the information from the database.
I want to get the json or request information and store it in a variable or use it directly in a text widget.
It is also to have loaded user information.
In the infoinitialuser2 list, the values ​​of the json or request are stored and I show them in the list builder or future builder, but as I just mentioned, I don't want to do it that way.
code::
Class State<NombreCabeceraDrawer> extends StatefulWidget{return nombrecabeceradrawer()}
class nombrecabeceradrawer extends State<NombreCabeceraDrawer> {
verride
void initState() {
cabeceradrawerservices.MostrarInfoInicialUser().then((value) {
setState(() {
info.addAll(value);
});
} );
super.initState();
}
UserAccountsDrawerHeader(
accountName: Text("here i want to show the value of the json or request"),
accountEmai: Text("here i want to show the value of the json or request too")
),
}
-------------------
class InfoUsuarioInicialServices{
Future MostrarInfoInicialUser() async{
Map<String, String> headers = {
'Content-Type':'application/json;charset=UTF-8',
'Charset':'utf-8'
};
var Url= Uri.parse("http://");
final response = await http.get((Url),headers: headers);
print(response.body);
return productInfoUsuarioInicialromJson(response.body);
}
}
---------------------
List productInfoUsuarioInicialromJson(String str) => List<InfoInicialUserModel>.from(json.decode(str).map((x) => InfoInicialUserModel.fromJson(x)));// con esto hago el get
class InfoInicialUserModel{
String UsuarioPk;
String FotoUsuario;
String CorreoUsuario;
String NombreUsuario;
InfoInicialUserModel({this.UsuarioPk,this.FotoUsuario,this.NombreUsuario,this.CorreoUsuario});
factory InfoInicialUserModel.fromJson(Map<String, dynamic> parsedJson){
return InfoInicialUserModel(
UsuarioPk: parsedJson['Usuari'],
FotoUsuario:parsedJson['image'],
NombreUsuario: parsedJson['Usuario_A'],
CorreoUsuario:parsedJson['Usuario_C']
);
}
}
This is how I would do it:
Future GetMostrarInfoInicialUser() async {
Map<String, String> headers = {
'Content-Type': 'application/json;charset=UTF-8',
'Charset': 'utf-8'
};
var Url = Uri.parse("http://");
final response = await http.get((Url), headers: headers);
if (response.statusCode == 200) {
print(response.body);
var jsonData = json.decode(response.body);
if (jsonData == "Error") {
} else {
if (mounted) {
setState(() {
accountEmail = jsonData['accountEmail'];
accountName = jsonData['accountName'];
});
}
}
}
}
#override
void initState() {
GetMostrarInfoInicialUser();
}

Flutter Refresh List From API

i have a GET function in my flutter code and everytime i add a new item to the list. the list doesn't refresh and won't display the newly added item unless i refresh the whole page.
this is my POST method :
Future<http.Response> ajoutFournisseur(
String numeroFournisseur,
String addressFournisseur,
String matriculeFiscaleFournisseur,
String raisonSocialeFournisseur,
String paysFournisseur,
String villeFournisseur,
double timberFiscaleFournisseur) async {
List fournisseurs = [];
final response = await http.post(
Uri.parse('http://127.0.0.1:8000/api/fournisseur/'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, dynamic>{
'tel': numeroFournisseur,
'adresse': addressFournisseur,
'mf': matriculeFiscaleFournisseur,
'raisonSociale': raisonSocialeFournisseur,
'pays': paysFournisseur,
'ville': villeFournisseur,
'timberFiscale': timberFiscaleFournisseur,
}),
);
if (response.statusCode == 200) {
return fournisseurs = jsonDecode(response.body);
} else {
throw Exception('Erreur base de données!');
}
}
Future<dynamic> future;
and this is code of the button to confirm :
ElevatedButton(
onPressed: (() {
if (_formKey.currentState.validate()) {
// If the form is valid, display a snackbar. In the real world,
// you'd often call a server or save the information in a database.
setState(() {
future = ajoutFournisseur(
numeroFournisseur.text,
addressFournisseur.text,
matriculeFiscaleFournisseur.text,
raisonSocialeFournisseur.text,
paysFournisseur.text,
villeFournisseur.text,
double.parse(timberFiscaleFournisseur.text));
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Ajout en cours')),
);
}
}), ...
and this is my GET methos to fetch the items from the list :
fetchFournisseurs() async {
final response =
await http.get(Uri.parse('http://127.0.0.1:8000/api/fournisseur'));
if (response.statusCode == 200) {
var items = jsonDecode(response.body);
setState(() {
fournisseurs = items;
print(fournisseurs[0]['raisonSociale']);
});
} else {
throw Exception('Error!');
}
}
.
.
.
for (var i = 0; i < fournisseurs.length; i++)
Card(
child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
ListTile(
title: Text(fournisseurs[i]['raisonSociale']), ...
how can i refresh the list everytime i add a new item without refreshing the whole page ?
I think you first of all need to learn some Flutter good practices.
For example, don't put your logic into the ElevatedButton, set it into a separate Widget function like below :
class Test extends StatefulWidget {
const Test({ Key? key }) : super(key: key);
#override
State<Test> createState() => _TestState();
}
class _TestState extends State<Test> {
Future<void> _handleAjout() async {
if (_formKey.currentState.validate()) {
// First, check your request succeed
try {
var fournisseurs = await ajoutFournisseur(
numeroFournisseur.text,
addressFournisseur.text,
matriculeFiscaleFournisseur.text,
raisonSocialeFournisseur.text,
paysFournisseur.text,
villeFournisseur.text,
double.parse(timberFiscaleFournisseur.text));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Ajout en cours')),
);
// Will only update state if no error occured
setState(() => future = fournisseurs);
}
on Exception catch(e) {
// Always make sure the request went well
print("error");
}
}
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: (() {
_handleAjout();
}),
)
}
}
And by awaiting POST result, then you can tell your Widget to fetch data and refresh the list by making :
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: (() {
_handleAjout().then(() => fetchFournisseurs());
}),
)
}
The .then function tells Flutter to execute the code contained in the () => myCallbackFunction() only if the previous asynchronous function went well.
By the way, you should always check if your content looks like what you expected before calling setState and set data to your variables :)

Cannot Get StreamBuilder Data

I am trying to get the updated data from a stream but, even though I get data coming down in my future function, the snapshot.data give me this error:
type '_ControllerStream<dynamic>' is not a subtype of type 'Iterable<dynamic>'
Here is my function and stream:
Future getChat(orderId) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var _token = prefs.getString('token');
print('The Latest Order Token is $_token');
final Map<String, dynamic> body = {
"id": "$orderId",
};
final List _messageData = [];
var url = Uri.parse('$_server/api/driver/get/convo/?access_token=$_token');
await http.post(url, body: body, headers: {
"Content-Type": 'application/x-www-form-urlencoded'
}).then((http.Response response) {
print(response.body);
switch (response.statusCode) {
case 200:
final Map<String, dynamic> responseData = json.decode(response.body);
print("The ${response.body}");
var x = responseData['message_data'].split(",");
print(x);
for (int i = 0; i < x.length; i++) {
_messageData.add(x[i]);
print(x[i]);
}
print(x);
break;
default:
final Map<String, dynamic> responseData = json.decode(response.body);
print(responseData);
return _messageData;
}
return _messageData;
});
}
Stream getChatData(Duration refreshTime, id) async* {
while (true) {
await Future.delayed(refreshTime);
yield getChat(id).asStream();
}
}
I get this in the data response:
"message_data": ""11-12-21:09:01:14AM - Billy Fakename: fire
test,11-12-21:09:01:30AM - Test TEster: ewserasece,""
My stream builder is:
Stream _chatStream;
#override
void initState() {
_chatStream = getChatData(Duration(seconds: 3), orderid);
super.initState();
}
StreamBuilder(
stream: _chatStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
final messages = snapshot.data;
List<MessageBubble> messageWidgets = [];
for (var message in messages) {
final msgText = message;
final msgSender = message;
// final msgSenderEmail = message.data['senderemail'];
final currentUser = "loggedInUser.displayName";
// print('MSG'+msgSender + ' CURR'+currentUser);
final msgBubble = MessageBubble(
msgText: msgText,
msgSender: msgSender,
user: currentUser == msgSender);
messageWidgets.add(msgBubble);
}
return Expanded(
child: ListView(
reverse: true,
padding:
EdgeInsets.symmetric(vertical: 15, horizontal: 10),
children: messageWidgets,
),
);
} else {
return Center();
}
},
),
But, I get this error: type '_ControllerStream' is not a subtype of type 'Iterable' or the snapshot will be null.
How do I get the information that shows up in the future function, show up in the stream?
Could you show us where you define _chatStream ?
Your StreamBuilder uses _chatStream but you only showed us where you define the method
Future getChat(orderId)
and the method
Stream getChatData(Duration refreshTime, id)
where you create a stream that you do not use in the code you've shared.
Did you want to use getChatData in your StreamBuilder?
Did I miss something?

SnackBar is not showing in flutter

AS I am new to flutter, I can't find why the SnackBar is not showing on my UI while I am calling different function for API call! In one case it is showing but not in other cases.
I have to show a Snackbar on success of each API call (like in my project it is on success of generateOtp and on success of verifyOtp).
Below is my code.
snackbar.dart
showInSnackBar(String message, key){
key.currentState.showSnackBar(
SnackBar(
content:Text(message),
backgroundColor: Colors.blueAccent[700],
)
);
}
api_service.dart
class ApiService {
bool isVerified = false;
BaseOptions options = BaseOptions(
baseUrl: "http://...",
);
generateOtp(String mobileNo, key) async {
Dio dio = new Dio(options);
FormData formData = FormData.fromMap({'mobile_no': mobileNo});
try {
Response response = await dio.post("generate_otp/", data: formData);
if (response.statusCode == 200) {
// on success of generate otp I have to show a message on SnackBar. But it is not working.
showInSnackBar(response.data["msg"], key);
print(response.data);
}
} on DioError catch (e) {
showInSnackBar(e.message, key);
}
}
Future<bool> verifyOtp(String mobileNo, String otp, key) async {
Dio dio = new Dio(options);
FormData formData = FormData.fromMap(
//.....);
try {
Response response = await dio.post("verify_otp/", data: formData);
if (response.statusCode == 200) {
// here also it is not working.
showInSnackBar(response.data["msg"], key);
// Otp verified
isVerified = true;
}
} on DioError catch (e) {
showInSnackBar(e.message, key);
}
return isVerified;
}
}
register.dart
class _RegisterPageState extends State<RegisterPage> {
var _key = new GlobalKey<ScaffoldState>();
//...........
service.generateOtp(_data.mobileNo, _key); /* here I am calling generateOtp() */
} else {
print('invalid credentials');
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _key,
body: SingleChildScrollView(
//..........
otp.dart
submit() async {
_formKey.currentState.save();
bool verify =
await service.verifyOtp(widget.mobNumber, pinController.text, _key); /* here I am calling
verifyOtp() */
if (verify) {
SharedPreferences preferences = await SharedPreferences.getInstance();
String userInfo = preferences.getString('user_data');
// Decoding String data to map
Map map = json.decode(userInfo);
service.registerUser(map);
} else {
showInSnackBar('Invalid otp', _key); /* here SnackBar is showing on my UI*/
}
}
Can anybody please help me to solve this!
Lack of context, (Context).
docs : https://api.flutter.dev/flutter/widgets/BuildContext-class.html
try this(work for me):
void _showSnackBar(BuildContext context, String text) {
Scaffold.of(context).showSnackBar(SnackBar(content: Text(text)));
}
If You want to use snackbar without context u can use this package get: ^3.13.2
and call snackbar like this any where you want:
Get.snackbar(
"title",
"content",
);

Correct way to call an api by provider in fflutter?

I have been trying to make a app in flutter where an api is called and data is updated in TextField
Used provider for state management, here is the code for it.
class ProfileProvider with ChangeNotifier {
var profileData;
String _url = "http://10.0.2.2:3000/api/v1/user/loggedin_user";
void getData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var token = prefs.getString('token');
var data = await http.get(
_url,
headers: {
"accept": "application/json",
"content-type": "application/json",
'Token': token,
},
);
var infoOfPerson = json.decode(data.body);
profileData = new ProfileObject(
name: infoOfPerson['name'],
mobile: infoOfPerson['mobile'],
email: infoOfPerson['email'],
role: infoOfPerson['role'],
);
notifyListeners();
}
ProfileObject get profileInfo {
return profileData;
}
}
I am getting the data fine, now i have to show it in the UI, but sometime data is populated, sometime its not. Can someone please point me the right direction why this is happening.
Here is the code for UI.
class Profile extends StatefulWidget {
#override
_ProfileState createState() => _ProfileState();
}
class _ProfileState extends State<Profile> {
final emailController = TextEditingController(text: '');
final nameController = TextEditingController(text: '');
final mobileController = TextEditingController(text: '');
var _isInit = true;
#override
void didChangeDependencies() {
if (_isInit) {
final profileData = Provider.of<ProfileProvider>(context);
profileData.getData();
if (profileData.profileInfo != null) {
emailController.text = profileData.profileInfo.name;
nameController.text = profileData.profileInfo.email;
mobileController.text = profileData.profileInfo.mobile;
}
_isInit = false;
super.didChangeDependencies();
}
}
#override
Widget build(BuildContext context) {
final profileData = Provider.of<ProfileProvider>(context);
return Scaffold(
drawer: NavigationDrawer(),
body: profileData.profileInfo == null
? Center(
child: CircularProgressIndicator(),
)
: Builder(
builder: (context) => SingleChildScrollView(
child: Padding(.....
Below the padding, there is normal TextField, can someone tell me why the data is being populated sometime and sometime its coming empty, even I wrapped it with CircularProgressIndicator() and a check the notifyListeners(); is not working there. The loader is not being shown and data is not being loaded.
Thanks
for StatelessWidget.
Inside the build method use:
Future.microtask(() async {
context.read<SomeProvider>().fetchSomething();
});
For StatefulWidgets if you want to call it once. Do this inside the initState() or didChangeDependencies (better if the latter). This will be called at the end of the frame which means after the build or rendering finishes..
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<SomeProvider>().fetchSomething();
});
}
EDIT: WidgetsBinding will also work on build. I forgot on why I used microtask lol
i've created a function which called nextTick, i call it in initState and it works for now, but want to see others method
void nextTick(Function callback, [int milliseconds = 0]) {
Future.delayed(Duration(milliseconds: 0)).then((_) {
callback();
});
}
then use it like below
#override
void initState() {
super.initState();
nextTick((){
ProfileProvider profileProvider = Provider.of<ProfileProvider>(context);
profileProvider.getProfile();
});
}
Edit: i store couple of variables to manage them on ui, like isLoading, hasError and errorMessage. Here is my provider class
class ProfileProvider extends ChangeNotifier {
bool _hasError = false;
bool _isLoading = true;
String _errorMsg = '';
Profile _profileResponse;
bool get hasError => _hasError;
bool get isLoading => _isLoading;
String get errorMsg => _errorMsg;
Profile get profileResponse => _profileResponse;
Future<void> getProfile() async {
this.setLoading = true;
this.setError = false;
this.setErrorMsg = '';
try {
await dio.post('$api/p/view', data: {}).then((res) {
print(res.data);
_profileResponse = Profile.fromJson(jsonDecode(res.data));
print(_profileResponse.data);
notifyListeners();
}).whenComplete(() {
this.setLoading = false;
});
} catch (e) {
this.setError = true;
this.setErrorMsg = '$e';
}
this.setLoading = false;
}
set setError(bool val) {
if (val != _hasError) {
_hasError = val;
notifyListeners();
}
}
set setErrorMsg(String val) {
if (val != null && val != '') {
_errorMsg = val;
notifyListeners();
}
}
set setLoading(bool val) {
_isLoading = val;
notifyListeners();
}
}