how to pass the model class and return data in flutter - flutter

Model class:
#JsonSerializable()
class EmpVerifyEntity extends Equatable {
#JsonKey(name: "access_token")
final String accessToken;
final EmpVerifyEmployee employee;
const EmpVerifyEntity(this.accessToken, this.employee);
factory EmpVerifyEntity.fromJson(Map<String, dynamic> json) =>
_$EmpVerifyEntityFromJson(json);
Map<String, dynamic> toJson() => _$EmpVerifyEntityToJson(this);
static const empty = EmpVerifyEntity(
'123',
EmpVerifyEmployee(
'1', 'avatar', 'sId', 'empName', 'empId', 'designation', 1));
#override
String toString() {
return jsonEncode(this);
}
#override
// TODO: implement props
List<Object?> get props => [accessToken, employee];
}
#JsonSerializable()
class EmpVerifyEmployee extends Equatable {
#JsonKey(name: "password")
final String password;
final String avatar;
#JsonKey(name: "_id")
final String sId;
#JsonKey(name: "emp_name")
final String empName;
#JsonKey(name: "emp_id")
final String empId;
final String designation;
#JsonKey(name: "__v")
final int iV;
const EmpVerifyEmployee(this.password, this.avatar, this.sId, this.empName,
this.empId, this.designation, this.iV);
factory EmpVerifyEmployee.fromJson(Map<String, dynamic> json) =>
_$EmpVerifyEmployeeFromJson(json);
Map<String, dynamic> toJson() => _$EmpVerifyEmployeeToJson(this);
static const empty = const EmpVerifyEmployee(
'password', 'avatar', 'sId', 'empName', 'empId', 'designation', 1);
#override
String toString() {
return jsonEncode(this);
}
#override
// TODO: implement props
List<Object?> get props =>
[password, avatar, sId, empName, empId, designation, iV];
}
auth repo:
class AuthenticationRepository {
Future<void> logIn({ //login process
required String username,
required String password,
}) async {
print("------------------");
print(username);
var res = await http.post(
Uri.parse("https://cots.com/api/v1/employee/login"),
headers: {
'Content-type': 'application/json',
'Accept': 'application/json'
},
body: jsonEncode({"emp_id": username, "password": password}));
dynamic data = json.decode(res.body);
print(data);
if (data['employee']["is_active"] == true) {
_controller.add(AuthenticationStatus.authenticated);///here it shows authenticated
}
}
User Repo:
class UserRepository {
EmpVerifyEntity? _empVerifyEntity;
UserRepository(this._empVerifyEntity);
Future<EmpVerifyEntity?> getUser() async {
if (_empVerifyEntity != null) {
return _empVerifyEntity;
}
}
}
Auth Bloc:
class AuthenticationBloc
extends Bloc<AuthenticationEvent, AuthenticationState> {
AuthenticationBloc({
required AuthenticationRepository authenticationRepository,
required UserRepository userRepository,
}) : _authenticationRepository = authenticationRepository,
_userRepository = userRepository,
super(const AuthenticationState.unknown()) {
on<AuthenticationStatusChanged>(_onAuthenticationStatusChanged);
on<AuthenticationLogoutRequested>(_onAuthenticationLogoutRequested);
_authenticationStatusSubscription = _authenticationRepository.status.listen(
(status) => add(AuthenticationStatusChanged(status)),
);
}
final AuthenticationRepository _authenticationRepository;
final UserRepository _userRepository;
late StreamSubscription<AuthenticationStatus>
_authenticationStatusSubscription;
#override
Future<void> close() {
_authenticationStatusSubscription.cancel();
_authenticationRepository.dispose();
return super.close();
}
void _onAuthenticationStatusChanged(
AuthenticationStatusChanged event,
Emitter<AuthenticationState> emit,
) async {
switch (event.status) {
case AuthenticationStatus.unauthenticated:
return emit(const AuthenticationState.unauthenticated());
case AuthenticationStatus.authenticated:
final user = await _tryGetUser();
return emit(user != null
? AuthenticationState.authenticated(user)
: AuthenticationState.unauthenticated());
default:
return emit(const AuthenticationState.unknown());
}
}
void _onAuthenticationLogoutRequested(
AuthenticationLogoutRequested event,
Emitter<AuthenticationState> emit,
) {
_authenticationRepository.logOut();
}
Future<EmpVerifyEntity?> _tryGetUser() async {
try {
final user = await _userRepository.getUser();
return user; /////----------------Here I'm getting null as a user
} catch (e) {
print(e);
}
}
}
I can able to do login but can't able to fetch data because it shows null, In login method I should return data so that I can able to fetch. It is authenticated but user data is null, how to pass the return data to userRepo so that I can fetch in authBloc.
-------------------------------------THank you--------------------------

Related

How to write exception in build of AsyncNotifier from riverpod

When not connected to the internet, executing the following code will cause a _ClientSocketException.
How should I write the exception handling?
class AsyncTodosNotifier extends AsyncNotifier<List<Todo>> {
Future<List<Todo>> _fetchTodo() async {
final json = await http.get('api/todos'); //** _ClientSocketException Error occurred**
final todos = jsonDecode(json) as List<Map<String, dynamic>>;
return todos.map((todo) => Todo.fromJson(todo)).toList();
}
#override
Future<List<Todo>> build() async {
return _fetchTodo();
}
Using the AsyncNotifier's build() from riverpod, I would like to code exception handling with AsyncValue.guard, but it results in a syntax error.
How should I write it to make it work?
When trying to get json data, if I can't connect to the internet, I want to write exception handling so that it doesn't abort.
Reference:
https://docs-v2.riverpod.dev/docs/providers/notifier_provider
full code:
implementation.
#immutable
class Todo {
const Todo({
required this.id,
required this.description,
required this.completed,
});
factory Todo.fromJson(Map<String, dynamic> map) {
return Todo(
id: map['id'] as String,
description: map['description'] as String,
completed: map['completed'] as bool,
);
}
final String id;
final String description;
final bool completed;
Map<String, dynamic> toJson() => <String, dynamic>{
'id': id,
'description': description,
'completed': completed,
};
}
class AsyncTodosNotifier extends AsyncNotifier<List<Todo>> {
Future<List<Todo>> _fetchTodo() async {
final json = await http.get('api/todos'); //** _ClientSocketException Error occurred**
final todos = jsonDecode(json) as List<Map<String, dynamic>>;
return todos.map((todo) => Todo.fromJson(todo)).toList();
}
#override
Future<List<Todo>> build() async {
return _fetchTodo();
}
Future<void> addTodo(Todo todo) async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async {
await http.post('api/todos', todo.toJson());
return _fetchTodo();
});
}
}
final asyncTodosProvider =
AsyncNotifierProvider<AsyncTodosNotifier, List<Todo>>(() {
return AsyncTodosNotifier();
});

How to solve the problem with types in flutter?

I have an array of elements that come from api and I get and error from api =>
The operator '[]' isn't defined for the type of 'Country'
Response from api looks like this:
{"success":true,"list":[{"id":2,"createdAt":"2022-11-11T15:25:31.680Z","updatedAt":"2022-11-11T15:25:31.680Z","name":"Afghanistan"}]}
This is the type of an element inside list:
class Country {
final int id;
final String createdAt;
final String updatedAt;
final String name;
const Country({
required this.id,
required this.createdAt,
required this.updatedAt,
required this.name
});
}
This is my widget:
class MyWidget extends StatefulWidget {
const MyWidget({super.key});
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
List<Country> countries = [];
Future<void> getCountries() async {
try {
final response = await _apiService.getCountries();
countries = response['list']; // [{"id": 2, "createdAt:""...}]
} catch (e) {
log(e.toString());
rethrow;
}
}
#override
void initState() {
getCountries();
super.initState();
}
#override
Widget build(BuildContext context) {
return Container();
}
}
And if I try to call this, IDE lights me this error in country['name'] =>
final List countriesWithNames = countries.map((country) => country['name']).toList();
Or when I try to get an element from the list, like this => countries[index]['name']
response['list'] returns list of map.You need to convert into model class.
You can use this model class
class Country {
final int id;
final String createdAt;
final String updatedAt;
final String name;
const Country({
required this.id,
required this.createdAt,
required this.updatedAt,
required this.name,
});
Map<String, dynamic> toMap() {
final result = <String, dynamic>{};
result.addAll({'id': id});
result.addAll({'createdAt': createdAt});
result.addAll({'updatedAt': updatedAt});
result.addAll({'name': name});
return result;
}
factory Country.fromMap(Map<String, dynamic> map) {
return Country(
id: map['id']?.toInt() ?? 0,
createdAt: map['createdAt'] ?? '',
updatedAt: map['updatedAt'] ?? '',
name: map['name'] ?? '',
);
}
String toJson() => json.encode(toMap());
factory Country.fromJson(String source) =>
Country.fromMap(json.decode(source));
}
And getting from local json string
final data = response["list"] as List?;
List<Country> countries =
data?.map((e) => Country.fromMap(e)).toList() ?? [];
print(countries);

Login from Flutter into Django Framework always showing Invalid Password

I am trying to login from Flutter to my Django Rest Framework but I am always getting the same error Invalid Password although the credentials are correct.
Here is the login_request_model.dart:
class LoginRequestModel {
LoginRequestModel({
this.username,
this.email,
this.password,
});
late final String? username;
late final String? email;
late final String? password;
LoginRequestModel.fromJson(Map<String, dynamic> json) {
username = json['username'];
email = json['email'];
password = json['password'];
}
Map<String, dynamic> toJson() {
final _data = <String, dynamic>{};
_data['username'] = username;
_data['email'] = email;
_data['password'] = password;
return _data;
}
}
Here is the login_page.dart:
class LoginPage extends StatefulWidget {
const LoginPage({Key? key}) : super(key: key);
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
bool isApiCallProcess = false;
bool hidePassword = true;
GlobalKey<FormState> globalFormKey = GlobalKey<FormState>();
String? userName;
String? password;
#override
void initState() {
super.initState();
}
Rest of the login_page.dart where login is inserted:
Center(
child: FormHelper.submitButton(
"Login",
() {
if (validateAndSave()) {
setState(() {
isApiCallProcess = true;
});
LoginRequestModel model = LoginRequestModel(
username: userName,
password: password,
);
APIService.login(model).then(
(response) {
setState(() {
isApiCallProcess = false;
});
if (response) {
Navigator.pushNamedAndRemoveUntil(
context,
'/home',
(route) => false,
);
} else {
FormHelper.showSimpleAlertDialog(
context,
Config.appName,
"Invalid Username/Password !!",
"OK",
() {
Navigator.of(context).pop();
},
);
}
},
Here is the login_response_model.dart:
class LoginResponse {
dynamic? key;
List<dynamic>? non_field_errors;
LoginResponse({this.key, this.non_field_errors});
factory LoginResponse.fromJson(mapOfBody) {
return LoginResponse(
key: mapOfBody['key'],
non_field_errors: mapOfBody['non_field_errors'],
);
}
}
LoginResponseModel loginResponseJson(String str) =>
LoginResponseModel.fromJson(json.decode(str));
class LoginResponseModel {
dynamic? key;
List<dynamic>? non_field_errors;
LoginResponseModel({this.key, this.non_field_errors});
LoginResponseModel.fromJson(mapOfBody) {
key:
mapOfBody['key'];
non_field_errors:
mapOfBody['non_field_errors'];
}
Map<String, dynamic> toJson() {
final _data = <String, dynamic>{};
_data['key'] = key;
_data['non_field_errors'] = non_field_errors;
return _data;
}
}
class Data {
Data({
required this.username,
required this.email,
required this.date,
required this.id,
required this.key,
});
late final String username;
late final String email;
late final String date;
late final String id;
late final String key;
Data.fromJson(Map<String, dynamic> json) {
username = json['username'];
email = json['email'];
date = json['date'];
id = json['id'];
key = json['key'];
}
Map<String, dynamic> toJson() {
final _data = <String, dynamic>{};
_data['username'] = username;
_data['email'] = email;
_data['date'] = date;
_data['id'] = id;
_data['key'] = key;
return _data;
}
}
In my django rest framework I am getting Bad Request: /api/dj-rest-auth/login/
My question:
As there is not specific error that I am trying to fix, I want to know how can I spot the error by debugging I need an example of how to try and catch error. Is there something wrong with my LoginRequestModel or LoginResponseModel
it is usually a mismatch error, try to check the following
check if the data you are sending from your flutter side, is being recieved accurately from the django side. like for example, if the backend accepts the data like this..
{
"username" : "user",
"password" : "password"
}
and you are sending the data from flutter side like this..
{
"email" : "user",
"password" : "password"
}
it expects a key of username and is receiving an email, then its a bad request
I'm not sure about django being case sensitive, but sending the data as Username instead of username.. this results in a bad request response.
so in short, to debug this.. convert the data you are sending to json and see how the data is being sent.. and check from the django side how its being received.

Why is GetConnect/GetX/Flutter not calling my backend and returning a null object?

I'm using get: 4.6.5
I have defined a provider
class CredentialsProvider extends GetConnect implements GetxService {
#override
void onInit() {
httpClient.defaultDecoder =
(val) => Auth.fromJson(val as Map<String, dynamic>);
httpClient.baseUrl = 'http://localhost:1337/api/';
super.onInit();
}
Future<Response<dynamic>> postCredentials(Credentials credentials) async {
return await post('auth/local', credentials);
}
}
In my binding class add it as a dependency
class LoginBinding extends Bindings {
#override
void dependencies() {
Get.lazyPut(() => CredentialsProvider());
Get.lazyPut(() => LoginController());
}
}
And register the LoginView as a route
GetPage(
name: "/login",
page: () => const LoginView(),
binding: LoginBinding(),
)
And added it to my controller
class LoginController extends GetxController {
final provider = Get.put(CredentialsProvider());
//...
}
The controller is used in my LoginView
class LoginView extends GetView<LoginController> {...}
In my MaterialButton of the LoginView I use the onPressed to call the provider and get the result object Auth and print it out as json.
onPressed: () {
var c = Credentials(
identifier: controller.emailController.text,
password: controller.passwordController.text);
controller.provider.postCredentials(c).then((value) {
var auth = value.body as Auth;
print(auth.toJson());
});
},
I generated my Auth model from JSON using the GetX cli:
class Auth {
String? jwt;
User? user;
Auth({this.jwt, this.user});
Auth.fromJson(Map<String, dynamic> json) {
jwt = json['jwt'];
user = json['user'] != null ? User?.fromJson(json['user']) : null;
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['jwt'] = jwt;
if (user != null) {
data['user'] = user?.toJson();
}
return data;
}
}
class User {
int? id;
String? username;
String? email;
String? provider;
bool? confirmed;
bool? blocked;
String? createdAt;
String? updatedAt;
User(
{this.id,
this.username,
this.email,
this.provider,
this.confirmed,
this.blocked,
this.createdAt,
this.updatedAt});
User.fromJson(Map<String, dynamic> json) {
id = json['id'];
username = json['username'];
email = json['email'];
provider = json['provider'];
confirmed = json['confirmed'];
blocked = json['blocked'];
createdAt = json['createdAt'];
updatedAt = json['updatedAt'];
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['id'] = id;
data['username'] = username;
data['email'] = email;
data['provider'] = provider;
data['confirmed'] = confirmed;
data['blocked'] = blocked;
data['createdAt'] = createdAt;
data['updatedAt'] = updatedAt;
return data;
}
}
What I get in the console is
flutter: {jwt: null}
And my localhost service is never called.
The issue was with the decoder for the request being different that for the response. So I ended up with something like this:
Future<Auth> postCredentials(Map<String, dynamic> body) async {
var response = await post(
contentType: 'application/json',
decoder: (val) => Auth.fromJson(val as Map<String, dynamic>),
"/api/auth/local",
body);
return response.body as Auth;
}
And I call this via
controller.provider.postCredentials(credentials.toJson());

Can't emit state conditionally on stream values (Flutter-Bloc)

In constructor of the following bloc, I subscribe to a stream of firebase auth changes. go_router handles redirection.
When a user signs in/out _onAppUserChanged is called. If successfully signed in, I await for user's firestore data to be fetched. If there is, among the data, a location entry, I need to emit AppState with status==AppStatus.hasLocation if and only if my CatalogBlog has emitted state with status==CatalogStatus.loaded. Neither await for (var catalogState in _catalogBloc.stream) nor adding a listener will work. AppState with status: AppStatus.hasLocation won't be emmited although CatalogBloc will definatelly emit with status==CatalogStatus.loaded at some point.
class AppBloc extends Bloc<AppEvent, AppState> {
final UserRepository _userRepository;
final AuthRepository _authRepository;
final CatalogBloc _catalogBloc;
AppBloc(
UserRepository userRepository,
AuthRepository authRepository,
PrefsRepository prefsRepository,
CatalogBloc catalogBloc,
) : _userRepository = userRepository,
_authRepository = authRepository,
_catalogBloc = catalogBloc,
super(const AppState()) {
on<AppUserChanged>(_onAppUserChanged);
on<AppSignOutRequested>(_onAppSignOutRequested);
on<AppSelectedLocationSet>(_onAppSelectedLocationSet);
_authRepository.getAuthStateChanges().listen((String uid) {
add(AppUserChanged(uid));
});
}
void _onAppUserChanged(AppUserChanged event, Emitter<AppState> emit) async {
if (event.uid.isEmpty) {
emit(const AppState(status: AppStatus.noUser));
} else {
await for (var user in _userRepository.getUserData(event.uid)) {
if (_catalogBloc.state.status == CatalogStatus.initial) {
_catalogBloc.add(CatalogStarted());
}
if (user.locations.isEmpty) {
// Go to '/location_search'
emit(AppState(status: AppStatus.noLocation, user: user));
} else {
// Wait for catalog data to be fetched, then go to '/catalog'
await for (var catalogState in _catalogBloc.stream) {
if (catalogState.status == CatalogStatus.loaded) {
emit(AppState(
status: AppStatus.hasLocation,
user: user,
selectedLocation: user.prefLocation,
));
}
}
}
}
}
}
}
App State:
enum AppStatus { initial, hasLocation, noLocation, noUser, error }
class AppState extends Equatable {
final AppStatus status;
final User user;
final Location? selectedLocation;
const AppState({
this.status = AppStatus.initial,
this.user = const User(),
this.selectedLocation = const Location(),
});
#override
List<Object?> get props => [status, user, selectedLocation];
AppState copyWith({
AppStatus? status,
User? user,
Location? selectedLocation,
}) {
return AppState(
status: status ?? this.status,
user: user ?? this.user,
selectedLocation: selectedLocation ?? this.selectedLocation);
}
}
AppEvents:
abstract class AppEvent extends Equatable {
const AppEvent();
#override
List<Object> get props => [];
}
class AppUserChanged extends AppEvent {
final String uid;
const AppUserChanged(this.uid);
#override
List<Object> get props => [uid];
}
User repo:
class UserRepository {
final FirebaseFirestore _db = FirebaseFirestore.instance;
late User? user;
Stream<User> getUserData(String uid) {
var documentStream = _db.collection('users').doc(uid).snapshots();
return documentStream.map((snapshot) {
var data = snapshot.data();
if (data != null) {
user = User.fromJson(data);
return user!;
}
throw Exception("Firestore entry was not created for the signed in user");
});
}
...
}