I have the below class like.dart that need to send some data to home.dart, the start page is the home dart and after click on button on post navigate to other page like.dart with parameters, after user click on like I want to update the view on home.dart to show that user liked this post
class imageModal extends StatefulWidget {
//imageModal({Key key}) : super(key: key);
final title;
final comments;
final links;
final urlImages;
final userid;
final postid;
final userlikes;
int totallikes;
final like;
imageModal(this.title,this.comments,this.links,
this.urlImages,this.postid,this.userid,this.userlikes,this.totallikes,this.like);
#override
imageModalState createState() => imageModalState();
}
...
new IconButton(
icon: new Icon(Icons.favorite_border, color: Colors.black) ,
onPressed: (){
_like(widget.postid,state);
}
),
...
_like(postid,StateSetter updateState) async {
SharedPreferences localStorage = await SharedPreferences.getInstance();
var userid = jsonDecode(localStorage.getString('userid'));
var user = jsonDecode(localStorage.getString('user'));
var data = {
'userid' : userid,
'postid': postid,
};
var res = await Network().like(data, 'post/like/');
var body = json.decode(res.body);
print(body['data']['users']);
updateState(() {
widget.like.add(body['data']);
//print( posts[i]['like']);
widget.userlikes.add(body['data']);
var totallikes = widget.totallikes;
var finallikes = totallikes+1;
widget.totallikes = finallikes;
});
}
I want to send to the home.dart the below values new values
widget.like
widget.userlikes
widget.totallikes
and when I send them to setstate them so can update the view
If you want to return from the other screen to home with the updated values, you can use Navigator.pop(context, result);
In FirstScreen
_navigateToAnotherScreen(BuildContext context) async {
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
// print(result);
}
In SecondScreen
Navigator.pop(context, [value1, value2, value3]);
Use state management technique like Provider
https://pub.dev/packages/provider
Related
I am trying to develop login checker. The following code should redirect to HomeScreen when user is loggedin and to LoginPage in User is not loggedin. But it only gets redirect to LoginScreen() not to HomeScreen() even though it's already logged in.
User Model:
class User {
User({
required this.id,
required this.name,
required this.email,
required this.cart,
});
String id;
String name;
String email;
List cart;
}
UserController:
class UserController extends GetxController {
RxString id = ''.obs;
RxBool isLoggedIn = false.obs;
RxString name = ''.obs;
RxString email = ''.obs;
RxString image = ''.obs;
RxList cart = [].obs;
final NetworkHandler _handler = NetworkHandler();
#override
void onInit() {
super.onInit();
getUserDetails();
}
getUserDetails() {
User _user = _handler.userDetails();
id.value = _user.id;
if (id.value != '') {
isLoggedIn.value = true;
}
name.value = _user.name;
}
}
Checker Screen
class StateCheck extends StatelessWidget {
var controller = Get.put(UserController());
StateCheck({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
SizeConfig().init(context);
return Obx(
() => controller.isLoggedIn.value
? const HomeScreen()
: const LoginScreen(),
);
}
}
First you have to set login value in SharedPreferences.
when login success
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool("isLogin", true);
log out
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool("isLogin", false);
You can Check login state || check this in splash screen for best practice (inside init method)
if(prefs.getBool('isLogin')==null||prefs.getBool('isLogin')==false){
// login screen
}else{
// home screen
}
I've create List<String> favId = []; variable to store item's ID with SharedPreferences, so the favorited items ID didnt lost after I restart the application. And here is my SharedPreferences method and favorite IconButton in detailDoaPage.dart :
...
static List<String> favId = [];
getData() async {
SharedPreferences pref = await SharedPreferences.getInstance();
setState(() {
favId = pref.getStringList("id") ?? [];
});
}
void initState() {
super.initState();
getIds();
}
getIds() async {
favId = getData();
}
void saveData() async {
SharedPreferences pref = await SharedPreferences.getInstance();
pref.setStringList("id", favId);
}
...
IconButton(
icon: Icon(
favId.contains(doa.id.toString())
? Icons.favorite
: Icons.favorite_border,
color: favId.contains(doa.id.toString())
? Colors.red
: Colors.grey,
),
onPressed: () => setState(() {
doa.fav = !doa.fav;
if (favId.contains(doa.id.toString())) {
favId.removeWhere(
(element) => element == doa.id.toString());
} else {
favId.add(doa.id.toString());
}
saveData();
favId.sort();
}),
)
Beside that, I also want to show the favorited item's with ListView.builder in favPage.dart (another page). Of course I want get the favId from detailDoaPage.dart. How can I implement the provider/riverpod across this 2 pages?
Here is the preview of my app :
Thank you :)
My recommended approach would be to create a StateNotifier that handles the state as well as the interactions with SharedPreferences. The following simplifies the logic in your widgets as well.
final sharedPrefs =
FutureProvider<SharedPreferences>((_) async => await SharedPreferences.getInstance());
class FavoriteIds extends StateNotifier<List<String>> {
FavoriteIds(this.pref) : super(pref?.getStringList("id") ?? []);
static final provider = StateNotifierProvider<FavoriteIds, List<String>>((ref) {
final pref = ref.watch(sharedPrefs).maybeWhen(
data: (value) => value,
orElse: () => null,
);
return FavoriteIds(pref);
});
final SharedPreferences? pref;
void toggle(String favoriteId) {
if (state.contains(favoriteId)) {
state = state.where((id) => id != favoriteId).toList();
} else {
state = [...state, favoriteId];
}
// Throw here since for some reason SharedPreferences could not be retrieved
pref!.setStringList("id", state);
}
}
Usage:
class DoaWidget extends ConsumerWidget {
const DoaWidget({Key? key, required this.doa}) : super(key: key);
final Doa doa;
#override
Widget build(BuildContext context, ScopedReader watch) {
final favoriteIds = watch(FavoriteIds.provider);
return IconButton(
icon: favoriteIds.contains('') ? Icon(Icons.favorite) : Icon(Icons.favorite_border),
color: favoriteIds.contains('') ? Colors.red : Colors.grey,
onPressed: () => context.read(FavoriteIds.provider.notifier).toggle(doa.id.toString()),
);
}
}
I have two page and I want to use the variable 'id' in the second screen to fetch data from API.
What should I do?
Screen one: it's the product screen where user click on profile image and after that I get all information about user owner in the second screen.
Screen two: I display data for this user by id
NB: I get all the data by API
id is always Null
Screen one:
InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UserProfile(
id: id,
)),
);
// do something here
},
),
Screen two:
class UserProfile extends StatefulWidget {
final int id;
const UserProfile({Key key, #required this.id}) : super(key: key);
#override
_UserProfileState createState() => _UserProfileState();
}
class _UserProfileState extends State<UserProfile> {
#override
void initState() {
getprofile(id);
super.initState();
}
Future<List<dynamic>> getprofile(int id) async {
var response = await Network().getData('/auth/user/$id');
data = json.decode(response.body);
return data;
}
When you want to use a property from the StatefulWidget you need to use widget.propertyName. In your case it's widget.id
class _UserProfileState extends State<UserProfile> {
#override
void initState() {
getprofile(widget.id);
super.initState();
}
Future<List<dynamic>> getprofile(int id) async {
var response = await Network().getData('/auth/user/$id');
data = json.decode(response.body);
return data;
}
Either do the same that you did before,so pass the id as a parameter to the _UserProfileState class, so just call:
_UserProfileState(#required this.id) : super();
Another option to make variables available is to use the Provider widget
I have a simple page, which shows a product, and i am trying to use riverpod, and futureprovider, but the future is only called the first time i go to the page?
final productInfo = FutureProvider<Map<String, dynamic>>((ref) async {
final response =
await http.get('https://www.api.com/$clickedID');
final content = json.decode(response.body) as Map<String, dynamic>;
return content;
});
With the recent Riverpod, you can do ref.refresh(userProvider) to refresh the provider.
Please use
final userProvider = StreamProvider<User>(
final response =
await http.get('https://www.api.com/$clickedID');
final content = json.decode(response.body) as Map<String, dynamic>;
return content;
);
now you can listen to the provider using watch,
AsyncValue<User> user = watch(userProvider);
No inbuilt functionality, but you can override the value with the same future at a later time.
recallProductInfoFutureProvider() {
final response = http.get('https://www.api.com/$clickedID');
final content = json.decode(response.body) as Map<String, dynamic>;
productInfo.overrideWithValue(content)
}
Consider using with StreamProvider and watching inside Consumer to update UI on overrideWithValue.
There are a two options.
ref.refresh(provider)
ref.invalidate(provider)
Note that if you are using a AsyncValue, the old result will be returned when you refresh the FutureProvider and AsyncValue.isRefreshing will be set to true:
final notificationRepositoryProvider = FutureProvider<bool?>((ref) async {
Future<bool> approveDocument() => Future.delayed(Duration(seconds: 1), () => Future.value(Random().nextBool()));
return approveDocument();
});
class HomeView extends ConsumerStatefulWidget {
const HomeView({Key? key}) : super(key: key);
#override
HomeViewState createState() => HomeViewState();
}
class HomeViewState extends ConsumerState<HomeView> {
#override
Widget build(BuildContext context) {
AsyncValue<bool?> rejectResponse = ref.watch(notificationRepositoryProvider);
return ElevatedButton(
onPressed: () {
// ref.refresh(notificationRepositoryProvider);
ref.watch(notificationRepositoryProvider).;
},
child: rejectResponse.when(
loading: () => const CircularProgressIndicator(
color: Colors.white,
),
skipLoadingOnRefresh: false,
error: (err, stack) => Text('Error'),
data: (data) => Text('Yes: $data'),
));
}
}
I have 3 screens, login, homepage and chat. In chat I want to use user, but for some reasons I am getting an error.
Error in HomePage class while trying to pass user to the Chat: 'Undefined name 'user''.
Here samples of code:
class Login extends StatefulWidget {
#override
_LoginState createState() => _LoginState();
}
class _LoginState extends State<Login> {
String email;
String password;
final FirebaseAuth _auth = FirebaseAuth.instance;
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
Future<String> login(String _email, String _password) async {
FirebaseUser user = (await _firebaseAuth.signInWithEmailAndPassword(
email: _email, password: _password))
.user;
() =>
Navigator.push(context, MaterialPageRoute(builder: (context) => Bar(user: user)));
}
...
Why is it not defined? I want to pass user from Login(), through Homepage() to the Chat().
Thank you in advance
EDIT:
I noticed it is not defined only in List, but besides it works properly.
How can I pass it to the list?
class HomePage extends StatefulWidget {
final FirebaseUser user;
const HomePage({Key key, this.user}) : super(key:key);
#override
_BarState createState() => _BarState();
}
class _BarState extends State<HomePage> {
void test() {
() =>
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) =>
Chat(user: widget.user))); < ---- WORKING
}
int tabIndex = 0;
List<Widget> widgetOptions = [
Match(),
Chat(user: widget.user), < --- NOT WORKING
AddBook(),
Profile(),
];
Here you want to access user object in other screen, instead of passing it in cconstructor, create a singleton class, Like this
class AuthUser {
static var authUserId;
static var email;
static var userName;
static var role;
static var companyId;
}
During login Set values like this, please check here print(user); and set value respectively
Future<String> login(String _email, String _password) async {
FirebaseUser user = (await _firebaseAuth.signInWithEmailAndPassword(
email: _email, password: _password))
.user;
() =>
AuthUser.email = user.email,
AuthUser.email = user.userName,
..........
));
}
Access data in other screens like, AuthUser.email, AuthUser.userName.....
Use Chat(user: widget.user) instead.
when you are using a stateful widgets and what to get properties from constructor you need to do it like this:
widget.varNameInConstructor
most of the time it's not needed but if you want to assign that to you own new variable in state you can do it at initState.