Flutter Cubit After state change my variable resets - android

On my screen, it has a list and i can choose some elements. When i choose i want to save it to a variable and can use it later in another screen. So i using cubit in here.I using like that :
My UI:
BlocProvider(
create: (context) => TherapyCubit(),
child: Wrap(
children: [
for (final therapy in therapyList)
EllipsisCard(
therapy: therapy,
)
],
),
)
EllipsisCard:
InkWell(
onTap: () {
setState(() {
isSelected = !isSelected;
});
if (isSelected) {
context.read<TherapyCubit>().addTherapy(widget.therapy);
} else {
context.read<TherapyCubit>().deleteTherapy(widget.therapy);
}
},
child: Container(..
Cubit:
class TherapyCubit extends Cubit<TherapyState> {
TherapyCubit() : super(TherapyInitial());
List<Therapy> selectedTherapies = [];
void addTherapy(Therapy therapy) {
selectedTherapies.add(therapy);
inspect(selectedTherapies);
}
void deleteTherapy(Therapy therapy) {
selectedTherapies.remove(therapy);
}
}
Cubit State:
abstract class TherapyState extends Equatable {
const TherapyState();
#override
List<Object> get props => [];
}
class TherapyInitial extends TherapyState {}
On my page i have a state named like "step". That thing has 2 step. When my step is 1 i showing first page and when my step is 2 i showing second page with Visibility.
But when i press back button i setting step state to 1 again so showing first page again but now i cant see any of my choosen elements. And on cubit my list (selectedTherapies) is being empty again. Why its being like that ? How can i solve it ?

There are two options:
Either the cubit is replaced (try adding a print inside your Cubit constructor to diagnose)
Or your UI isn't showing the selectedTherapies List properly
Where did you placed your BlocProvider inside the widget tree?

Related

change Image assets depend on state changed with cubit (flutter)

I have 2 ImageAssets in my flutter project I want to switch between them depending on action,
I am using cubit and states.
the image changes after going to another widget and then coming back
how can I refresh the widget or do this without a stateful widget
this is my image and I want to switch between the grey one and basic one
indicator:Image.asset(cubit.planeState ? ImageAssets.plane : ImageAssets.planeGrey )),
and this the OnButtonPressed
btnOkOnPress: () {
context.read<RehlatechCubit>().onPlaneStateChanged();
},
Assuming that you have set up the page with cubit.
Modify the state (not the cubit) from a method
Emit the new state
Ensure the widget is wrapped in a BlocBuilder/BlocSelector/BlocConsumer
Psuedo Code
// rehlatech_cubit.dart
onPlaneStateChanged() {
...
/// Mutate the current state with the plane state
/// Make a copy or create a new state all together
emit(state.copyWith(planeState: true));
}
Ensure that your widget to be updated is wrapped in a BlocBuilder
// widget.dart
Scaffold(
body: BlocBuilder< RehlatechCubit, RehlatechState>(
builder: (context, state) {
// The state that is to be changed
return Content(
...
child: WidgetToBeUpdated(
indicator:Image.asset(state.planeState ?
ImageAssets.plane : ImageAssets.planeGrey,
),
),
)
)
}
)
);

flutter) The status of the icon can't be changed immediately (Edit)

I made it possible to toggle the heart icon in the post like Instagram.
And I implemented it so that user information is uploaded to the firestore when I toggle the icon.
There is no problem with the 'Like' and 'Unlike' function of 'Like Icon', but there is one other problem.
There is a problem that the color of the icon does not change immediately when toggling the icon in the post. I can check that this icon has changed when I go to a different screen and come back in.
The following is the code for this icon. (I edited this code)
class LikeToggleIcon extends StatefulWidget {
final String postKey;
final PostModel postModel;
const LikeToggleIcon(
{Key key,
this.postKey,
this.postModel,
this.fromSearch,
this.searchResults})
: super(key: key);
#override
State<LikeToggleIcon> createState() => _LikeToggleIconState();
}
class _LikeToggleIconState extends State<LikeToggleIcon> {
// bool _isLiked = false;
#override
Widget build(BuildContext context) {
//get userModel
UserModel userModel =
Provider.of<UserModelState>(context, listen: false).userModel;
return IconButton(
onPressed: () {
// setState(() {
postNetworkRepo.toggleLike(
widget.postModel.postKey, userModel.userKey);
// });
},
icon: Icon(
widget.postModel.numOfLikes.contains(userModel.userKey)
? Icons.favorite_outlined
: Icons.favorite_border_outlined,
size: 27,
color: Colors.redAccent,
),
);
//toggle method
class PostNetworkRepo with Transformers {
Future<void> toggleLike(String postKey, String userKey) async {
final DocumentReference postRef =
Firestore.instance.collection(COLLECTION_POSTS).document(postKey);
final DocumentSnapshot postSnapshot = await postRef.get();
//check Post collection
if (postSnapshot.exists) {
//check already contain userKey
//if no contain upload userKey, else delete userKey (toggle Like/Unlike)
if (postSnapshot.data[KEY_NUMOFLIKES].contains(userKey)) {
postRef.updateData({
KEY_NUMOFLIKES: FieldValue.arrayRemove([userKey])
});
} else {
postRef.updateData({
KEY_NUMOFLIKES: FieldValue.arrayUnion([userKey])
});
}
}
}
}
PostNetworkRepo postNetworkRepo = PostNetworkRepo();
//This is the part of detail post screen.
class DetailScreen extends StatefulWidget {
#override
_DetailScreenState createState() => _DetailScreenState();
}
class _DetailScreenState extends State<DetailScreen> {
#override
Widget build(BuildContext context) {
//get userModel
UserModel userModel =
Provider.of<UserModelState>(context, listen: false).userModel;
return Scaffold(
appBar: AppBar(
title: _postUser(),
actions: [
//toggle likeIcon
LikeToggleIcon(
postKey: widget.postKey,
postModel: widget.postModel,
),
],
),
I tried using setState() on the IconButton(), but the problem was not solved.
can I get help with this problem?
I believe you are using setState() to change value of variable that's inside the Widget and NOT the State of that widget.
You need to have variables that changes the Widget inside the state for it to be updated. Keeping it inside Widget won't change the Icon, unless the whole widget is recreated (like when you change the screen).
(correct me if I'm wrong. Providing full code of the widget could be more useful)

How can I update the state (refresh) of a single list tile?

I have a setting page displaying a list view and each list tile displays the name of a user and a percentage which can be edited. By tapping on a list tile the user can change the percentage using a dialog.
I would like to update the state of a single list tile when the user saves the new value instead of building the entire page again (should be better for perfomance too). Is this possible?
I have already tried to export the list tile as a stateful widget (it must be stateful) and using it in my setting page but the setState() method does not work on the single tile at all. I don't know if it would somehow work with a notifier.
Here is how the page looks like (showing the dialog to set the new percentage):
enter image description here
Thanks in advance!
Edit: I added some code.
setting_page.dart
// this widget is called in a ListView.builder to bild the list tile.
// before creating the UserListTile widget I used to return the ListTile in
// _buildRow and call setState() but then everything is re-built again, which is what I don't want to
Widget _buildRow(User user) {
return UserListTile(
user: user, userDAO: _userDAO, myPercentage: _myPercentage);
}
user_list_tile.dart
import 'package:flutter/material.dart';
import 'package:myapp/classes/user.dart';
import 'package:myapp/database/user_dao.dart';
import 'package:numberpicker/numberpicker.dart';
class UserListTile extends StatefulWidget {
const UserListTile(
{Key? key,
required this.user,
required this.userDAO,
required this.myPercentage})
: super(key: key);
final User user;
final UserDAO userDAO;
final ValueNotifier<double> myPercentage;
#override
_UserListTileState createState() => _UserListTileState();
}
class _UserListTileState extends State<UserListTile> {
final TextStyle _biggerFont = const TextStyle(fontSize: 18);
double _currentDoubleValue = 10.0;
double? trailing;
#override
Widget build(BuildContext context) {
trailing = widget.user.percentage;
return ListTile(
title: Text(
widget.user.name,
style: _biggerFont,
),
trailing: Text('${trailing} %'),
onTap: () => _showDoubleDialog(widget.user).then((value) {
if (value != false && value != null) {
// some code to set the new percentage
User _updatedUser = User(
name: widget.user.name,
id: widget.user.id,
percentage: _newPercentage);
widget.userDAO.updateData(_updatedUser);
setState(() {}); // <- this setState does not update the single tile
}
}),
);
}
Future _showDoubleDialog(User user) {
_currentDoubleValue = user.percentage;
return showDialog(
// code to shod the dialog to edit the percentage
);
}
}

Change data from modal sheet in flutter without changing the modal itself

I have 4 modal sheets in flutter, the order is modal-1 -> modal-2 -> modal-3 -> modal-4.
so, one modal goes down and other pops up ! is there any way in which I can stop this going down and poping up of another modal?
I'm using Bloc architecture on stateless widgets.
please help me to achieve any of the following, or suggest some thing else
1 - change data from instead of changing modal itself.
2 - stop modal going down and poping up animation.
I had a similar situation and used IndexedStack inside of the modal. So in essence, the actual content widgets (not the entire modal) from Sheet 1-4 become child widgets of the IndexedStack which actually is inside the modal.
import 'package:flutter/material.dart';
class ParentModalContent extends StatefulWidget {
ParentModal({Key? key}) : super(key: key);
#override
_ParentModalContentState createState() => _ParentModalContentState();
}
class _ParentModalContentState extends State<ParentModalContent> {
int currentIndex = 0;
#override
Widget build(BuildContext context) {
return IndexedStack(
children: [
Child1(
onNext: () {
setState(() {
currentIndex = currentIndex + 1;
});
},
),
// more widgets
],
);
}
}
class Child1 extends StatelessWidget {
final Function onNext;
const Child1({Key? key, required this.onNext}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
// content
);
}
}
Wrap the ParentModalContent inside of the function that shows your modal.

Display full screen loading widget in flutter

I want to show full-screen loading view in flutter while API calling. But when I add loading widget in scaffold body it appears after appbar and bottom navigator.
I am spending lots of time for display loading view in full-screen. Also, I want to prevent back action while API calling.
Well, since you're using Scaffold, then make use of its showDialog() method.
It has a property called barrierDismissible that if you set as false, the user won't be able to close or interact with the screen outside of it.
void _openLoadingDialog(BuildContext context) {
showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: CircularProgressIndicator(),
);
},
);
}
Once you're done with the API loading, call Navigator.pop(context); to dismiss the dialog.
To prevent the user from clicking the back button on the Dialog, dismissing it, envelop your Scaffold inside a WillPopScope widget and implement the onWillPop function.
#override
Widget build(BuildContext context) {
return WillPopScope(
child: Scaffold(
body: Container(),
),
onWillPop: _onBackButton
);
}
Future<bool> _onBackButton() {
// Implement your logic
return Future.value(false);
}
If you return false on it, the user won't be able to press the back button. So use any logic you desire, e.g 'If I'm loading return false, otherwise return true'.
Full-screen loader: Best solution to display a full screen loader is to use package
https://pub.dev/packages/screen_loader.
Prevent back action while loading: This package this package provides a variable isLoading, use it to prevent navigating back. eg:
// --------------- some_screen.dart ---------------
import 'package:flutter/material.dart';
import 'package:screen_loader/screen_loader.dart';
class SomeScreen extends StatefulWidget {
#override
_SomeScreenState createState() => _SomeScreenState();
}
class _SomeScreenState extends State<SomeScreen> with ScreenLoader<SomeScreen> {
getData() {
this.performFuture(NetworkService.callApi);
}
#override
Widget screen(BuildContext context) {
return WillPopScope(
child: Scaffold(
// your beautiful design..
),
onWillPop: () async {
return !isLoading;
},
);
}
}
// --------------- app_api.dart ---------------
class NetworkService {
static Future callApi() async {
}
}
NOTE: You'll need to see the definition of performFuture to see how it works for different scenarios.
You can use this dialog for full screen loading progress_dialog
Did you not think of removing the Scaffold for the loading screen?

Categories

Resources