Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

_ReorderableBuilderState._handleDragEnd thows null check error #44

Closed
meomap opened this issue Jul 24, 2022 · 9 comments
Closed

_ReorderableBuilderState._handleDragEnd thows null check error #44

meomap opened this issue Jul 24, 2022 · 9 comments
Labels
bug Something isn't working

Comments

@meomap
Copy link

meomap commented Jul 24, 2022

Hello, thank you for your great package.

We've got this exception thrown at the production app.

flutter_reorderable_grid_view: ^3.1.2

Sentry stacktrace:

_CastError: Null check operator used on a null value
  split_config.arm64_v8a.apk0x727de7b968 _ReorderableBuilderState._handleDragEnd (reorderable_builder.dart:318)
  split_config.arm64_v8a.apk0x727de7b558 _ReorderableBuilderState._handleDragEnd (reorderable_builder.dart:315)
  split_config.arm64_v8a.apk0x727de7ca24 _ReorderableScrollingListenerState.build.<T> (reorderable_scrolling_listener.dart:82)
  split_config.arm64_v8a.apk0x727dc6e674 RenderPointerListener.handleEvent (proxy_box.dart:2916)
  split_config.arm64_v8a.apk0x727e15f3a0 GestureBinding.dispatchEvent (binding.dart:425)
  split_config.arm64_v8a.apk0x727db0df7c RendererBinding.dispatchEvent (binding.dart:329)
  split_config.arm64_v8a.apk0x727db0dec0 GestureBinding._handlePointerEventImmediately (binding.dart:380)
  split_config.arm64_v8a.apk0x727db0dac4 GestureBinding.handlePointerEvent (binding.dart:344)
  split_config.arm64_v8a.apk0x727dbd560c GestureBinding._handlePointerDataPacket (binding.dart:285)
  split_config.arm64_v8a.apk0x727dbd5510 GestureBinding._handlePointerDataPacket (binding.dart:280)
  split_config.arm64_v8a.apk0x727e1143a0 _rootRunUnary (zone.dart:1442)
  split_config.arm64_v8a.apk0x727da35d4c _rootRunUnary (zone.dart:1432)
  split_config.arm64_v8a.apk0x727e00f5f8 _CustomZone.runUnary (zone.dart:1335)
  split_config.arm64_v8a.apk0x727e23269c _CustomZone.runUnaryGuarded (zone.dart:1244)
  split_config.arm64_v8a.apk0x727da46af0 _invoke1 (hooks.dart:170)
  split_config.arm64_v8a.apk0x727da469f0 PlatformDispatcher._dispatchPointerDataPacket (platform_dispatcher.dart:331)
  split_config.arm64_v8a.apk0x727da46968 _dispatchPointerDataPacket (hooks.dart:94)
@karvulf
Copy link
Owner

karvulf commented Jul 24, 2022

Hi @meomap
Thank you for opening the issue.
I will try to fix that problem

@karvulf karvulf added the bug Something isn't working label Jul 24, 2022
@karvulf
Copy link
Owner

karvulf commented Jul 24, 2022

I am wondering why that problem is happening. Could you share the code snippet of the part where you use ReorderableBuilder? Are you changing some values of ReorderableBuilder while dragging? @meomap

@meomap
Copy link
Author

meomap commented Jul 24, 2022

Sure, we use ReorderableBuilder to render a grid view of photo collection where user can add new photo, remove existing one and dragging to change order.

Here is code snippet:

import 'package:flutter/material.dart';
import 'package:equatable/equatable.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_reorderable_grid_view/entities/order_update_entity.dart';
import 'package:flutter_reorderable_grid_view/widgets/widgets.dart';

class PhotoCollectionModal extends StatefulWidget {
  const PhotoCollectionModal({Key? key}) : super(key: key);

  @override
  State<PhotoCollectionModal> createState() => _PhotoCollectionModalState();
}

class _PhotoCollectionModalState extends State<PhotoCollectionModal> {
  bool draggingItem = false;

  final _gridViewKey = GlobalKey();
  final _scrollController = ScrollController();
  final List<Photo> items =
      List<Photo>.generate(10, (index) => _dummyPhoto(index));

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;
    const double space = 16;
    const cols = 3;
    final generatedChildren = List<Widget>.generate(
        items.length,
        (index) => Container(
              key: ValueKey(items[index]),
              decoration: BoxDecoration(
                color: colorScheme.surface,
                borderRadius: BorderRadius.circular(4),
                border: Border.all(color: colorScheme.outline),
              ),
              child: Stack(
                children: [
                  Center(
                    child: CachedNetworkImage(
                      imageUrl: items[index].networkUrl,
                      fit: BoxFit.scaleDown,
                    ),
                  ),
                  Align(
                      alignment: Alignment.topRight,
                      child: Padding(
                        padding: const EdgeInsets.all(4),
                        child: GestureDetector(
                            onTap: () => handleRemove(index),
                            child:
                                Icon(Icons.remove_circle, color: colorScheme.error)),
                      ))
                ],
              ),
            ));

    return SafeArea(
      child: Scaffold(
          appBar: AppBar(title: const Text('Collection')),
          floatingActionButton: draggingItem
              ? null
              : FloatingActionButton(
                  child: const Icon(Icons.add), onPressed: handleAdd),
          body: Padding(
            padding: const EdgeInsets.all(space),
            child: ReorderableBuilder(
              children: generatedChildren,
              onReorder: handleReorder,
              enableLongPress: false,
              dragChildBoxDecoration: BoxDecoration(
                  color: Theme.of(context)
                      .colorScheme
                      .secondaryContainer
                      .withOpacity(0.54)),
              onDragStarted: () {
                setState(() {
                  draggingItem = true;
                });
              },
              onDragEnd: () {
                setState(() {
                  draggingItem = false;
                });
              },
              scrollController: _scrollController,
              builder: (children) {
                return GridView.builder(
                  key: _gridViewKey,
                  controller: _scrollController,
                  itemCount: children.length,
                  itemBuilder: (context, index) {
                    return children[index];
                  },
                  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: cols,
                    mainAxisSpacing: space,
                    crossAxisSpacing: space,
                  ),
                );
              },
            ),
          )),
    );
  }

  void handleAdd() {
    setState(() {
      items.add(_dummyPhoto(items.length));
    });
  }

  void handleRemove(int index) {
    setState(() {
      items.removeAt(index);
    });
  }

  void handleReorder(List<OrderUpdateEntity> updates) {
    for (final entry in updates) {
      if (entry.oldIndex >= items.length || entry.newIndex >= items.length) {
        // Skip deleted one
        continue;
      }
      final child = items.removeAt(entry.oldIndex);
      items.insert(entry.newIndex, child);
    }
    if (!mounted) return;
    setState(() {});
  }
}

Photo _dummyPhoto(int index) => Photo(
    'https://picsum.photos/${100 + index}/${100 + index}/',
    DateTime.now().microsecondsSinceEpoch + index);

class Photo extends Equatable {
  final int id;
  final String networkUrl;

  const Photo(this.networkUrl, this.id);

  @override
  List<Object?> get props => [networkUrl, id];
}

@meomap
Copy link
Author

meomap commented Jul 24, 2022

In void handleReorder(List<OrderUpdateEntity> updates), we noticed that when user removed an item then immediately drag one at the end of list, entry.oldIndex sometimes equals current items.length. It happens occasionally and is reproducible in dev environment.

@karvulf
Copy link
Owner

karvulf commented Jul 26, 2022

I realized that this bug that you describe seems to happen when using GridView.builder. I also recognized that the animation of removing an item is not working.
So if you don't have a special reason to use GridView.builder, you could also use GridView as workaround. I will try to fix this problem in the next days. @meomap

return  GridView(
  key: _gridViewKey,
  controller: _scrollController,
  children: children,
  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: cols,
    mainAxisSpacing: space,
    crossAxisSpacing: space,
  ),

@karvulf
Copy link
Owner

karvulf commented Jul 26, 2022

I think I found a solution, so you could also wait for the update in the next days 👍 @meomap

@meomap
Copy link
Author

meomap commented Jul 27, 2022

@karvulf It's ok, I think it's just a minor glitch and we don't have much users affected by it yet. I can use GridView as well. No need to hurry :)

Thank you so much for your support 👌

@karvulf
Copy link
Owner

karvulf commented Jul 30, 2022

The bug should be fixed and released in a few minutes.
You can use version 3.1.3 for that. Also the animation for adding or removing your images should work now :)
I am closing this issue, if there are some other new issues with this update, feel free to open a new one @meomap

@karvulf karvulf closed this as completed Jul 30, 2022
@meomap
Copy link
Author

meomap commented Jul 31, 2022

That's great! @karvulf Thanks so much 👌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants