Edit object in edit screen and sync updated object in first screen (list of object screen) - flutter

I have a list of class user showing with StateNotifier in one screen. Clicking on any item, opens the edit screen of that user.
I have 2 questions to ask:
Should I send user object In another screen and edit the user or create state provider (without auto dispose) of single object and set it before opening edit screen and access it on another screen.
After edit the user, how to sync updated user object in user list screen (first screen).
Please feel free to ask any further detail.
Thank you in advance.

Should I send user object In another screen and edit the user or
create state provider (without auto dispose) of single object and set
it before opening edit screen and access it on another screen.
No, just send user object to edit screen, then after you edit done, pass data back to list screen by Navigator.pop(context, editedUser);
After edit the user, how to sync updated user object in user list
screen (first screen).
On list screen :
Navigator.pushNamed(context, 'yourEditScreen', arguments: editUser).then((value) {
if (value != null) {
//update your state list;
}
});

I won't cover how you send data between screens because it depends on what state manager you are using and there are actually many ways.
Assuming that you are on screen 1 after finishing screen 2, you have a list of old objects and a new object that has been modified. now you'll want to inject the changes into the old list object and call the update state
Example:
var objects = [{...}];
return ListView(
children: objects.map((object) {
final index = objects.indexOf(object);
onPressed() {
// handle will call when screen2 poped
handleEditedData(editedObject) {
// check object was edited and differences with old object (can override operator ==)
if (editedObject != null && editedObject != object) {
setState(() {
// replace old object by editedObject with the index
objects[index] = editedObject;
});
}
}
// push screen2 and wait to data from screen2 (using .pop with editedObj as parameter)
Navigator.of(context)
.pushNamed('screen2route', arguments: object)
.then(handleEditedData);
}
return ElevatedButton(onPressed: onPressed, child: const Text('edit'));
}).toList(),
);

Related

Flutter, how to update reactive list declared in first screen controller from the second screen?

I am using GetX in my project. Lets suppose I have two screens. Each screen have a button.
The controller of first screen have observable list of objects. When a button in first screen is pressed, one of the object list is sent to second screen.
In second screen user changes the value of the data received and when save button is pressed, I want to update the observable list in first screen as well.
I know I can use Get.back() to send updated data back to first screen. I could do following in second screen
Get.back(result: [
{"updatedObject": listDetails}
]);
And in first screen I could do
Get.to(() => SecondScreen(), arguments: [
{"list": listDetails[index]}
]).then((result) {
// result[0]["updatedObject"] // use this to update list observable
});
My Question.
Can I update list in first screen without navigating back to first screen when Save button is pressed.
I am pretty new with GetX and i am trying to learn it. Thanks
As long that when you navigate to the second screen using Get.to() then you can always access the data in the controller of the first screen using Get.find().
Here's a sample I tweaked it a little bit:
Get.to(() => SecondScreen(), arguments:
{"index": index},
)
On the controller on the second screen you can access(view/update) the data using the index.
#override
void onInit() {
index = Get.arguments['index'];
// View
name = Get.find<FirstScreenController>().listDetails[index].name;
print(name);
// Update
Get.find<FirstScreenController>().listDetails[index].name = "John";
}

pop and push the same route back with different params in Futter (GET X)

I have 2 screens,
Screen one contains a list view with onPressed action on every item
screen two contains the detail of the pressed item as well as a drawer with the same list view as screen one.
What I want to do here is when the user goes to the detail screen and click on an item from the drawer the detail screen should pop and push back with new params.
Code so far,
Route
GetPage(
name: '/market-detail',
page: () => MarketDetail(),
binding: MarketDetailBinding(),
),
Binding
class MarketDetailBinding extends Bindings {
#override
void dependencies() {
Get.lazyPut(() => MarketDetailController());
}
}
Click action in screen one
onTap: () {
Get.toNamed('market-detail',
arguments: {'market': market});
},
Detail Screen Class
class MarketDetail extends GetView<MarketDetailController> {
final Market market = Get.arguments['market'];
}
Click action in detail screen sidebar
onTap: () {
Get.back();
Get.back();
Get.toNamed('market-detail',
arguments: {'market': market});
},
First Get.back() is to close the drawer, then remove the route and push the same route back again,
Expected behaviour,
MarketDetailController should be deleted from memory and placed again,
What actually happening
The controller only got delete and not getting back in memoery on drawer click action until I hot restart the app(By clicking save).
If anybody understands it, please help I am stuck here.
As I can see, you're trying to pop and push the same route with a different parameter in order to update a certain element on that route. Well, if that's the case then just let me show you a much better way.
In your MarketDetailController class you should add those:
class MarketDetailsController extends GetxController {
// A reactive variable that stores the
// instance of the market you're currently
// showing the details of.....
Rx<Market> currentMarket;
// this method will be called once a new instance
// of this controller gets created
// we will use it to initialize the controller
// with the required values
#override
void onInit() {
// some code here....
// .......
// intializing the variable with the default value
currentMarket = Market().obs;
super.onInit();
}
void updateCurrentMarket(Market market) {
// some code here if you need....
// updating the reative variable value
// this will get detected then by the Obx widgets
// and they will rebuild whatever depends on this variable
currentMarket.value = market;
}
}
Now inside your page UI, you can wrap the widget that will display the market details with the Obx widget like this:
Obx(
() {
final Market currentMarket = controller.currentMarket.value;
// now you have the market details you need
// use it and return your widget
return MyAwesomeMarketDetailsWidget();
},
)
Now for your click action, it can just be like this:
onTap: () => controller.updateCurrentMarket(myNewMarketValue)
This should be it. Also, I advise you to change GetView to GetWidget and Get.lazyPut() to Get.put()

How to launch a another screen with different functions based on the user onTap

I am currently working on a Math app that has a category screen which has Cards for different math operations(plus, Minus, Division). when user taps on it displays the next screen with questions and answers. I had create a separate class for question and answer generation and inside it I had created methods for each math operation like below
class QandAgeneration{
//Perform the addition quiz generation
Map addition(){}
//Perform the subtraction quiz generation
Map subtraction(){}
//Perform the division quiz generation
Map division(){}
// Generate answers
List generateAnswers(){}
}
In the next screen I want to display the output of the user selected operation eg:- QandAgeneration.addition() as widgets and those widgets state changed to display the next question when user press a button to select the answer. I think I can do it by using if else by passing the String value from category screen to next screen by using constructor with string parameter. Then I can do it as below
if(widget.selectedType == 'addition'){
QandAgeneration.addition()
}
else if(widget.selectedType == 'subtraction'){
QandAgeneration.subtraction()
}else{
QandAgeneration.division()
}
Is this is the best way to achieve this function. Is there any other method?
Create a class for another screen and pass to this screen needed parameters. On another screen implement functionality based on passed data. For example:
class AnotherScreen extends StatelessWidget {
final data;
AnotherScreen(this.data);
#override
void build(BuildContext context) {
if (data == 'firstType') {
return widgetForDataWithFirstType(data);
}
return widgetForDatawithSecondType(data);
}
}
And move to this screen:
Navigator.push(
context,
MaterialPageRoute(builder: (context) => AnotherScreen(data)),
);
Where data - parameter that you need to receive to another screen.
You can read more about how to work with navigation in Official Documentation.

How to modify current bloc state like adding, updating, and deleting?

I have 2 screens, the first screen is to display a list of items. The second one is a form for creating a new item. On the server side, after each store, update, destroy action, I returns its value back to the client as a response. The goal here is to update the state without having to make a new request over the network and showing a loading screen over and over every time the user creating a new resource and going back to the screen containing the list.
But since the BlocBuilder is only depends on a specific event and state.data is only available inside an if (event is NameOfEvent) statement, I couldn't modify the current state to push the new value to it.
I found that using BlocListener combined with InitState and setState is somehow working, but I think it makes the state.data is useless inside the BlocBuilder also making the algorithm complicated for such a small task.
Is there any simpler way to achieve this condition?
child: BlocBuilder<EducationBloc, EducationState>(
builder: (context, state) {
if (state is EducationLoaded) {
return ListEducationData(educations: state.data);
}
if (state is EducationCreated) {
final education = state.data;
// i want to push [education] to <ListEducationData>
}
...
...
Create list to store data and use BlocListener so you don't need to change ui every state changes.
var _listEducations = List<EducationData>();
child : BlocListener<EducationBloc,EducationState>(
listener: (_,state){
if (state is EducationLoaded) {
setState(() { //update _listEducations
_listEducations.addAll(state.data);
});
}
},
child : Container()
)

How to disable click events for entire children except one selected child in flutter?

Scenario: I have number of children in Listview.builder. For every children there is button which invokes TTS(text to speech). I wanted when any one of child is pressed rest all children should be in Listview.builder un-clickable till pressed child finishes its TTS.
I got answer from google like absorb pointer, ignore pointer to solve this.
But i dont know how to implement above scenario using these widgets.
Store a bool in the State of the class enclosing the ListView.builder. This bool should store if one of the children is currently doing its TTS. If it is true you should set all of the onPressed(or equivalent) method to null to prevent other taps from triggering an action. Ex:
bool hasBeenClicked = false;
void yourTTSMethod() {
setState(() {
hasBeenClicked = true;
});
... //Do normal method body
setState(() {
hasBeenClicked = true;
});
}
//In build method with each `List` item:
GestureDetector(//Just for sample, use whatever click detector you're currently using
onTap: hasBeenClicked ? null : yourTTSMethod
)