From 598e856322d0be1e2b7e066605dde29d363e3b0b Mon Sep 17 00:00:00 2001 From: idubnori Date: Wed, 10 Dec 2025 09:09:15 +0900 Subject: [PATCH] refactor: replace flutter_reorderable_grid_view with custom _ReorderableGrid implementation --- .../quick_action_configurator.widget.dart | 137 +++++++++++++++--- mobile/pubspec.lock | 8 - mobile/pubspec.yaml | 1 - 3 files changed, 115 insertions(+), 31 deletions(-) diff --git a/mobile/lib/presentation/widgets/asset_viewer/quick_action_configurator.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/quick_action_configurator.widget.dart index eb6b3f1576..66c73746f1 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/quick_action_configurator.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/quick_action_configurator.widget.dart @@ -1,7 +1,6 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_reorderable_grid_view/widgets/reorderable_builder.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/providers/infrastructure/viewer_quick_action_order.provider.dart'; import 'package:immich_mobile/utils/action_button.utils.dart'; @@ -32,9 +31,10 @@ class _QuickActionConfiguratorState extends ConsumerState reorder) { + void _onReorder(int oldIndex, int newIndex) { setState(() { - _order = reorder(_order); + final item = _order.removeAt(oldIndex); + _order.insert(newIndex, item); _hasLocalChanges = true; }); } @@ -101,31 +101,22 @@ class _QuickActionConfiguratorState extends ConsumerState constraints.maxHeight; - final horizontalPadding = 8.0; // matches GridView padding + final horizontalPadding = 8.0; final tileWidth = (constraints.maxWidth - horizontalPadding - (crossAxisSpacing * (crossAxisCount - 1))) / crossAxisCount; final childAspectRatio = tileWidth / tileHeight; final gridController = shouldScroll ? _scrollController : null; - return ReorderableBuilder( - onReorder: _onReorder, - enableLongPress: false, + return _ReorderableGrid( scrollController: gridController, - children: [ - for (var i = 0; i < _order.length; i++) - _QuickActionTile(key: ValueKey(_order[i].name), index: i, type: _order[i]), - ], - builder: (children) => GridView.count( - controller: gridController, - crossAxisCount: crossAxisCount, - crossAxisSpacing: crossAxisSpacing, - mainAxisSpacing: mainAxisSpacing, - // padding: const EdgeInsets.fromLTRB(4, 0, 4, 12), - physics: shouldScroll ? const BouncingScrollPhysics() : const NeverScrollableScrollPhysics(), - childAspectRatio: childAspectRatio, - children: children, - ), + items: _order, + onReorder: _onReorder, + crossAxisCount: crossAxisCount, + crossAxisSpacing: crossAxisSpacing, + mainAxisSpacing: mainAxisSpacing, + childAspectRatio: childAspectRatio, + shouldScroll: shouldScroll, ); }, ), @@ -151,11 +142,113 @@ class _QuickActionConfiguratorState extends ConsumerState items; + final Function(int oldIndex, int newIndex) onReorder; + final int crossAxisCount; + final double crossAxisSpacing; + final double mainAxisSpacing; + final double childAspectRatio; + final bool shouldScroll; + + const _ReorderableGrid({ + required this.scrollController, + required this.items, + required this.onReorder, + required this.crossAxisCount, + required this.crossAxisSpacing, + required this.mainAxisSpacing, + required this.childAspectRatio, + required this.shouldScroll, + }); + + @override + State<_ReorderableGrid> createState() => _ReorderableGridState(); +} + +class _ReorderableGridState extends State<_ReorderableGrid> { + int? _draggingIndex; + int? _hoveringIndex; + + @override + Widget build(BuildContext context) { + return GridView.builder( + controller: widget.scrollController, + physics: widget.shouldScroll ? const BouncingScrollPhysics() : const NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: widget.crossAxisCount, + crossAxisSpacing: widget.crossAxisSpacing, + mainAxisSpacing: widget.mainAxisSpacing, + childAspectRatio: widget.childAspectRatio, + ), + itemCount: widget.items.length, + itemBuilder: (context, index) { + final item = widget.items[index]; + final isDragging = _draggingIndex == index; + final isHovering = _hoveringIndex == index; + + return DragTarget( + onWillAcceptWithDetails: (details) { + if (details.data != index) { + setState(() => _hoveringIndex = index); + } + return details.data != index; + }, + onLeave: (_) { + setState(() => _hoveringIndex = null); + }, + onAcceptWithDetails: (details) { + final oldIndex = details.data; + if (oldIndex != index) { + widget.onReorder(oldIndex, index); + } + setState(() { + _hoveringIndex = null; + _draggingIndex = null; + }); + }, + builder: (context, candidateData, rejectedData) { + return LongPressDraggable( + data: index, + feedback: Material( + color: Colors.transparent, + child: Opacity( + opacity: 0.8, + child: Transform.scale( + scale: 1.1, + child: _QuickActionTile(index: index, type: item), + ), + ), + ), + childWhenDragging: Opacity( + opacity: 0.3, + child: _QuickActionTile(index: index, type: item), + ), + onDragStarted: () { + setState(() => _draggingIndex = index); + }, + onDragEnd: (_) { + setState(() => _draggingIndex = null); + }, + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + transform: isHovering && !isDragging ? Matrix4.translationValues(0, -4, 0) : Matrix4.identity(), + child: _QuickActionTile(index: index, type: item), + ), + ); + }, + ); + }, + ); + } +} + class _QuickActionTile extends StatelessWidget { final int index; final ActionButtonType type; - const _QuickActionTile({super.key, required this.index, required this.type}); + const _QuickActionTile({required this.index, required this.type}); @override Widget build(BuildContext context) { diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 735031846f..6a067f509f 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -665,14 +665,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.27" - flutter_reorderable_grid_view: - dependency: "direct main" - description: - name: flutter_reorderable_grid_view - sha256: beb85f95325c83515d8953e8612dc70d287a69d1437c14262b7d738070133a87 - url: "https://pub.dev" - source: hosted - version: "5.5.2" flutter_riverpod: dependency: transitive description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 2c2816e548..a49a012031 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -38,7 +38,6 @@ dependencies: flutter_udid: ^4.0.0 flutter_web_auth_2: ^5.0.0-alpha.0 fluttertoast: ^8.2.12 - flutter_reorderable_grid_view: ^5.5.2 geolocator: ^14.0.2 home_widget: ^0.8.1 hooks_riverpod: ^2.6.1