Closed isenbj closed 1 month ago
Hi @isenbj Can you share the code you are using and also the version of this package and the output of flutter doctor -v?
Flutter doctor:
[✓] Flutter (Channel stable, 3.22.2, on macOS 14.1.1 23B81 darwin-arm64, locale en-US)
• Flutter version 3.22.2 on channel stable at /Users/jermi/Development/flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 761747bfc5 (4 months ago), 2024-06-05 22:15:13 +0200
• Engine revision edd8546116
• Dart version 3.4.3
• DevTools version 2.34.3
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
• Android SDK at /Users/jermi/Library/Android/sdk
• Platform android-34, build-tools 34.0.0
• Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 17.0.10+0-17.0.10b1087.21-11572160)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 15.0)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 15A240d
• CocoaPods version 1.15.2
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2023.3)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 17.0.10+0-17.0.10b1087.21-11572160)
[✓] VS Code (version 1.93.1)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension can be installed from:
🔨 https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter
[✓] Connected device (5 available)
• Jerm iPhone (mobile) • 00008130-000855400A90013A • ios • iOS 17.7 21H16
• Laura’s iPad (mobile) • 00008112-000A28E62203401E • ios • iOS 17.7 21H16
• macOS (desktop) • macos • darwin-arm64 • macOS 14.1.1 23B81 darwin-arm64
• Mac Designed for iPad (desktop) • mac-designed-for-ipad • darwin • macOS 14.1.1 23B81 darwin-arm64
• Chrome (web) • chrome • web-javascript • Google Chrome 129.0.6668.70
! Error: Browsing on the local area network for Todd’s iPhone. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac.
The device must be opted into Developer Mode to connect wirelessly. (code -27)
! Error: Browsing on the local area network for Jeremy’s Apple Watch. Ensure the device is unlocked and discoverable via Bluetooth. (code -27)
! Error: Browsing on the local area network for Jeremy’s iPad. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac.
The device must be opted into Developer Mode to connect wirelessly. (code -27)
[✓] Network resources
• All expected network resources are available.
• No issues found!
The class is setup like this for each grid: The grid is updated by using a riverpod provider that stores the list of photos. When a photo is added to the list, the provider holding the list is updated forcing a rebuild.
class AttachmentPhotoView extends ConsumerStatefulWidget {
final PropertyArea area;
final ScrollController scrollController;
const AttachmentPhotoView(this.area, this.scrollController, {super.key});
@override
ConsumerState createState() => _AttachmentPhotoViewState();
}
class _AttachmentPhotoViewState extends ConsumerState<AttachmentPhotoView> {
late Future<PhotoSortOrder?> _photosSortOrderFuture;
List<String>? sortOrder;
bool _isInitialLoad = true;
final _gridViewKey = GlobalKey();
late ClaimPhoto _footerPhoto;
final int _staggerAnimationDuration = 250;
@override
void initState() {
super.initState();
_loadPhotos();
_footerPhoto = ClaimPhoto(
uuid: 'footer',
areaId: '',
structureId: '',
claimNumber: '',
userId: '',
photoOrders: PhotoOrder(location: DamageLocation(), order: 0),
photoTaker: '',
description: '',
personalProperty: PersonalProperty(),
formAssociations: {},
damageLocation: DamageLocationEnum.undefined,
location: DamageLocation(),
bytes: Uint8List(0),
);
}
void _loadPhotos() {
var areaId = widget.area.id;
var structureId = widget.area.structureId;
_photosSortOrderFuture = ref.read(photoSortOrderProvider).getSortOrderForArea(areaId, structureId);
}
@override
Widget build(BuildContext context) {
// Schedule a callback to set _isInitialLoad to false after this frame
if (_isInitialLoad) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
if (mounted) {
await Future.delayed(Duration(milliseconds: _staggerAnimationDuration));
setState(() => _isInitialLoad = false);
}
});
}
return ref.watch(areaPhotosListProvider(widget.area.id, widget.area.structureId)).when(
data: (claimPhotos) {
return Padding(
padding: const EdgeInsets.all(6.0),
child: ResponsiveBuilder(
builder: (context, sizingInfo) {
if (claimPhotos.isNullOrEmpty()) {
return Center(child: AddPhotoToGridButton(area: widget.area, isFooter: false));
}
claimPhotos = claimPhotos;
// print('claimPhotos: ${claimPhotos.map((e) => e.uuid).toList()}');
var delegate = SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: sizingInfo.isMobile ? 80 : 110,
crossAxisSpacing: 6,
mainAxisSpacing: 6,
);
int colCount = _getColumnCount(delegate, sizingInfo.localWidgetSize.width);
return AsyncBuilder<PhotoSortOrder?>(
future: _photosSortOrderFuture,
waiting: (_) => const Center(child: CircularProgressIndicator()),
builder: (ctx, order) {
sortOrder ??= order?.orderList.toList() ?? [];
_sortPhotos(claimPhotos);
var items = [...claimPhotos, _footerPhoto]
.map((p) => _buildGridItem(p, claimPhotos.indexOf(p), colCount))
.toList();
return AnimationLimiter(
child: ReorderableBuilder(
key: Key(_gridViewKey.toString()),
scrollController: widget.scrollController,
longPressDelay: const Duration(milliseconds: 120),
lockedIndices: [claimPhotos.length],
enableScrollingWhileDragging: true,
nonDraggableIndices: [claimPhotos.length],
releasedChildDuration: const Duration(milliseconds: 0),
positionDuration: const Duration(milliseconds: 0),
enableDraggable: true,
feedbackScaleFactor: 1.2,
builder: (children) {
return GridView(
// key: _gridViewKey,
padding: EdgeInsets.zero,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate: delegate,
children: children,
);
},
onReorder: (func) => _handlePhotoReorder(func, claimPhotos),
children: items,
),
);
},
);
},
),
);
},
error: (error, stackTrace) => const Center(child: Text('Error loading photos')),
loading: () => const Center(child: CircularProgressIndicator()),
);
}
void _sortPhotos(List<ClaimPhoto> claimPhotos) {
claimPhotos.sort((a, b) {
int indexA = sortOrder!.indexOf(a.uuid);
int indexB = sortOrder!.indexOf(b.uuid);
return (indexA == -1 ? sortOrder!.length : indexA) - (indexB == -1 ? sortOrder!.length : indexB);
});
}
void _handlePhotoReorder(ReorderedListFunction reorderedListFunction, List<ClaimPhoto> claimPhotos) {
setState(() {
claimPhotos = reorderedListFunction(claimPhotos) as List<ClaimPhoto>;
final newSortOrder = claimPhotos.map((p) => p.uuid).toList();
ref.read(photoSortOrderProvider).setOrderForArea(widget.area.id, widget.area.structureId, newSortOrder);
claimPhotos.add(_footerPhoto);
sortOrder = newSortOrder;
});
}
Widget _buildGridItem(ClaimPhoto photo, int index, int colCount) {
Widget item;
if (photo.uuid == 'footer') {
item = AddPhotoToGridButton(area: widget.area, isFooter: true);
} else {
item = GestureDetector(
onTap: () => debugPrint('navigate to carousel'),
child: CompactGridViewEntity(photo: photo, selected: false),
);
}
return CustomDraggable(
key: ValueKey(photo.uuid + photo.areaId),
data: photo,
child: AnimationConfiguration.staggeredGrid(
position: index,
duration: const Duration(milliseconds: 250),
columnCount: colCount,
child: _isInitialLoad ? FadeInAnimation(child: ScaleAnimation(child: item)) : item,
),
);
}
int _getColumnCount(SliverGridDelegate delegate, double width) {
int colCount = 1;
if (delegate is SliverGridDelegateWithMaxCrossAxisExtent) {
colCount = (width / (delegate.maxCrossAxisExtent + delegate.crossAxisSpacing)).ceil();
} else if (delegate is SliverGridDelegateWithFixedCrossAxisCount) {
colCount = delegate.crossAxisCount;
}
return max(1, colCount);
}
}
@karvulf and flutter_reorderable_grid_view: ^5.3.0
Thanks for the response. Is it possible to make a reproducible example for me that I can try to fix it with that one? @isenbj
Closing this one because of no response
Hey @karvulf thanks for the great library, it really is working wonderfully and enhances my app greatly. One possible bug, or misconfiguration on my part is causing issue. When adding elements to the grid, they do not reorder very well and cause animation issues. The grid is still reorderable, just not the new elements. They start animating weird. After reloading the page it works fine.
I've uploaded a video of the issue here:
https://youtu.be/rNe7yb6W-xc