Parse data Map from json fail, how to fix - flutter

I've method search from api, data model like object, but i don't know how to parse, anyone can help me let see my code below, i have search method and api link, if u wanna other code, u can comment on column comment
import 'package:get/get.dart';
import 'package:resto_app/app/models/detail_model.dart';
import 'package:resto_app/shared/constant/api_services.dart';
import 'package:resto_app/shared/services/base_client.dart';
class SearchController extends GetxController {
//TODO: Implement SearchController
RxBool isAwaitData = false.obs;
// RxList<DetailModel> ;
Future<List<DetailModel>> searchController(
{String query = '', bool isWait = true}) async {
if (isWait) {
isAwaitData.value = true;
List result = await BaseClient().get(
baseUrl: ApiService.baseURL, api: ApiService.searchResto + query);
// var response = await http
// .get(Uri.parse(ApiService.baseURL + ApiService.searchResto + query));
return result.map((e) => DetailModel.fromJson(e)).where((resto) {
final nameLower = resto.restaurant.name.toLowerCase();
final cityLower = resto.restaurant.city.toLowerCase();
final searchLower = query.toLowerCase();
return nameLower.contains(cityLower);
}).toList();
} else {
throw Exception();
}
// if (response.statusCode == 200) {
// final result = Map<String, dynamic>.from(jsonDecode(response.body));
// result.map((key, value) =>
// key.contains(result)
// );
// }
}
}
class ApiService {
static const baseURL = 'https://restaurant-api.dicoding.dev';
static const allResto = '/list';
static const detailPage = '/detail/';
static const searchResto = '/search?q=';
}

The API doesn't return a list, but a map - The restaurants key of the response points to a list of objects.
Try:
final Map<String, dynamic> responseData = await BaseClient().get(
baseUrl: ApiService.baseURL,
api: ApiService.searchResto + query
);
final restaurantData = responseData['restaurants'];
return restaurantData.map((e) => DetailModel.fromJson(e)).where((resto) {
final nameLower = resto.restaurant.name.toLowerCase();
final cityLower = resto.restaurant.city.toLowerCase();
final searchLower = query.toLowerCase();
return nameLower.contains(cityLower);
}).toList();

Related

Flutter Dart update dynamic data using variable on a map to get data using provider

I want assign _slug on a variable map. The error shoes The instance member '_slug' can't be accessed in an initializer. When I set static keyword it works. But I want to set the value dynamically.
Here is my code .
`
class SingleProductFetchProvider extends ChangeNotifier {
late String _slug;
void onClicked(BuildContext context, {required String slug}) {
_slug = slug;
notifyListeners();
Navigator.pushNamed(
context,
MyRoutes.productDetails,
);
}
Map<String, String> singleProductVariable = {"slug": _slug};
Future<ProductDetailsModel> singleProductFetch() async {
// print(_productSlug);
QueryResult queryResult = await qlclient.query(
QueryOptions(
document: gql(QueryDocument.productDetailsSingle),
variables: singleProductVariable),
);
var data = queryResult.data as Map<String, dynamic>;
var body = ProductDetailsModel.fromJson(data);
return body;
}
}
// Future<void> _fetchUsers() async {
// _homeState = HomeState.Loading;
// try {
// await Future.delayed(Duration(seconds: 5));
// final apiusers = await UserApi.instance.getAllUser();
// users = apiusers;
// _homeState = HomeState.Loaded;
// } catch (e) {
// message = '$e';
// _homeState = HomeState.Error;
// }
// notifyListeners();
// }
// Future<List<ProductDetailsModel>> productDetailsFetch(String slug) async {
// late List<ProductDetailsModel> productd = [];
// print(slug);
// if (condition) {
// return productd;
// }
// }
// String get my => _productSlug;
// var _text = "samsung-galaxy-a53-5g-8gb-128gb-free-gift-updated-9";
// var varibalbe = {"slug": ""};
`
I want to get set the slug variable dynamically.
if you want to assign a map to a string from the concept you can't assign a map to a string however, you can convert the map to a String and then assign it or also you may go with jsonEncode() it will work let me know if you need a proper explanation.

Mokito wont return a stubbed value

I started TDD recently and it has slowed my progress, I m trying to stub a return value with Mockito, but i keep getting "null is not a subtype of Future-dynamic' not sure what i did wrong, here is my setup
class ImageSelectorMock extends Mock implements ImageSelector {}
getImageSelectorMock({logo}) {
_unregisterIfRegistered<ImageSelector>();
var imageSelector = ImageSelectorMock();
if (logo != null) {
when(imageSelector.businessLogo).thenReturn('string');
when(imageSelector.uploadImage(null, 'folder'))
.thenAnswer((realInvocation) async =>
Future.value('send this')
);
}
locator.registerSingleton<ImageSelector>(imageSelector);
return imageSelector;
}
setupService() {
//getAuthServiceMock();
//getFirestoreServiceMock();
//getNavigationServiceMock();
getImageSelectorMock();
}
void _unregisterIfRegistered<T extends Object>() {
if (locator.isRegistered<T>()) {
locator.unregister<T>();
}
}
getImageSelectorMock takes an optional arg "logo" to check if imageSelector.uploadImage is called. Also, I'm using a locator/get_it package
group('RegistrationviewmodelTest -', () {
setUp(() => {setupService()});
test('when business logo is NOT null => uplaodImage should be called', () async {
// final navigation = getNavigationServiceMock();
// final fireStore = getFirestoreServiceMock();
final imageSelector = await getImageSelectorMock(logo: "test");
final model = RegistraionViewModel();
await model.createBusiness({
'title': "text",
'description': "text",
});
verify(imageSelector.uploadImage('', 'upload'));
// verify(navigation.navigateTo());
});
});
// uploadImage does
Future uploadImage(file, folder) async {
// String fileName = basename(_imageFile.path);
String fileName = basename(file.path);
final firebaseStorageRef =
FirebaseStorage.instance.ref().child('$folder/$fileName');
final uploadTask = firebaseStorageRef.putFile(file);
final taskSnapshot = await uploadTask;
return taskSnapshot.ref.getDownloadURL().then((value) => value);
}
uploadImage is just an image picker that returns download Url from fire storege.
Thanks for helping out!!

Flutter Riverpod : How to Implement FutureProvider?

I using Flutter Riverpod package to handling http request. I have simple Http get request to show all user from server, and i using manage it using FutureProvider from Flutter Riverpod package.
API
class UserGoogleApi {
Future<List<UserGoogleModel>> getAllUser() async {
final result = await reusableRequestServer.requestServer(() async {
final response =
await http.get('${appConfig.baseApiUrl}/${appConfig.userGoogleController}/getAllUser');
final Map<String, dynamic> responseJson = json.decode(response.body);
if (responseJson['status'] == 'ok') {
final List list = responseJson['data'];
final listUser = list.map((e) => UserGoogleModel.fromJson(e)).toList();
return listUser;
} else {
throw responseJson['message'];
}
});
return result;
}
}
User Provider
class UserProvider extends StateNotifier<UserGoogleModel> {
UserProvider([UserGoogleModel state]) : super(UserGoogleModel());
Future<UserGoogleModel> searchUserByIdOrEmail({
String idUser,
String emailuser,
String idOrEmail = 'email_user',
}) async {
final result = await _userGoogleApi.getUserByIdOrEmail(
idUser: idUser,
emailUser: emailuser,
idOrEmail: idOrEmail,
);
UserGoogleModel temp;
for (var item in result) {
temp = item;
}
state = UserGoogleModel(
idUser: temp.idUser,
createdDate: temp.createdDate,
emailUser: temp.emailUser,
imageUser: temp.emailUser,
nameUser: temp.nameUser,
tokenFcm: temp.tokenFcm,
listUser: state.listUser,
);
return temp;
}
Future<List<UserGoogleModel>> showAllUser() async {
final result = await _userGoogleApi.getAllUser();
state.listUser = result;
return result;
}
}
final userProvider = StateNotifierProvider((ref) => UserProvider());
final showAllUser = FutureProvider.autoDispose((ref) async {
final usrProvider = ref.read(userProvider);
final result = await usrProvider.showAllUser();
return result;
});
After that setup, i simply can call showAllUser like this :
Consumer((ctx, read) {
final provider = read(showAllUser);
return provider.when(
data: (value) {
return ListView.builder(
itemCount: value.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
final result = value[index];
return Text(result.nameUser);
},
);
},
loading: () => const CircularProgressIndicator(),
error: (error, stackTrace) => Text('Error $error'),
);
}),
it's no problem if http request don't have required parameter, but i got problem if my http request required parameter. I don't know how to handle this.
Let's say , i have another http get to show specific user from id user or email user. Then API look like :
API
Future<List<UserGoogleModel>> getUserByIdOrEmail({
#required String idUser,
#required String emailUser,
#required String idOrEmail,
}) async {
final result = await reusableRequestServer.requestServer(() async {
final baseUrl =
'${appConfig.baseApiUrl}/${appConfig.userGoogleController}/getUserByIdOrEmail';
final chooseURL = idOrEmail == 'id_user'
? '$baseUrl?id_or_email=$idOrEmail&id_user=$idUser'
: '$baseUrl?id_or_email=$idOrEmail&email_user=$emailUser';
final response = await http.get(chooseURL);
final Map<String, dynamic> responseJson = json.decode(response.body);
if (responseJson['status'] == 'ok') {
final List list = responseJson['data'];
final listUser = list.map((e) => UserGoogleModel.fromJson(e)).toList();
return listUser;
} else {
throw responseJson['message'];
}
});
return result;
}
User Provider
final showSpecificUser = FutureProvider.autoDispose((ref) async {
final usrProvider = ref.read(userProvider);
final result = await usrProvider.searchUserByIdOrEmail(
idOrEmail: 'id_user',
idUser: usrProvider.state.idUser, // => warning on "state"
);
return result;
});
When i access idUser from userProvider using usrProvider.state.idUser , i got this warning.
The member 'state' can only be used within instance members of subclasses of 'package:state_notifier/state_notifier.dart'.
It's similiar problem with my question on this, but on that problem i already know to solved using read(userProvider.state) , but in FutureProvider i can't achieved same result using ref(userProvider).
I missed something ?
Warning: This is not a long-term solution
Assuming that your FutureProvider is being properly disposed after each use that should be a suitable workaround until the new changes to Riverpod are live. I did a quick test to see and it does work. Make sure you define a getter like this and don't override the default defined by StateNotifier.
class A extends StateNotifier<B> {
...
static final provider = StateNotifierProvider((ref) => A());
getState() => state;
...
}
final provider = FutureProvider.autoDispose((ref) async {
final a = ref.read(A.provider);
final t = a.getState();
print(t);
});
Not ideal but seems like a fine workaround. I believe the intention of state being inaccessible outside is to ensure state manipulations are handled by the StateNotifier itself, so using a getter in the meantime wouldn't be the end of the world.

How do you turn a List <dynamic> into a List <Map> >>

I started to learn Flutter because I want to build an app which can handle API-Calls.
But now I'm frustrated because I want to make an infinite Load and don't get it to work.
The Problem is, that the method require an Future<List> but I dont know how to convert the response from the API into an List
Future<List<Map>> _getServerData(int length) async{
String api = data.url +length.toString();
final res=
await http.get("data.url");
if (res.statusCode == 200) {
List<dynamic> resp = jsonDecode(res.body);
return resp;
} else {
throw Exception('Failed to load DATA');
}
}
The whole class is out of an Tutorial from oodavid.
But in his tutorial he dont use an API
Future<List<Map>> _getExampleServerData(int length) {
return Future.delayed(Duration(seconds: 1), () {
return List<Map>.generate(length, (int index) {
return {
"body": WordPair.random().asPascalCase,
"avatar": 'https://api.adorable.io/avatars/60/${WordPair.random().asPascalCase}.png',
};
});
});
}
That was the how he solved it
Down below is the whole class
import 'dart:async';
import 'dart:convert';
import 'package:Kontra/pages/articel_list.dart';
import 'package:http/http.dart' as http;
import 'package:Kontra/api/url.dart' as data;
import 'package:Kontra/api/articelsResponse.dart';
/// Example data as it might be returned by an external service
/// ...this is often a `Map` representing `JSON` or a `FireStore` document
Future<List<Map>> _getServerData(int length) async{
String api = data.url +length.toString();
final res=
await http.get(data.url);
if (res.statusCode == 200) {
List<dynamic> resp = jsonDecode(res.body);
return resp;
} else {
throw Exception('Failed to load DATA');
}
}
/// PostModel has a constructor that can handle the `Map` data
/// ...from the server.
class PostModel {
String sId;
String title;
String text;
String author;
String pictures;
String link;
int postId;
String createdAt;
PostModel({this.title, this.text, this.pictures, this.link, this.postId});
factory PostModel.fromServerMap(Map<String, dynamic> json) {
return PostModel(
title: json['title'],
text: json['text'],
pictures: json['pictures'],
link: json['link'],
postId: json['postId']
);
}
}
/// PostsModel controls a `Stream` of posts and handles
/// ...refreshing data and loading more posts
class PostsModel {
int reload = 0;
Stream<List<PostModel>> stream;
bool hasMore;
bool _isLoading;
List<Map> _data;
StreamController<List<Map>> _controller;
PostsModel() {
_data = List<Map>();
_controller = StreamController<List<Map>>.broadcast();
_isLoading = false;
stream = _controller.stream.map((List<Map> postsData) {
return postsData.map((Map postData) {
return PostModel.fromServerMap(postData);
}).toList();
});
hasMore = true;
refresh();
}
Future<void> refresh() {
return loadMore(clearCachedData: true);
}
Future<void> loadMore({bool clearCachedData = false}) {
if (clearCachedData) {
_data = List<Map>();
hasMore = true;
}
if (_isLoading || !hasMore) {
return Future.value();
}
_isLoading = true;
return _getServerData(reload++).then((postsData) {
_isLoading = false;
_data.addAll(postsData);
hasMore = (_data.length < 30);
_controller.add(_data);
});
}
}
Thanks for your help guys
Try with
return List<Map>.from(resp.whereType<Map>());
Or
return resp.whereType<Map>().toList();
Or
return resp.cast<Map>();

Is there any way to get the data returned from the class (apiData) in "Dart"?

class _CategoriesPageState extends State<CategoriesPage> {
List postsData;
int categoryID;
void getApiData() async {
//Get posts api data
ApiData apiData = ApiData(
apiUrl: 'http://website.com/wp-json/wp/v2/posts?categories=$categoryID',
);
dynamic responseBody = await apiData.getData();
setState(() {
postsData = json.decode(responseBody);
});
}
Now I want to reach the (apiData) in my widget, but because it's inside void function I cant use it.
You have to create a variable outside function, so that you can access by class object.
class _CategoriesPageState extends State<CategoriesPage> {
List postsData;
ApiData apiData
int categoryID;
void getApiData() async {
//Get posts api data
apiData = ApiData(
apiUrl: 'http://website.com/wp-json/wp/v2/posts?categories=$categoryID',
);
dynamic responseBody = await apiData.getData();
setState(() {
postsData = json.decode(responseBody);
});
}