karvulf / flutter-reorderable-grid-view

BSD 3-Clause "New" or "Revised" License
143 stars 20 forks source link

reorder doesnt work on showModalBottomSheet() #83

Closed naw2nd closed 1 year ago

naw2nd commented 1 year ago

dragStart() & dragEnd() called, but onReorder() isn't called

// Calling bottom sheet
showModalBottomSheet(
      context: context,
      isScrollControlled: true,
      useSafeArea: true,
      enableDrag: true,
      backgroundColor: Colors.transparent,
      builder: (context) {
        return AllMenusBottomSheet(
          homeController: homeController,
        );
      },
    );

 // Bottom Sheet Widget
class AllMenusBottomSheet extends StatelessWidget {
  AllMenusBottomSheet({
    super.key,
    required this.homeController,
  });

  final DashboardHomeController homeController;

  final _gridViewKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return DraggableScrollableSheet(
      initialChildSize: 0.75,
      minChildSize: 0.75,
      maxChildSize: 0.75,
      snapSizes: const [0.75],
      snap: true,
      builder: 

it works when I use showDialog(), but it works weirdly when my DraggableScrollableSheet widget change position after scrolled

karvulf commented 1 year ago

Hello @naw2nd Thanks for opening the issue. Can you also share the full code of AllMenusBottomSheet where you use ReoderableBuilder? And which version of this package and of flutter are you using?

naw2nd commented 1 year ago

i'm using flutter 3.7.12

this is my AllMenusBottomSheet widget

class AllMenusBottomSheet extends StatelessWidget {
  AllMenusBottomSheet({
    super.key,
    required this.homeController,
  });

  final DashboardHomeController homeController;

  final _gridViewKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return DraggableScrollableSheet(
      builder: (context, scrollController) {
        return Column(
          children: [
            Container(
              padding: const EdgeInsets.all(10),
              decoration: const BoxDecoration(
                color: ThemeConfig.kGreenLime,
                borderRadius: BorderRadius.only(
                  topLeft: Radius.circular(10),
                  topRight: Radius.circular(10),
                ),
              ),
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      TranslatedText(
                        StringLabel.allMenus,
                        style: Theme.of(context)
                            .textTheme
                            .titleSmall
                            ?.copyWith(fontWeight: FontWeight.bold),
                      ),
                      const SizedBox(
                        height: 2,
                      ),
                      TranslatedText(
                        StringLabel.changeMenuOrder,
                        style: Theme.of(context).textTheme.bodySmall,
                      ),
                    ],
                  ),
                  GestureDetector(
                    onTap: () {
                      Get.back();
                    },
                    child: const Icon(
                      Icons.close,
                    ),
                  )
                ],
              ),
            ),
            Expanded(
              child: Container(
                color: Colors.white,
                child: ReorderableBuilder(
                  scrollController: scrollController,
                  dragChildBoxDecoration: const BoxDecoration(),
                  onReorder: (List<OrderUpdateEntity> orderUpdateEntities) {
                    for (final orderUpdateEntity in orderUpdateEntities) {
                      final item = homeController.homeMenuWidgetItems
                          .removeAt(orderUpdateEntity.oldIndex);
                      homeController.homeMenuWidgetItems
                          .insert(orderUpdateEntity.newIndex, item);
                    }
                  },
                  enableDraggable: true,
                  builder: (childern) {
                    return GridView(
                      key: _gridViewKey,
                      controller: scrollController,
                      shrinkWrap: true,
                      addAutomaticKeepAlives: false,
                      padding: const EdgeInsets.symmetric(
                        horizontal: 8,
                        vertical: 8,
                      ),
                      gridDelegate:
                          const SliverGridDelegateWithFixedCrossAxisCount(
                        crossAxisCount: 5,
                        childAspectRatio: 0.8,
                      ),
                      children: childern,
                    );
                  },
                  children: [
                    ...homeController.homeMenuWidgetItems.map(
                      (value) => HomeMenuWidget(
                        key: Key(value.name),
                        label: value.name,
                        assetUrl: value.assetPath,
                        onTap: () {
                          if (value.onTap != null) {
                            value.onTap!();
                          } else {
                            Get.toNamed(
                              value.route,
                              preventDuplicates: false,
                            );
                          }
                        },
                        isNew: value.menu.isNew,
                        isVisible: value.menu.value,
                      ),
                    )
                  ],
                ),
              ),
            ),
          ],
        );
      },
    );
  }
}
karvulf commented 1 year ago

It looks good so far, unfortunately in the next 3 weeks I am on vacation and can't test this. What I suggest is that you could try out the version 5.0.0-dev.6 of the package. There are some breaking changes but you can check them in the example app. Probably that fixes the issue but can't guarantee it. @naw2nd

naw2nd commented 1 year ago

I have tried using 5.0.0-dev.6. with flutter 3.10.1 The onReorder function still has not been called when I use showModalBottomSheet(). here some minimal code that I have been used to test it:

My Code ```dart import 'package:flutter/material.dart'; import 'package:flutter_reorderable_grid_view/widgets/reorderable_builder.dart'; void main() { runApp(const MainApp()); } class MainApp extends StatelessWidget { const MainApp({super.key}); // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const MyApp(), ); } } class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @override MyAppState createState() => MyAppState(); } class MyAppState extends State { final _scrollController = ScrollController(); final _gridViewKey = GlobalKey(); final _fruits = ["apple", "banana", "strawberry"]; @override Widget build(BuildContext context) { final generatedChildren = List.generate( _fruits.length, (index) => Container( key: Key(_fruits.elementAt(index)), color: Colors.lightBlue, child: Text( _fruits.elementAt(index), ), ), ); return Scaffold( body: ReorderableBuilder( scrollController: _scrollController, onReorder: (onReorder) { print('reorder'); }, builder: (children) { return GridView( key: _gridViewKey, controller: _scrollController, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 4, mainAxisSpacing: 4, crossAxisSpacing: 8, ), children: children, ); }, children: generatedChildren, ), floatingActionButton: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ FloatingActionButton( child: Text('dialog'), onPressed: () { showDialog( context: context, builder: (context) { return ReorderDialog(); }, ); }), SizedBox( width: 20, ), FloatingActionButton( child: Text('bottomSheet'), onPressed: () { showModalBottomSheet( context: context, builder: (context) { return ReorderBottomSheet(); }, ); }), ], ), ); } } class ReorderBottomSheet extends StatelessWidget { ReorderBottomSheet({super.key}); final _scrollController = ScrollController(); final _gridViewKey = GlobalKey(); final _fruits = ["apple", "banana", "strawberry"]; @override Widget build(BuildContext context) { final generatedChildren = List.generate( _fruits.length, (index) => Container( key: Key(_fruits.elementAt(index)), color: Colors.lightBlue, child: Text( _fruits.elementAt(index), ), ), ); return DraggableScrollableSheet( builder: (context, scrollController) { return ReorderableBuilder( scrollController: _scrollController, onReorder: (onReorder) { print('reorder'); }, builder: (children) { return GridView( key: _gridViewKey, controller: _scrollController, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 4, mainAxisSpacing: 4, crossAxisSpacing: 8, ), children: children, ); }, children: generatedChildren, ); }, ); } } class ReorderDialog extends StatelessWidget { ReorderDialog({super.key}); final _scrollController = ScrollController(); final _gridViewKey = GlobalKey(); final _fruits = ["apple", "banana", "strawberry"]; @override Widget build(BuildContext context) { final generatedChildren = List.generate( _fruits.length, (index) => Container( key: Key(_fruits.elementAt(index)), color: Colors.lightBlue, child: Text( _fruits.elementAt(index), ), ), ); return Dialog( child: ReorderableBuilder( scrollController: _scrollController, onReorder: (onReorder) { print('reorder'); }, builder: (children) { return GridView( key: _gridViewKey, controller: _scrollController, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 4, mainAxisSpacing: 4, crossAxisSpacing: 8, ), children: children, ); }, children: generatedChildren, ), ); } } ```
karvulf commented 1 year ago

thank you very much for preparing this, I will try to fix this issue as soon as possible @naw2nd

karvulf commented 1 year ago

I checked your example and I know why it does not work. The main issue is that the bottom modal sheet is animated and comes from bottom to the middle. While this happens, the widgets are also built. At the beginning when the bottom sheet appears, I get positions of the widgets which are still at the bottom. To ensure that the positions are correct, you need to add a delay to ReorderableBuilder. The modal bottom sheets of flutter have a default animation duration of 200 ms. If you add the following line to ReorderableBuilder the drag and drop will work also for the bottom modal sheet:

...
        return ReorderableBuilder(
          initDelay: kThemeAnimationDuration, // add this line and import the const variable
          scrollController: _scrollController,
...

@naw2nd

karvulf commented 1 year ago

I did some more research and I think I found a solution for this bug. If the solution works, the parameter initDelay can be deleted. @naw2nd

karvulf commented 1 year ago

You can test it when you add the following to your pubspec.yaml:

  flutter_reorderable_grid_view:
    git:
      url: https://github.com/karvulf/flutter-reorderable-grid-view.git
      ref: bugfix/calculating-positions-wrong
karvulf commented 1 year ago

I released a new pre version 5.0.0-dev.7 that should fix your issue @naw2nd