Created
October 18, 2024 12:21
-
-
Save adamsmaka/dd20d888ea98545d7a7c5a36dc7eddfe to your computer and use it in GitHub Desktop.
Signals + Computed + Reordering
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'package:signals/signals_flutter.dart'; | |
void main() { | |
runApp(const MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
const MyApp({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return const MaterialApp( | |
home: HomePage(), | |
); | |
} | |
} | |
class HomePage extends StatelessWidget { | |
const HomePage({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar(title: const Text('Reorderable List with Signals')), | |
body: const ReorderableItemList(), | |
); | |
} | |
} | |
class ReorderableItemList extends StatefulWidget { | |
const ReorderableItemList({Key? key}) : super(key: key); | |
@override | |
_ReorderableItemListState createState() => _ReorderableItemListState(); | |
} | |
class _ReorderableItemListState extends State<ReorderableItemList> { | |
final allItems = listSignal<ItemModel>([ | |
ItemModel(id: '1', title: 'Item 1', orderIndex: 0, categoryId: 'A'), | |
ItemModel(id: '2', title: 'Item 2', orderIndex: 1, categoryId: 'A'), | |
ItemModel(id: '3', title: 'Item 3', orderIndex: 2, categoryId: 'B'), | |
ItemModel(id: '4', title: 'Item 4', orderIndex: 3, categoryId: 'A'), | |
ItemModel(id: '5', title: 'Item 5', orderIndex: 4, categoryId: 'B'), | |
ItemModel(id: '6', title: 'Item 6', orderIndex: 5, categoryId: 'A'), | |
ItemModel(id: '7', title: 'Item 7', orderIndex: 6, categoryId: 'A'), | |
ItemModel(id: '8', title: 'Item 8', orderIndex: 7, categoryId: 'B'), | |
ItemModel(id: '9', title: 'Item 9', orderIndex: 8, categoryId: 'A'), | |
ItemModel(id: '10', title: 'Item 10', orderIndex: 9, categoryId: 'A'), | |
ItemModel(id: '11', title: 'Item 11', orderIndex: 10, categoryId: 'A'), | |
ItemModel(id: '12', title: 'Item 12', orderIndex: 11, categoryId: 'A'), | |
ItemModel(id: '13', title: 'Item 13', orderIndex: 12, categoryId: 'A'), | |
ItemModel(id: '14', title: 'Item 14', orderIndex: 13, categoryId: 'A'), | |
ItemModel(id: '15', title: 'Item 15', orderIndex: 14, categoryId: 'A'), | |
ItemModel(id: '16', title: 'Item 16', orderIndex: 15, categoryId: 'A'), | |
ItemModel(id: '17', title: 'Item 17', orderIndex: 16, categoryId: 'A'), | |
ItemModel(id: '18', title: 'Item 18', orderIndex: 17, categoryId: 'A'), | |
ItemModel(id: '19', title: 'Item 19', orderIndex: 18, categoryId: 'A'), | |
ItemModel(id: '20', title: 'Item 20', orderIndex: 19, categoryId: 'A'), | |
]); | |
final selectedCategoryId = signal<String>('A'); | |
late final filteredAndSortedItems = computed(() { | |
final filtered = allItems.value | |
.where((item) => item.categoryId == selectedCategoryId.value) | |
.toList(); | |
filtered.sort((a, b) { | |
int orderComparison = a.orderIndex.compareTo(b.orderIndex); | |
if (orderComparison != 0) return orderComparison; | |
// Newer items first if order index is the same | |
return b.createdAt.compareTo(a.createdAt); | |
}); | |
return filtered; | |
}); | |
void reorderItems(int oldIndex, int newIndex) { | |
if (oldIndex < newIndex) { | |
newIndex -= 1; | |
} | |
final items = [...filteredAndSortedItems.value]; | |
// final items = filteredAndSortedItems.value; // modifying original List works fine | |
final ItemModel item = items.removeAt(oldIndex); | |
items.insert(newIndex, item); | |
// Update orderIndex for all items | |
for (int i = 0; i < items.length; i++) { | |
final updatedItem = items[i].copyWith(orderIndex: i); | |
final allItemsIndex = | |
allItems.value.indexWhere((element) => element.id == updatedItem.id); | |
if (allItemsIndex != -1) { | |
allItems.value[allItemsIndex] = updatedItem; | |
} | |
} | |
// Trigger update | |
allItems.value = List.from(allItems.value); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Column( | |
children: [ | |
ElevatedButton( | |
onPressed: () { | |
selectedCategoryId.value = | |
selectedCategoryId.value == 'A' ? 'B' : 'A'; | |
}, | |
child: const Text('Switch Category'), | |
), | |
Expanded( | |
child: Watch((context) { | |
final items = filteredAndSortedItems.value; | |
return ReorderableListView.builder( | |
itemCount: items.length, | |
itemBuilder: (context, index) { | |
final item = items[index]; | |
return ListTile( | |
key: ValueKey(item.id), | |
title: Text(item.title), | |
subtitle: Text( | |
'Order: ${item.orderIndex}, Category: ${item.categoryId}'), | |
); | |
}, | |
onReorder: reorderItems, | |
); | |
}), | |
), | |
], | |
); | |
} | |
} | |
class ItemModel { | |
final String id; | |
final String title; | |
final int orderIndex; | |
final String categoryId; | |
final DateTime createdAt; | |
ItemModel({ | |
required this.id, | |
required this.title, | |
required this.orderIndex, | |
required this.categoryId, | |
DateTime? createdAt, | |
}) : this.createdAt = createdAt ?? DateTime.now(); | |
ItemModel copyWith( | |
{String? id, String? title, int? orderIndex, String? categoryId}) { | |
return ItemModel( | |
id: id ?? this.id, | |
title: title ?? this.title, | |
orderIndex: orderIndex ?? this.orderIndex, | |
categoryId: categoryId ?? this.categoryId, | |
createdAt: this.createdAt, | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment