I'm a beginner with Flutter and I'm currently implementing a local Save of Items using Hive and Boxes.
Everything was okay until I decided to reorder the list of Items.
My question is: How can I save the reordered changes, knowing that I can't use the traditional InsertAt(index) and RemoveAt(index) with Hive.
Box<Item> itemsBox;
List<Item> items; //Item class extends HiveObject
void addItem(Item item) {
setState(() {
items.add(item);
itemsBox.add(item);
});
}
void removeItem(Item item) {
setState(() {
items.remove(item);
item.delete();
});
}
void _onReorder(int oldIndex, int newIndex) {
setState(() {
Item row = items.removeAt(oldIndex);
items.insert(newIndex, row);
int lowestInt = (oldIndex < newIndex) ? oldIndex : newIndex;
int highestInt = (oldIndex > newIndex) ? oldIndex : newIndex;
// What Can I Do with my box to save my List<Item> items
// Box is a Box<Item>
});
}
I was looking for the answer too, it has turned out pretty easy.
void _onReorder(int oldIndex, int newIndex) {
if (oldIndex < newIndex) {
newIndex -= 1;
}
setState(() {
// this is required, before you modified your box;
final oldItem = itemsBox.getAt(oldIndex);
final newItem = itemsBox.getAt(newIndex);
// here you just swap this box item, oldIndex <> newIndex
itemsBox.putAt(oldIndex, newItem);
itemsBox.putAt(newIndex, oldItem);
});
}
Related
I got an issue when I tried to remove an item in a list. when I tried to remove a list it disappeared, but they show a new list. the case looks like this.
below I provided my code also
adapter.setOnItemClickedCallback(object : ListOrderAdapter.OnItemClickCallback {
override fun onDecrementButtonClicked(orderMenu: OrderMenu, itemView: View, position: Int) {
var quantityChanged = (orderMenu.quantity)!!.minus(1)
itemView.tv_order_number.text = quantityChanged.toString()
Log.e("Position", position.toString())
if (quantityChanged > 0) {
updateData(quantityChanged, orderMenu)
} else {
listItem.remove(orderMenu)
recyclerView.removeViewAt(position)
adapter.notifyItemRemoved(position)
adapter.notifyItemRangeRemoved(position, listItem.size)
Toast.makeText(applicationContext, "Pesanan telah di hapus dari orderan", Toast.LENGTH_SHORT).show()
}
}
})
}
private fun updateData(quantityChanged: Long, orderMenu: OrderMenu) {
for (item in listItem){
if (item.name.equals(orderMenu.name)){
item.quantity = quantityChanged
}
}
}
I am trying to filter items from an array and create a new one out of it. When a user taps on one of the tabs, the list sorts the products into its categories(ie: When the dinner tab is selected, all products labeled as category "Dinner" will show) from there organize into sub categories (Chicken, Beef, Oxtail). I am not sure where I am going wrong here. Here is what I have so far:
Tab Bar
TabBar(
onTap: _changedDropDownItem,
indicatorColor: buddiesGreen,
labelColor: buddiesPurple,
unselectedLabelColor: Colors.grey,
// isScrollable: true,
tabs: [
Tab(icon: Icon(FontAwesomeIcons.medkit), text: "Dinner"),
Tab(icon: Icon(FontAwesomeIcons.heart), text: "Desserts"),
Tab(icon: Icon(Icons.info), text: "Taste"),
Tab(
icon: Icon(FontAwesomeIcons.home),
text: "More",
),
],
),
Then I use these methods to get the categories from the tab bar:
void _tabBarItemsTapped(int newValue) {
setState(() {
_selectedCustomerType = newValue;
});
_tabBarFilterSwitch(newValue);
print(_selectedCustomerType);
}
void _tabBarFilterSwitch(filterNumb) {
if (filterNumb == 0) {
_dropdownList = _usage;
} else if (filterNumb == 1) {
_dropdownList = _productType;
} else if (filterNumb == 2) {
_dropdownList = _productType;
} else if (filterNumb == 3) {
_dropdownList = _productType;
}
print(_dropdownList);
}
Here is a data model I'm pulling down:
{
"resId":123,
“image”: “ 'assets/ChocolateCake.jpg',
“productType”: “Dinner”
“type”: “main”
“sizes” :
[
{“size”:1,
“price”:$2.99},
{“size”:3,
"quantity”: $4.99}
]
}
I am trying to tap the tab that would trigger a switch case. This switch chase will update the list:
void _filterProductList(int value, productList, filter) {
List tempProducts = [];
switch (value) {
case 0:
for (int i = 0; i < productList.length; i++) {
tempProducts.add(productList[i].category.contains(filter[i]));
// tempProducts = productList
// tempProducts.
productList =[];
productList = tempProducts;
print(tempProducts[i].name);
}
break;
case 1:
for (int i = 0; i < productList.length; i++) {
tempProducts.add(productList[i].category.contains(filter[i]));
// tempProducts = productList
// tempProducts.
productList =[];
productList = tempProducts;
print(tempProducts[i].name);
}
break;
case 2:
for (int i = 0; i < productList.length; i++) {
tempProducts.add(productList[i].category.contains(filter[i]));
// tempProducts = productList
// tempProducts.
productList =[];
productList = tempProducts;
print(tempProducts[i].name);
}
break;
case 3:
for (int i = 0; i < productList.length; i++) {
tempProducts.add(productList[i].category.contains(filter[i]));
// tempProducts = productList
// tempProducts.
productList =[];
productList = tempProducts;
print(tempProducts[i].name);
}
break;
}
return;
}
And presents like:
Container(
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: _prodType.length,
shrinkWrap: true,
itemBuilder: (context, ii) {
_filterProductList(filterNumb, productList, _dropdownList);
return Container(child: _buildProduct(context, ii));
},
)),
Instead of updating the list with every tap, it just seems like nothing is happening.
To refresh the hi you need to use setState.
productList = tempProducts should be:
setState(()=>productList = tempProducts);
Also, the widget should be a stateful widget.
I have a method to skip to the next list item, which works fine, but the method to display the previous list item doesn't seem to re-render.
When I print to console, the list item index it goes down by 1, but the text widget doesn't update like it does when it increases by 1.
I have shown the 2x methods below and an excerpt from the build. Help! :)
void _skipFlashcard () {
setState(() {
int currentIndex = allFlashcards.indexOf(widget.flashcardToShow);
var nextFlashcard = allFlashcards[currentIndex + 1];
widget.flashcardToShow = nextFlashcard;
print(widget.flashcardToShow.flashcardId);
});
}
void _previousFlashcard () {
int currentIndex = allFlashcards.indexOf(widget.flashcardToShow);
var previousFlashcard = allFlashcards[currentIndex - 1];
widget.flashcardToShow = previousFlashcard;
print(widget.flashcardToShow.flashcardId);
}
-------------------------
Container(
child: Row(
children: <Widget>[
Text(widget.flashcardToShow.flashcardId.toString()),
Wrap your code in setState, that's all that is missed :-)
void _previousFlashcard () {
setState() {
int currentIndex = allFlashcards.indexOf(widget.flashcardToShow);
var previousFlashcard = allFlashcards[currentIndex - 1];
widget.flashcardToShow = previousFlashcard;
print(widget.flashcardToShow.flashcardId);
}
}
I'm developing the spark List with Large Images in flex mobile.
Each ItemRenderer has a large image.
like this.
<s:ItemRenderer>
...
<s:BitmapImage source="{data.bmpData}" />
</s:ItemRenderer>
In dataProvider, there is BitmapData as name "bmpData".
The problem is performance while scrolling.
While scrolling, it stopped for a while when new Image is rendered.
help me please.
If the problem is you render too many bitmapdata in same time, you can render them one by one in different frames.
Here is an example.
Make a custom ItemRenderer
class YourItemRenderer
{
override public function set data(value:Object):void
{
if (super.data != value)
{
super.data = value;
yourBitmapImage.source = null;
//when the data change, don't call the render function directly
EnterFrameManager.getInstance().addRenderFunction(render)
}
}
private function render():void
{
if (yourBitmapImage != null && data != null)
{
yourBitmapImage.source = data.bmpData;
}
}
}
EnterFrameManager is used to control the render functions.
class EnterFrameManager
{
import mx.core.FlexGlobals;
public function EnterFrameManager()
{
FlexGlobals.topLevelApplication.addEventListener( Event.EnterFrame, onEnterFrameHandler)
}
private var _instance:EnterFrameManager;
public static function getInstance():EnterFrameManager
{
if (_instance == null)
{
_instance = new EnterFrameManager();
}
return instance;
}
//save the render functions
private var renderQueue:Array = [];
private var nowIntervalFrame:int = 0;
//change it to small value when you don't feel lag
private const UPDATE_INTERVAL_FRAMES:int = 6;
private function onEnterFrameHandler(e:Event):void
{
nowIntervalFrame++;
if (nowIntervalFrame >= UPDATE_INTERVAL_FRAMES)
{
nowIntervalFrame = 0;
//change renderQueue by waitQueue
for each (var f:Function in waitQueue)
{
addFunctionToQueue(f, renderQueue);
}
waitQueue.length = 0;
if (renderQueue.length > 0)
{
var f:Function = renderQueue.shift();
f();
}
}
}
private var waitQueue:Array = [];
public function addRenderFunction(f:Function):void
{
addFunctionToQueue(f, waitQueue);
}
private function addFunctionToQueue(f:Function, queue:Function):void
{
var index:int = queue.indexOf(f);
if (index == -1)
{
queue.push(f);
}
else
{
var temp:Function = queue.splice(index, 1);
queue.push(temp);
}
}
}
I have an Ember Array Controller that is binded to Ember select view that gets sorted by the user if they choose to. Once everything runs through my sort and I reset the array with the now sorted array the view doesn't change but if I loop through the array that I just set, it shows that it is sorted. So the view isn't updating with the array controller, I believe. I was looking at other posts with similar problems but none of the solutions from them worked for me.
dmp: Ember.ArrayController.create(),
tempArray: new Array(),
sort: function() {
debugger;
var self = this;
var textA, textB, i, t, pos, temp;
this.set('tempArray', self.dmp.get('content'));
var nxt;
for(t = 0; t < (self.get('tempArray').length) - 1; t++) {
nxt = this.get('tempArray')[t];
for(i = t; i < self.get('tempArray').length; i++) {
if(self.get('tempArray')[i].name.toUpperCase() <= nxt.name.toUpperCase()) {
nxt = self.get('tempArray')[i];
pos = i;
}
}
temp = self.get('tempArray')[t];
self.get('tempArray')[t] = nxt;
self.get('tempArray')[pos] = temp;
}
//App.defRouteSearch.dmpName.set('content', self.get('tempArray'));
//App.defRouteSearch.dmp.set('content', self.get('tempArray'));
self.setArray();
},
setArray: function() {
debugger;
var a = 0, b = 1;
var self = this;
while(a < this.get('tempArray').length) {
self.get('dmp').toArray[b] = self.get('tempArray')[a];
a++;
b++;
}
}
I switch everything over to a normal js array because it's quicker to manipulate data than compared to the Array Controller, I do this throughout the rest of my code when filling the 6 other arrays I have so that's not causing any problems. The commented code was what I was doing before to set the array.
Thanks for any help.
No need to do all of this. This should do the trick:
App.MyArrayController = Ember.ArrayController.create({
content: songs,
sortProperties: ['name'],
sortAscending: true
});
I was able to get it to work after a while. Here's my sort now
sortName: function() {
var self = this;
var i, t, pos, temp;
this.set('tempArray', new Array());
this.set('tempArray', self.dmp.get('content'));
var nxt;
for(t = 0; t < (self.get('tempArray').length) - 1; t++) {
nxt = this.get('tempArray')[t];
for(i = t; i < self.get('tempArray').length; i++) {
if(self.get('tempArray')[i].name.toUpperCase() <= nxt.name.toUpperCase()) {
nxt = self.get('tempArray')[i];
pos = i;
}
}
temp = self.get('tempArray')[t];
self.get('tempArray')[t] = nxt;
self.get('tempArray')[pos] = temp;
}
self.dmp.set('content', self.tempArray.clone());
},
Array.prototype.clone = function () {
var newObj = [];
for (i in this) {
if (this[i]) {
if ($.isPlainObject(this[i])) {
newObj[i] = $.extend(true, {}, this[i]);
}
else if ($.isArray(this[i])) {
this[i].clone();
}
else {
newObj[i] = this[i];
}
}
}
return newObj;
};
I'm not entirely sure why it works but it does. The small bit of reasoning I was able to come up with is that in js when you copy an array it's only referenced and not actually copied. But then I shouldn't need the clone() at the end since the referenced array was modified. Feel free to correct me if I'm wrong.