flutter : display current user information flutter [closed] - flutter

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 16 days ago.
Improve this question
i try to display user info like email and user name in profile page , how to get user data for current user
i used flutter , php , mysql , thats my code :
1- login controller
abstract class LoginController extends GetxController {
// late TextEditingController phone;
late TextEditingController password;
late TextEditingController username;
LoginData loginData = LoginData(Get.find());
late StatusRequiest statusRequiest;
bool isShowPassword = true;
GlobalKey<FormState> formstate = GlobalKey<FormState>();
toSignUp();
toForgetPassword();
login();
showPassword() {
isShowPassword = isShowPassword == true ? false : true;
update();
}
}
class LoginControllerImp extends LoginController {
#override
login() async {
var formdata = formstate.currentState;
if (formdata!.validate()) {
statusRequiest = StatusRequiest.loading;
var response = await loginData.postdata(username.text,password.text); //getData from "Data folder" not "controller folder"
// if (kDebugMode) {
// print("===================== $response");
// }
statusRequiest = handlingData(response);
if (StatusRequiest.success == statusRequiest) {
if (response["status"] == "success") {
// data.addAll(response['data']);
Get.offAllNamed(AppRoutes.navbar);
// print(response);
} else {
Get.defaultDialog(title: "99".tr, middleText: "110".tr);
statusRequiest = StatusRequiest.faliure;
}
}
update();
// Get.offAllNamed(AppRoutes.verviyCodeSignUp);
} else {
if (kDebugMode) {
print('wrong');
}
}
}
#override
toSignUp() {
Get.toNamed(AppRoutes.signUp);
}
#override
toForgetPassword() {
Get.toNamed(AppRoutes.forgetPassword);
}
#override
void onInit() {
// phone = TextEditingController();
password = TextEditingController();
username = TextEditingController();
super.onInit();
}
#override
void dispose() {
// phone.dispose();
username.dispose();
password.dispose();
super.dispose();
}
}
2- json :
class UserModel {
String? userId;
String? userName;
String? userImage;
String? userCreate;
UserModel({
this.userId,
this.userName,
this.userImage,
this.userCreate,
});
UserModel.fromJson(Map<String, dynamic> json) {
userId = json['user_id'];
userName = json['user_name'];
userImage = json['user_image'];
userCreate = json['user_create'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['user_id'] = userId;
data['user_name'] = userName;
data['user_image'] = userImage;
data['user_create'] = userCreate;
return data;
}
}
3-
class LoginData {
Crud crud;
LoginData(this.crud);
postdata(String username, String password) async {
var response = await crud.postData(AppLink.login, {
"username": username,
// "phone": phone,
"password": password,
});
return response.fold((l) => l, (r) => r);
}
}
4-profile controller
abstract class ProfileController extends GetxController {
initialData();
getdata();
// goToItems(List categories, int selectedCat, String categoryid);
// goToDetails(List items, int selecteditems, String itemsid);
}
class ProfileControllerImp extends ProfileController {
MyServices myServices = Get.find();
String? username;
// String? email;
String? id;
ProfileData homeData = ProfileData(Get.find());
List users=[];
late StatusRequiest statusRequiest;
#override
initialData() {
username = myServices.sharedPreferences.getString("username");
id = myServices.sharedPreferences.getString("id");
}
#override
void onInit() {
getdata();
initialData();
super.onInit();
}
#override
getdata() async {
statusRequiest = StatusRequiest.loading;
var response = await homeData.getData(); //getData from "Data folder" not "controller folder"
statusRequiest = handlingData(response);
if (StatusRequiest.success == statusRequiest) {
// SharedPreferences sharedPreferences = SharedPreferences;
if (response['status'] == "success") {
users.addAll(response['users']);
print(users);
// items.addAll(response['items']);
if (kDebugMode) {
print(response);
}
} else {
statusRequiest = StatusRequiest.faliure;
}
}
update();
}
}
5-profile page
class Profile extends GetView<ProfileControllerImp> {
final UserModel? usermodel;
const Profile({super.key, this.usermodel});
#override
Widget build(BuildContext context) {
Get.put(ProfileControllerImp());
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
// automaticallyImplyLeading: false,
title: Text(
// '${usermodel?.userName}',
'118'.tr,
style: const TextStyle(color: Colors.black),
),
// actions: [],
centerTitle: true,
elevation: 0,
),
body: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount:2,
itemBuilder: (context, index) {
return SingleChildScrollView(
physics: NeverScrollableScrollPhysics(),
child: Text(controller.username!),
anyone can help plz
in php website and html i used to use $_session['username'] but here in flutter i dont know how to save in seesion , if you can solve this problem in same way plz help and thnx

use your user model, as i can see you areastoraging the data of the user in a class called User so you can use this same class to acces that data in the profile controller in the line
users.addAll(response['users']);
you create a list of user so you have to acces to the index of the list that contains the data of the user and instead of $_sesion["username"] you can use .username for example if you wanna show the data of the first user of the list you can do this:
users[0].name;
and thats equivalent to: $_sesion["username"]

Related

Flutter firebase Getx shopping cart: How do I increase the quantity of items without getting duplicates?

So right now I'm building a shopping cart using GetX and firebase. I'm trying to write the data to firebase if the user taps add to cart but not allow duplicate entries just increase the quantity #. Right now I'm getting duplicate entries.
class CartController extends GetxController {
static CartController instance = Get.find();
//cart controller for the shopping cart
Rx<double> totalCartPrice = 0.0.obs;
RxList<CartItemModel> products = RxList<CartItemModel>([]);
#override
void onReady() {
super.onReady();
ever(logController.userModel, editTotalPrice);
}
void addToCart(ProductModel product) {
try {
if (_isItemAlreadyAdded(product)) {
Get.snackbar("Check your cart", "${product.name} is already added");
} else {
String itemID = Uuid().v1();
logController.updateUserData({
"cart": FieldValue.arrayUnion([
{
"id": itemID,
"productId": product.id,
// "name": product.name,
"quantity": 1,
"price": product.price,
"image": product.image,
"cost": product.price,
}
])
});
Get.snackbar("Item added", "${product.name} was added to your cart");
}
} catch (e) {
Get.snackbar("Whoops...Cannot add this right now!",
"There was an error adding to cart",
duration: Duration(seconds: 3), backgroundColor: Colors.pinkAccent);
debugPrint(e.toString());
}
}
void deductCartItem(CartItemModel cartItem) {
try {
logController.updateUserData({
"cart": FieldValue.arrayRemove([cartItem.toJson()])
});
} catch (e) {
Get.snackbar("Error", "Cannot remove this item");
debugPrint(e.toString());
}
}
editTotalPrice(LoginUserModel usrModel) {
if (usrModel.cart!.isEmpty) {
print("Cart empty!");
} else if (usrModel.cart!.isNotEmpty) {
totalCartPrice.value = 50;
print("hi");
usrModel.cart!.forEach((cartItem) {
totalCartPrice.value += cartItem.cost!;
});
}
}
bool _isItemAlreadyAdded(ProductModel product) =>
logController.userModel.value.cart!
.where((item) => item.name == product.name)
.isNotEmpty;
void decreaseQuantity(CartItemModel item) {
if (item.quantity == 1) {
deductCartItem(item);
} else {
deductCartItem(item);
item.quantity! - 1;
logController.updateUserData({
"cart": FieldValue.arrayUnion([item.toJson()])
});
}
}
void increaseQuantity(CartItemModel item) {
deductCartItem(item);
item.quantity! + 1;
logger.i({"quantity": item.quantity});
logController.updateUserData({
"cart": FieldValue.arrayUnion([item.toJson()])
});
}
}
and here is my class:
class CartItemModel {
CartItemModel(
{this.productId,
this.id,
this.image,
this.name,
this.quantity,
required this.price,
this.cost});
CartItemModel.fromMap(Map<String, dynamic> data) {
id = data[ID];
image = data[IMAGE];
name = data[NAME];
quantity = data[QUANTITY];
cost = data[COST].toDouble();
productId = data[PRODUCT_ID];
price = data[PRICE];
}
static const COST = "cost";
static const ID = "id";
static const IMAGE = "image";
static const NAME = "name";
static const PRICE = "price";
static const PRODUCT_ID = "productId";
static const QUANTITY = "quantity";
double? cost;
String? id;
String? image;
String? name;
double? price;
String? productId;
int? quantity;
Map toJson() => {
ID: id,
PRODUCT_ID: productId,
IMAGE: image,
NAME: name,
QUANTITY: quantity,
COST: price! * quantity!,
PRICE: price
};
}
and because this is tied into the login session here is my login class:
class LoginUserModel {
String? displayName;
String? email;
String? photoUrl;
String? uid;
List<CartItemModel>? cart;
LoginUserModel(
{this.displayName, this.email, this.photoUrl, this.uid, this.cart});
LoginUserModel.fromSnapshot(DocumentSnapshot<Map<String, dynamic>> snapshot) {
displayName = snapshot.data()!["DISPLAYNAME"];
photoUrl = snapshot.data()!["PHOTOURL"];
email = snapshot.data()!["EMAIL"];
uid = snapshot.data()!["UID"];
cart = _convertCartItems(snapshot.data()!["CART"] ?? []);
}
List<CartItemModel> _convertCartItems(List cartFomDb) {
List<CartItemModel> _result = [];
if (cartFomDb.length > 0) {
cartFomDb.forEach((element) {
_result.add(CartItemModel.fromMap(element));
});
}
return _result;
}
List cartItemsToJson() => cart!.map((item) => item.toJson()).toList();
}
And my login controller:
class LoginController extends GetxController {
static LoginController instance = Get.find();
Rxn<User> fbUser = Rxn<User>();
final googleSignIn = GoogleSignIn();
RxBool isLoggedIn = false.obs;
Rx<LoginUserModel> userModel = LoginUserModel().obs;
String usersCollection = "users";
// Rx<UserModel> usrModel = UserModel().obs;
GoogleSignInAccount? _googleAcc;
LoginUserModel? _userModel;
#override
void onReady() {
super.onReady();
fbUser = Rxn<User>(auth.currentUser);
fbUser.bindStream(auth.userChanges());
ever(fbUser, setInitialScreen);
}
LoginUserModel? get loggedInUserModel => _userModel;
setInitialScreen(User? user) {
if (user == null) {
print("going to login page...");
Get.offAll(() => LoginPage());
} else {
print("The user is ${user.displayName}");
userModel.bindStream(listenToUser());
Get.offAll(() => AppSetup());
}
}
void googleLogin() async {
final googleUser = await googleSignIn.signIn();
if (googleUser == null) return;
_googleAcc = googleUser;
final googleAuth = await googleUser.authentication;
final cred = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
try {
await auth.signInWithCredential(cred).then((res) async {
print('Signed in successfully as ' + res.user!.displayName.toString());
print('email: ' + res.user!.email.toString());
LoginUserModel _newUser = LoginUserModel(
uid: res.user!.uid,
email: res.user!.email!,
displayName: res.user!.displayName,
photoUrl: res.user!.photoURL,
cart: [],
);
_addUserToFB(_newUser, res.user!);
});
} catch (e) {
debugPrint(e.toString());
Get.snackbar("Sign In Failed", "Try again");
}
}
// void signUp() async {
// try {
// await auth
// .createUserWithEmailAndPassword(
// email: email.text.trim(), password: password.text.trim())
// .then((result) {
// String _userId = result.user.uid;
// _addUserToFirestore(_userId);
// _clearControllers();
// });
// } catch (e) {
// debugPrint(e.toString());
// Get.snackbar("Sign In Failed", "Try again");
// }
// }
void signOut() async {
googleSignIn.signOut();
auth.signOut();
}
//maybe add clear controllers method?
updateUserData(Map<String, dynamic> data) {
logger.i("UPDATED");
firebaseFirestore
.collection(usersCollection)
.doc(fbUser.value?.uid)
.update(data);
}
Stream<LoginUserModel> listenToUser() => firebaseFirestore
.collection(usersCollection)
.doc(fbUser.value!.uid)
.snapshots()
.map((snapshot) => LoginUserModel.fromSnapshot(snapshot));
_addUserToFB(LoginUserModel usr, User firebaseUser) {
firebaseFirestore.collection(usersCollection).doc(usr.uid).set({
"displayName": usr.displayName,
"id": usr.uid,
"photoURL": usr.photoUrl,
"email": usr.email,
"cart": usr.cart
});
}
}
My firebase database is pictured below. The cart is in a subcategory (array) as I have it tied to the user who is logged in:
It looks like the issue is in your arrayUnion call because you are adding a new entry to the shopping cart array. There is perhaps an expectation that the method will find and update the provided object in the array.
What you would need to do is to find and replace/update the cart item in your local user object array and make a document update back to Firestore.
But instead of doing that, I'd like to suggest something you could consider: you might want to consider making the shopping cart a subcollection instead.
For two reasons
You have an unbound growing data-set (shopping cart items). That is making a good candidate for being a collection of its own.
You want to be able to update field values in individual documents (shopping cart items) without causing too much other "noise" in your app. When you store the shopping cart in your user object and update items in the array, you are also causing any listener that is subscribing to your user object to trigger a new read from the database.
Implementing your shopping cart as a sub collection allows for you to do this instead
Future<void> increaseQuantity(CartItemModel item) {
return FirebaseFirestore.instance
.collection("users")
.doc(userId)
.collection("cart")
.doc(item.id)
.update({"quantity": FieldValue.increment(1)});
}
You would do the same for decreaseQuantity and update quantity with FieldValue.increment(-1) instead.

Flutter shared_preferences save list<any_class>

I tried to save a list to my shared preferences but the list isn't a String list it is a list of a special type class "todo". I tried it with ".cast<Todo>();", this worked with prefs.getStringList.... but dont work with prefs.setStringList.
Here is a screenshot:
When I try to do prefs.setStringList("shoppingCard", _todos); it says: "The argument type 'List' can't be assigned to the parameter type 'List'."
This is the source code of the class todo:
class Todo {
Todo({required this.name, required this.checked});
final String name;
bool checked;
}
class TodoItem extends StatelessWidget {
TodoItem({
required this.todo,
required this.onTap,
}) : super(key: ObjectKey(todo));
final Todo todo;
final Function onTap;
TextStyle? _getTextStyle(bool checked) {
if (!checked) return null;
return const TextStyle(
color: Colors.black54,
decoration: TextDecoration.lineThrough,
);
}
#override
Widget build(BuildContext context) {
return ListTile(
onTap: () {
onTap(todo);
},
leading: CircleAvatar(
child: Text(todo.name[0]),
),
title: Text(todo.name, style: _getTextStyle(todo.checked)),
);
}
}
If you need to save list of custom class you need convert it to string. First change your class model to this:
class Todo {
Todo({required this.name, required this.checked});
final String name;
bool checked;
static Todo fromJson(Map<String, dynamic> json) {
return Todo(name: json['name'], checked: json['checked']);
}
Map<String, dynamic> toJson() {
return {
'name': name,
'checked': checked,
};
}
}
then for saving your list in SharedPreferences, try this:
var prefs = await SharedPreferences.getInstance();
prefs.setString('shoppingCard',
jsonEncode({'data': _todos.map((e) => e.toJson()).toList()}));
and for getting it, try this:
var prefs = await SharedPreferences.getInstance();
String? str = prefs.getString('shoppingCard');
if (str != null) {
var result = (jsonDecode(str) as Map)['data'] as List;
result.map((e) => Todo.fromJson(e)).toList();
}
for example in you case, lets say we have list below:
List<Todo> _todos = [
Todo(checked: false, name: 'test1'),
Todo(checked: true, name: 'test2')
];
we add this list to SharedPreferences, like this:
Future<void> _addTodoItem(String name) async {
var prefs = await SharedPreferences.getInstance();
prefs.setString('shoppingCard',
jsonEncode({'data': _todos.map((e) => e.toJson()).toList()}));
_textFieldController.clear();
}
and we get list from SharedPreferences, like this:
Future<void> _getodoItem(String name) async {
var prefs = await SharedPreferences.getInstance();
var value = prefs.getString('shoppingCard');
if (value != null) {
var result = (jsonDecode(value) as Map)['data'] as List;
setState(() {
_todos = result.map((e) => Todo.fromJson(e)).toList();
});
}
}

Understanding and implementing OOP (in flutter/dart) to abstract shared structure and functionality

(Edited to clarify & update with progress)
When building an app with various lists of items and some cross-referencing between them; e.g. I have a stock class and a StockSet that extends ChangeNotifier as well as holds some basic rest functionality etc. But I'm also going to have Clients, Meetings etc, all of which are basically the same except for minor differences in fields.
So I recon I should define a parent class, say Item and ItemSet that, and then the actual elements in my app will extend those. - Or is that over-engineering?
However, in say my current StockSet I have methods to fetch either a single, or a number of stock items, i.e. ...Future<Stock> _fetchAStock() async {...
the only significant difference between it and say client records will be the REST url, and type of object returned (and give or take a few fields).
Is it better to have individual methods in all the child level classes - seems simpler though more code = more potential errors etc. OR build the fetch functionality into the parent class with some kind of abstraction/specification/parameterization(/or external configuration look-up) of the case specific differences?
New-ish to OOP and Flutter/Dart and it seems clearer some days than others... this is an others :-)
What I have:
class StocksSetRoute extends StatelessWidget with asc_alertBar {
const StocksSetRoute({Key? key}) : super(key: key);
static const indexStr = 'stocks';
static const labelStr = 'Properties';
static GlobalKey myKey = GlobalKey();
static GlobalKey parentKey = GlobalKey();
#override
Widget build(BuildContext context) {
var stockSet = context.watch<StockSet>();
return Scaffold(
key: parentKey,
appBar: AppBar(
title: const TitleRow(indexStr: indexStr, labelStr: labelStr),
),
body: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// these will be filters, order toggle etc.
children: [
ElevatedButton(
onPressed: (stockSet.isRemoteEmpty)
? null
: () async {
try {
await stockSet.fetch(5);
} catch (e) {
alertBar(
'Could not fetch any more. ', stockSet as ItemSet,
backgroundColor: Colors.grey, context: context);
}
},
child: const Text('Fetch More'),
),
ElevatedButton(
onPressed:
(stockSet.stocks.isEmpty) ? null : () => stockSet.clear(),
child: const Text('Clear'),
),
],
),
Expanded(
child: StockListViewBuilder(
stockSet: stockSet, theKey: myKey, alert: alertBar))
],
),
);
}
}
class StockListViewBuilder extends StatefulWidget {
final StockSet stockSet;
final GlobalKey theKey;
final Function alert;
const StockListViewBuilder({
Key? key,
required this.stockSet,
required this.theKey,
required this.alert,
}) : super(key: key);
#override
State<StockListViewBuilder> createState() => _StockListViewBuilderState();
}
class _StockListViewBuilderState extends State<StockListViewBuilder>
with asc_alertBar {
final ScrollController _scrollController = ScrollController();
late double _scrollPosition;
late double _maxScrollExtent;
late bool isThisTheEnd = false;
_scrollListener() async {
setState(() {
_scrollPosition = _scrollController.position.pixels;
_maxScrollExtent = _scrollController.position.maxScrollExtent;
});
if (!isThisTheEnd && _scrollPosition / _maxScrollExtent > 0.90) {
isThisTheEnd = true;
if (widget.stockSet.isRemoteEmpty) {
alertBar('No more items available', null, context: context);
} else {
await widget.stockSet.fetch(5);
}
}
if (isThisTheEnd && _scrollPosition / _maxScrollExtent <= 0.90) {
isThisTheEnd = false;
}
}
#override
void initState() {
super.initState();
int listCount;
_scrollController.addListener(_scrollListener);
WidgetsBinding.instance.addPostFrameCallback((_) async {
listCount = widget.stockSet.stocks.length;
if (listCount < 10 && !widget.stockSet.isRemoteEmpty) {
try {
await widget.stockSet.fetch(10);
} catch (e) {
super.setState(() {
widget.alert("Can't load stock.", widget.stockSet,
backgroundColor: Colors.red);
});
}
}
});
}
#override
Widget build(BuildContext context) {
return ListView.builder(
scrollDirection: Axis.vertical,
controller: _scrollController,
shrinkWrap: true,
key: widget.theKey,
itemCount: widget.stockSet.stocks.length + 1,
itemBuilder: (context, index) {
if (index <= widget.stockSet.stocks.length - 1) {
return InkWell(
onTap: (() => Navigator.pushNamed(
context,
'/stocks/stock',
arguments: ScreenArguments(widget.stockSet.stocks[index]),
)),
child: StockListItem(
stock: widget.stockSet.stocks[index],
));
} else {
return LoadingItemNotifier(
isLoading: widget.stockSet.isBusyLoading,
);
}
},
);
}
}
class StockSet extends ItemSet {
final List<Stock> _stocks = [];
List<Stock> get stocks => _stocks;
List<int> getHaveStocksIds() {
final List<int> ids = _stocks.map((stock) => stock.id).toList();
return ids;
}
void add(Stock stock) {
_stocks.add(stock);
notifyListeners();
}
void remove(Stock stock) {
_stocks.remove(stock);
notifyListeners();
}
void clear() {
_stocks.clear();
isBusyLoading = false;
isRemoteEmpty = false;
notifyListeners();
}
Future<void> fetch([int num = 1]) async {
int i = 0;
for (i; i < num; i++) {
if (!isRemoteEmpty) {
try {
Stock tmpStock = await _fetchAStock();
if (getHaveStocksIds().contains(tmpStock.id)) {
throw Exception('We allready have ${tmpStock.id}');
}
add(tmpStock);
} catch (e) {
i = num;
isRemoteEmpty = true;
isBusyLoading = false;
notifyListeners();
throw Exception('No more to fetch $e');
}
}
}
return;
}
Future<Stock> _fetchAStock() async {
List<int> have = getHaveStocksIds();
final queryParameters = {
'exclude': json.encode(have),
};
isBusyLoading = true;
notifyListeners();
try {
final response = await http.post(
Uri.https('onethread.design', 'agency/wp-json/ypagp/v1/pr-get-a',
queryParameters),
);
isBusyLoading = false;
notifyListeners();
if (response.statusCode != 200) {
throw HttpException('${response.statusCode}');
}
final Map<String, dynamic> map = json.decode(response.body);
return Stock(
id: map['id'] as int,
title: map['title'] as String,
description: map['description'] as String,
thumbUrl: map['thumbUrl'] as String,
);
} on SocketException {
feedback = 'Please enable an internet connection.';
notifyListeners();
} on HttpException {
feedback = "Couldn't find the/a post.";
notifyListeners();
} on FormatException {
feedback = "Bad response format.";
} catch (e, s) {
feedback = 'fetchA catch $e $s ';
}
throw Exception('Could not _fetchAStock');
}
}
class ItemSet extends ChangeNotifier {
bool isBusyLoading = false;
bool isRemoteEmpty = false;
String? feedback;
}
what I'm aiming at (and have partially succeeded in implementing) is something like
class StockSet extends ItemSet and class ItemSet extends ChangeNotifier {...
class Item extends Equatable {
const Item({
required this.id,
required this.title,
required this.description,
});
final int id;
final String title;
final String description;
#override
List<Object> get props => [id, title, description];
}
and I think then
class Stock extends Item {
final String thumbUrl;
const Stock(
{required super.id, required super.title, required super.description, required this.thumbUrl});
#override
List<Object> get props => [id, title, description, thumbUrl];
}
though when it comes to the ItemSet's methods, e.g.
been trying things like e.g.
in ItemSet
Future<Type> fetchAItem(String typeSlug, Type type) async {
List<int> have = getHaveItemsIds();
final queryParameters = {
'exclude': json.encode(have),
};
developer.log('_fetchAItem $queryParameters');
isBusyLoading = true;
notifyListeners();
try {
final response = await http.post(
Uri.https('###', 'a###/v1/$typeSlug-get-a',
queryParameters),
);
developer.log('response.statusCode:${response.statusCode}');
isBusyLoading = false;
notifyListeners();
if (response.statusCode != 200) {
throw HttpException('${response.statusCode}');
}
final Map<String, dynamic> map = json.decode(response.body);
return Item(
id: map['id'] as int,
title: map['title'] as String,
description: map['description'] as String,
) as type;// no! - was worth a guess though
} on...
but I'm getting lost in the plumbing, should I use #override in the child classes? seems clunky - or maybe use more mixins?.
Thanks in advance
and the alertBar mixin...
mixin asc_alertBar {
void alertBar(
String message,
ItemSet? itemSet, {
String label = 'Okay',
Function? action,
Color? backgroundColor,
required BuildContext context,
}) {
if (itemSet != null) {
String? itemSetFeedback = itemSet.feedback;
if (itemSetFeedback != null) {
message += '\n$itemSetFeedback';
itemSet.feedback = null;
}
}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: backgroundColor,
duration: const Duration(seconds: 15),
content: AnimatedText(
textContent: message,
durationFactor: 0.25,
),
action: SnackBarAction(
label: label,
onPressed: () => action,
),
),
);
}
}
I hope the question makes sense.

fetch data by using flutter http request and load more data on scroll down the screen

i fetch data from server using flutter http request and load more data when user scroll to bottom of screen. i receive this error "Unhandled Exception: type 'List' is not a subtype of type 'Product'". Please help, i struggle all day without success.
model.dart file
class Product {
final int id;
final String accountName,
callNumber,
whatsappNumber,
businessLocation,
caption;
final List<Images> productPhoto;
Product({
this.id,
this.accountName,
this.callNumber,
this.whatsappNumber,
this.businessLocation,
this.caption,
this.productPhoto,
});
// this is static method
factory Product.fromJson(Map<String, dynamic> json) {
return Product(
id: json['id'],
accountName: json['account_name'],
callNumber: json['call_number'],
whatsappNumber:
json['whatsapp_number'] != null ? json['whatsapp_number'] : null,
businessLocation: json['business_location'],
caption: json['caption'],
productPhoto:
(json['post_photos'] as List).map((i) => Images.fromJson(i)).toList(),
);
}
}
class Images {
final String filename;
Images({this.filename});
factory Images.fromJson(Map<String, dynamic> json) {
return Images(
filename: json['filename'],
);
}
}
explore.dart file (i import models.dart to this file)
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:windowshoppi/models/global.dart';
import 'package:windowshoppi/models/product.dart';
import 'package:http/http.dart' as http;
class Explore extends StatefulWidget {
#override
_ExploreState createState() => _ExploreState();
}
class _ExploreState extends State<Explore> {
ScrollController _scrollController = ScrollController();
List<Product> data;
String nextUrl;
#override
void initState() {
// TODO: implement initState
super.initState();
this.fetchProduct(http.Client(), ALL_PRODUCT_URL);
_scrollController.addListener(() {
// print(_scrollController.position.pixels);
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
if (nextUrl != null) {
this.fetchProduct(http.Client(), nextUrl);
}
// print(nextUrl);
}
});
}
Future<List<Product>> fetchProduct(http.Client client, url) async {
final response = await client.get(url);
if (response.statusCode == 200) {
Map<String, dynamic> mapResponse = json.decode(response.body);
nextUrl = mapResponse['next'];
if (mapResponse["count"] != "") {
final products = mapResponse["results"].cast<Map<String, dynamic>>();
final listOfProducts = await products.map<Product>((json) {
return Product.fromJson(json);
}).toList();
// return listOfProducts;
setState(() {
data.add(listOfProducts);
});
} else {
return [];
}
} else {
throw Exception('failed to load data from internet');
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('http get'),
),
body: ListView.builder(
controller: _scrollController,
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Container(
height: 200,
color: Colors.blue,
child: Text(data[index].caption),
),
);
},
),
);
}
}
Have a look at this part of the code.
final listOfProducts = await products.map<Product>((json) {
return Product.fromJson(json);
}).toList();
In the .map() method you are casting it to type < Product >. So judging by the error you have mentioned, "Unhandled Exception: type 'List' is not a subtype of type Product"
I think the json data being returned contains a List, instead of the product fields. I would highly recommend you to once check the json data being returned, and double-check if you are targeting the correct JSON tree nodes.
Let me know if this solved the issue.

How to authenticate the websocket in flutter

I am using Traccar but could not use websocket in Flutter as it is not providing any data.
I think websocket requires authentication or tokens to get the data.
class _HomeState extends State<Home> {
IOWebSocketChannel channel = IOWebSocketChannel.connect("ws://167.172.215.197:8082/api/socket");
#override
Widget build(BuildContext context) {
print(channel);
return new Scaffold(
resizeToAvoidBottomPadding: false,
appBar: AppBar(
title: Text('Map'),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// details(),
StreamBuilder(
stream: channel.stream,
builder: (context, snapshot) {
print(snapshot);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 24.0),
child: Container(
child:
Column(
children: <Widget>[
Text(snapshot.hasData ? '${snapshot.data.positions}' : 'No Data'),
],
),
),
);
},
)
],
),
);
}
}
I want to receive the data from the server using websocket in flutter.
yes you need to authenticate and use some variables, for example:
String _cookie;
String _email = "your_email";
String _password = "your_password" ;
final _dio = Dio();
String _cookie;
String serverUrl = "your_server_ip";
StreamSubscription<dynamic> _rawPosSub;
final _devicesMap = <int, Device>{};
final _positions = StreamController<Device>.broadcast();
Future<void> _getConnection({String protocol = "http", String email, String password}) async {
final addr = "$protocol://$serverUrl/api/session";
Map<String, String> body = {
'email' : '$email',
'password': '$password',
};
final response = await Dio().post(addr, data: body,
options: new Options(contentType:"application/x-www-form-urlencoded"));
_cookie = response.headers["set-cookie"][0];
print(_cookie);
}
Next, you should need to create a Device Class (Don't forget to change the project name in the import line of utils)
import 'package:geopoint/geopoint.dart';
import 'package:your_project/utils/utils.dart';
/// A class representing a device
class Device {
/// Main constructor
Device(
{this.id,
this.uniqueId,
this.groupId,
this.name,
this.position,
this.batteryLevel,
this.keepAlive = 1,
this.isActive,
this.isDisabled,
this.properties = const <String, dynamic>{}});
/// The device database id
final int id;
/// The on device unique id
String uniqueId;
/// The group of the device
int groupId;
/// The device name
String name;
/// The device position
DevicePosition position;
/// The device battery level
double batteryLevel;
/// Minutes a device is considered alive
int keepAlive;
/// The device can be disabled
bool isDisabled;
/// false if the device has never updated one position
bool isActive;
/// Extra properties for the device
Map<String, dynamic> properties;
/// Is the device online
bool get isAlive => _isDeviceAlive();
/// Create a device from json data
Device.fromPosition(Map<String, dynamic> data,
{String timeZoneOffset = "0", int keepAlive = 1})
: this.keepAlive = keepAlive,
this.id = int.parse(data["deviceId"].toString()),
this.position =
DevicePosition.fromJson(data, timeZoneOffset: timeZoneOffset),
this.batteryLevel =
double.parse(data["attributes"]["batteryLevel"].toString());
bool _isDeviceAlive() {
if (position == null) {
return false;
}
final now = DateTime.now();
final dateAlive = now.subtract(Duration(minutes: keepAlive));
bool isAlive = false;
if (position.date.isAfter(dateAlive)) {
isAlive = true;
}
return isAlive;
}
/// Print a description of the device
void describe() {
print("Device:");
print(" - id : $id");
print(" - uniqueId : $uniqueId");
print(" - name : $name");
print(" - batteryLevel: $batteryLevel");
print(" - position : $position");
}
#override
String toString() {
String _name = "$uniqueId";
if (name != null) {
_name = name;
}
String res;
if (position != null) {
res = "$_name: $position";
} else {
res = "$_name";
}
return res;
}
}
/// A class to handle a device position
class DevicePosition {
/// The position database id
final int id;
/// The geo data
final GeoPoint geoPoint;
/// The distance since previous point
final double distance;
/// The total distance for the device
final double totalDistance;
/// The address of the device position
final String address;
/// The date of the position
DateTime date;
/// Create a position from json
DevicePosition.fromJson(Map<String, dynamic> data,
{String timeZoneOffset = "0"})
: this.id = int.parse(data["id"].toString()),
this.geoPoint = GeoPoint(
name: data["id"].toString(),
latitude: double.parse(data["latitude"].toString()),
longitude: double.parse(data["longitude"].toString()),
speed: double.parse(data["speed"].toString()),
accuracy: double.parse(data["accuracy"].toString()),
altitude: double.parse(data["altitude"].toString())),
this.distance = double.parse(data["attributes"]["distance"].toString()),
this.totalDistance =
double.parse(data["attributes"]["totalDistance"].toString()),
this.address = data["address"].toString() {
this.date = dateFromUtcOffset(data["fixTime"].toString(), timeZoneOffset);
}
#override
String toString() {
return "$date : ${geoPoint.latitude}, ${geoPoint.longitude}";
}
}
Also you should use a utils method
/// parse a date
DateTime dateFromUtcOffset(String dateStr, String timeZoneOffset) {
DateTime d = DateTime.parse(dateStr);
if (timeZoneOffset.startsWith("+")) {
final of = int.parse(timeZoneOffset.replaceFirst("+", ""));
d = d.add(Duration(hours: of));
} else if (timeZoneOffset.startsWith("-")) {
final of = int.parse(timeZoneOffset.replaceFirst("-", ""));
d = d.subtract(Duration(hours: of));
}
return d;
}
Finally you should need the following methods to listen positions:
/// Get the device positions
Future<Stream<Device>> positions() async {
final posStream =
await _positionsStream(serverUrl: serverUrl, email: _email, password: _password);
_rawPosSub = posStream.listen((dynamic data) {
print("DATA $data");
final dataMap = json.jsonDecode(data.toString()) as Map<String, dynamic>;
if (dataMap.containsKey("positions")) {
DevicePosition pos;
for (final posMap in dataMap["positions"]) {
//print("POS MAP $posMap");
pos = DevicePosition.fromJson(posMap as Map<String, dynamic>);
final id = posMap["deviceId"] as int;
Device device;
if (_devicesMap.containsKey(id)) {
device = _devicesMap[id];
} else {
device = Device.fromPosition(posMap as Map<String, dynamic>,
keepAlive: 1);
}
device.position = pos;
_devicesMap[id] = device;
_positions.sink.add(device);
}
} else {
for (final d in dataMap["devices"]) {
if (!_devicesMap.containsKey(d["id"])) {
final id = int.parse(d["id"].toString());
d["name"] ??= d["id"].toString();
final device = Device(id: id, name: d["name"].toString());
_devicesMap[id] = device;
//print(" - ${device.name}");
}
}
}
});
return _positions.stream;
}
Future<Stream<dynamic>> _positionsStream(
{String serverUrl, String email, String password, String protocol = "http"}) async {
if (_cookie == null) {
await _getConnection(email: _email, password: _password);
}
final channel = IOWebSocketChannel.connect("ws://$serverUrl/api/socket",
headers: <String, dynamic>{"Cookie": _cookie});
return channel.stream;
}
When you finish, you can call
_init() async {
_getConnection(email: _email, password: _password);
final pos = await positions();
print("Listening for position updates");
pos.listen((device) {
print("POSITION UPDATE: $device");
print("${device.id}: ${device.position.geoPoint.latitude} / " +
"${device.position.geoPoint.longitude}");
});
}
Also I use these dependences and flutter version 1.17.0 stable:
dio: ^3.0.9
web_socket_channel:
geopoint: ^0.7.1
Note: I use code from traccar_client 0.1.0 and modify it to access from email and password, but if you need to use the token, you can follow the example from
https://github.com/synw/traccar_client. The credits are to them. :)