Replace dismissed widget with new widget in Flutter - dart

I am getting an error with my current implementation of my Dismissable widget. It returns the 'Make sure to implement the onDismissed handler and to immediately remove the Dismissable widget' error. The error makes sense to me, but I am trying to reset the state of the widget. I can't figure out how to set the onDismissed property return a new card. I am using the Dismissable widget on a card. And I would like to be able to dismiss the card and automatically replace it with a new one. Is the dismissable widget even the appropriate route I should be taking? Thanks.

If you were to include your code (paired down to just what's necessary), it would most likely be much easier to see what's going on. In future questions, please include an example as it'll help us help you =).
That being said, it seems you might have a little bit of confusion about how to use the Dismissable widget. Basically, it will show its child and will handle the dismissal gesture, but you are responsible from removing it from the list (assuming this is in a list). From the Dismissable documentation:
If the Dismissible is a list item, it must have a key that
distinguishes it from the other items and its onDismissed callback
must remove the item from the list.
What that is saying is that if the Dismissable is in a list, you must externally manage the state of the list and the items within. I wouldn't be surprised if this is also true when the Dismissable isn't in a list.
Basically, you should be doing something in that callback to remove the Dismissable and its' child from your list. The most likely way for that to happen is that the Dismissable and the list it is contained in are built in a class inheriting State.
Say for example you have a list of items stored as a member variable of the State: 1,2,3,4,5,6,7,8,9,10. If you dismiss 8, you should then be building from the list 1,2,3,4,5,6,7,9,10. If you simply remove the 8th item from the list it won't quite work as dart isn't smart enough to compare list items - it will still see the list as being the same list. So you could either make a copy of the list without the 8 and set the member to that, or use a second variable (something like count) that gets incremented whenever there is a change so that flutter knows to rebuild the State.
Now, what you're doing is slightly more complicated than even that - you want to replace the item with something else. What I would then recommend doing is changing (or copying) the list form 1,2,3,4,5,6,7,8,9,10 -> 1,2,3,4,5,6,7,8*,9,10 within the OnDismissed callback, and calling setState appropriately, with the 8* being some sort of object which indicates that the list item has been dismissed.
When you build your list item, it should take into account the fact that 8 has been changed to 8* and build the new widgets appropriately.

Related

Angular Dart Router: navigating to the same component does not create a new instance even if canReuse() returns false

I'm using dart 2.0.0
With dart 1.24.3 and related angular router, I had a component with CanReuse() always returning false. In this case, when I navigated again to the same component, a new instance was created.
On the first call, onActivate() was called, and, when called the second time, the CanReuse() was executed and finally the onActivate() method on a new instance was called.
Now, with the last version, when we navigate a second time to the same component, CanReuse() and onActivate() are not called, and the same instance of the component is used.
Is there any way to change this behavior?
Edit 1:
Consider like you have a menu and each menu item navigate to a component. If you press the same menu item the same component is called. In my case the component called can manage data from different servers, so I can have multiple dataset active (but only one shown, almost like a tab..) at the same time. I have a list of open datasets, where the ones "not showed" are listed and you can select them again. There is a mechanism that preserve data and reinitialize the newly created instance from the selected one.
Theoretically I could manage everything in one instance only, but, as the logic is quite complicated, I would like to avoid it, and, in any case, I'd need to know when the menu item component (new or initialized from an existing dataset) has been selected (with an onActivate() call, for example..).
At present this only works if I select different components, let's say, for example, component A, component B, and then again component A.
If I call component A and then again component A, the onActivate() method is not called and I'm not able to know when a different dataset is needed.

Nested data and BLoCs in Flutter

My Flutter app has the following general structure:
The initial screen shows a list of contact objects. The user can tap a contact, which brings up
a detail screen that lets the user see the whole contact info, and modify any details temporarily. The user can either
dismiss the screen without saving the changes, or
tap a save button, which updates the contact permanently, and upon completion leads back to the initial screen.
I'm using FireStore. The list is built with Stream<QuerySnapshots>, and when the user taps an item, the app's router parses the route name (e.g. /contacts/123), creates the respective DocumentReference and forwards it to the detail screen's initializer, which then uses DocumentReference.get to load the details, and DocumentReference.updateData to save changes. Works beautifully – but is only a proof of concept.
Now I would like to hide FireStore and the remaining business logic behind a BLoC. This leads to some questions:
Keeping business logic out of the UI, as far as I understand it, implies that I should stick to named routes, and have the detail screen somehow use the route details to retrieve the relevant contact data through the BLoC. Is that true, and what's the best solution for this?
How can I subscribe to nested data using the BLoC? I want the detail screen to observe data changes, so that I can warn the user if the data becomes stale. Using functions on BLoCs (something like streamOfContact(contact) -> Stream<Contact>) is forbidden, so how would I do that with Sinks? Or is there generally a different way to do this without breaking the BLoC pattern? I'm very new to all of this, so may very well be I'm overlooking something important.
Similar question: How would I update a particular contact?
The tutorials I've found online only deal with root data (e.g. adding cart items to a cart, handling user authentication, ...), but I haven't seen an example showcasing nested data yet. Any help is highly appreciated!
1) Routing and navigation is in the responsibility of the UI layer. That means the UI layer must call Navigator.push[Named](...).
If it makes sense, you can move the logic that decides if the app should navigate to the detail screen:
// in the BLoC:
Stream<int> showContactDetail;
// in the UI layer:
bloc.showContactDetail.listen(_showContactDetail);
How you transfer the parameters to the detail route is totally up to you. You can use named routes, but it would be easier to transfer data with a builder:
void _showContactDetail(int contactId) {
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) {
return ContactDetailPage(
contactId: contactId,
);
}));
}
2) One BLoC should be tied to a single screen, that means there should be a separate BLoC for the detail screen (or dialog/sidebar/...), and you would pass the id of your contact to the second BLoC as a constructor parameter, or using a StreamSink, or using a simple setter method.
I would recommend you to use plain old methods instead of StreamSinks for the BLoC inputs. Here is a recent discussion about the topic.
3) The question is not only how to update your contact from the detail screen, but also how the detail BLoC obtains the contact data (if you are only transferring the contact id).
The answer: Another application layer, what I would call the data layer, that is shared between all BLoCs. You can store the data in Firebase, a sqlite database or a simple Map<int, Contact>.
The data layer would also propagate changes to the backend, and notify all BLoCs when the data has changed, probably using a Stream.
The next question that would come up is where you put this data layer (e.g. a class called ContactService):
You can create the ContactService in your ContactListPage, then pass it to the ContactDetailPage in a constructor parameter (using a route builder, as explained above). No magic here. A side effect that you may not want is that the service will be discarded when the list page is popped.
InheritedWidget that is above the MaterialApp, or at least above the Navigator generated by the MaterialApp (You can use the builder of MaterialApp to wrap the navigator with your own widgets). Putting it that high up in the tree ensures that all pages of your app can access it.
Using scoped_model, which is basically the same. Must also be inserted above the navigator to be accessible from both routes
A static variable/singleton

Selenium Webdriver - Refresh Stale Element Shortcut?

I'm writing a Selenium test for my Rails application, and sometimes run into the "Stale Element" exception. It's easy enough to perform another lookup on the element before operating on it, but to keep my code DRY, I'm wondering - is there any sort of method available on Selenium::WebDriver::Element objects to "refresh" stale references?
EDIT: To clarify, I'm asking if there's some shorthand method for doing another find_by_xpath (or id, etc.) call with the same parameter being passed. It's perfectly functional, just not as terse as some sort of "refresh" method would probably be.
If I understand correctly, you are asking about some sort of "refresh" or "update" method on a WebElement instance.
Unfortunately, there is no such thing in selenium by definition. According to the relevant issue WebElement refresh() or getBy() needed:
WebElement object is a proxy, a representative of a DOM element. When
DOM element is destroyed by the browser, WebElement is marked "stale"
and can't be used anymore.
If you have a similar DOM element on another page, you can obtain it
with findElement, and Selenium will create a new WebElement object
(a proxy) for this new DOM element. Your old WebElement object will
remain stale because underlaying DOM object is destroyed and never can
be restored. Similarity is not equality.
I would recommend store element-specific locators, but once the page refreshes, you would still need to "refind" the element by the locator.
The only thing available is keeping track of your application state when writing your Selenium code.
Did you perform an action that refreshed the DOM? Then you need to perform another findElement call to get a new instance of the element you want to interact with next.
Otherwise, write your own method findElementByXpath in a Utility class that tries to find an element, catches a StaleElement exception and retries.
I am going to describe my case which is similar:
I tried to find a table cell with some value by doing this:
Get all table rows
For each row - get a collection of the columns
For each column - seek the value
One day, I had a case with too many results in the table. So, I programmed the loop to click the "Get next page" link/icon.
Simple? no! when the program clicked the "next page of the table" link, the WebElement which pointed to the table was not usable, I received the same error as the author of this question: StaleElementReferenceException
What I did is:
Get the XPATH of the table first, and then I changed the loop so if all the rows were scanned, and he value was not found and if the next icon is enabled:
Click the next page of the table.
Update the WebElement to re-point the table.
I hope I am clear.

Dart observable list that does not delete and reinsert all elements on change

We have some code that has a custom web component to display each element in an observable list. It seems that when an element is added to the list, the Web-UI infrastructure sees fit to remove all of the DOM nodes that represent elements in the list, and then re-add them along with the new element.
Not knowing much about the internal workings of the Dart-UI code, I would actually expect the addition of an element in the observable list to simply add one (or more) nodes to the DOM, without removing any of the existing nodes from the DOM.
Is it possible to achieve this sort of behavior?
There is an open issue for this: https://github.com/dart-lang/web-ui/issues/431. John wrote
fwiw, I'm working on a data-binding overhaul right now. The new
implementation includes stable lists by default.
so this should be fixed eventually.

jQuery Sortable todo List

I'm trying to build a todo list. http://d2burke.com/exp/todo/
I want my user to be able to mark items as complete, at which point the item is dropped down to the 'Complete' list, and vice versa. I also want each of these lists to be independently sortable.
I'm using a basic custom method to move the items back and forth, and I've used jQueryUI Sortable to sort them.
All of these things I've been able to accomplish; however, it doesn't seem as though the site (the DOM?) is registering that the items were moved from one list to the other. If I mark one item as 'Complete' it physically moves down...and becomes sortable in the 'Complete' list, but I'm echoing out the current positions in serialized format (because I'm going to record the order in a db) and the app doesn't seem to recognize that the item has moved.
I'd like to force the app to recalculate the list of items when an item is marked complete, or incomplete.
Help?
I looks to me like you should look at the resfresh methods of of the sortable: http://jqueryui.com/demos/sortable/
I would start with .refresh() too see if that also recalculates the positions. I assume it does but if not you might need to trigger the other one too.
There are few things that stand out that I would comment on:-
1) When you 'move' an item, you are moving more than you intend - the moved item has a 'tbody' around it - your code is very fragile, depending on parent.parent... which will be a nightmare to maintain.
2) You have various parent.parent constructs - lose these switch to '.closest()' to search up the tree to the class of item you wan to operate on.
3) You are doing things with binding/unbinding - lose them and switch to '$(document.body).on(,...)' then jquery will add/remove events automatically based on your selector - link once and forget.
Once this is done the bugs may go away - if not it will still be clearer so possible to debug properly!
Regards
ps. If it were me, I think I'd link the two list so they are sortable together, and toggle the completed flag if an item is moved from one list to the other.
This way the user has two ways of changing the status and jquery will do all the donkey work as you are simply sorting a normal linked list.

Resources