karvulf / flutter-reorderable-grid-view

BSD 3-Clause "New" or "Revised" License
158 stars 23 forks source link

Reorder not possible when there is an animation #36

Closed cavedweller closed 2 years ago

cavedweller commented 2 years ago

Issue

When adding an animation I can no longer re-order the grid.

Possible cause

I found that it might be the setState((){}) in the animation listener. If you remove the _expandAnimation.value in the Transform.scale and set the scale to 1, I can re-order due to the animation not being built.

Code ```dart import 'package:flutter/material.dart'; import 'package:flutter_reorderable_grid_view/widgets/reorderable_builder.dart'; class Grids extends StatefulWidget { const Grids({Key key}) : super(key: key); @override State createState() => _GridsState(); } class _GridsState extends State with SingleTickerProviderStateMixin { List editable_urls = [ Test('https://picsum.photos/id/0/5616/3744', 1), Test('https://picsum.photos/id/1/5616/3744', 2), Test('https://picsum.photos/id/10/2500/1667', 3), ]; AnimationController _controller; Animation _expandAnimation; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(milliseconds: 200), vsync: this, ); _expandAnimation = Tween(begin: 0, end: 1.0).animate(_controller); _expandAnimation.addListener(() { setState(() { print('setstate'); }); }); } List media() { List generatedChildren = List.generate( editable_urls.length, (index) { final devicesize = MediaQuery.of(context).size; double _width = devicesize.width; double _height = devicesize.height; return Stack( key: Key(editable_urls[index].url), // children: [ InkWell( onTap: () { print('ola'); }, child: AspectRatio( aspectRatio: 1, child: Container( child: Image.network( editable_urls[index].url, fit: BoxFit.cover, ), ), ), ), ], ); }, ); return generatedChildren; } @override Widget build(BuildContext context) { final devicesize = MediaQuery.of(context).size; double _width = devicesize.width; double _height = devicesize.height; return SafeArea( child: Scaffold( appBar: AppBar( title: const Text('ola'), ), floatingActionButton: // body: Container( height: _height, width: _width, child: SizedBox.expand( child: Stack( alignment: Alignment.bottomRight, clipBehavior: Clip.none, children: [ InkWell( highlightColor: Colors.transparent, splashColor: Colors.transparent, onTap: () { if (_controller.isCompleted) { _controller.reverse(); } else { _controller.forward(); } }, child: Container( decoration: BoxDecoration( shape: BoxShape.circle, gradient: LinearGradient( begin: Alignment.centerLeft, end: Alignment.centerRight, colors: _controller.isCompleted ? [Colors.red, Colors.red] : [Color(0xff283e87), Color(0xff743351)]), ), height: _height * .065, width: _height * .065, child: _controller.isCompleted ? Icon( Icons.close, color: Colors.white, ) : Icon( Icons.perm_media_outlined, color: Colors.white, ), ), ), Positioned( right: _width * .01, bottom: _height * .08, child: Transform.scale( scale: _expandAnimation.value, child: Container( padding: EdgeInsets.all(_height * .007), clipBehavior: Clip.hardEdge, height: _height * .25, width: _width * .9, decoration: BoxDecoration( color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(_height * .03), ), child: ReorderableBuilder( children: media(), onReorder: (List orderUpdateEntities) { for (final orderUpdateEntity in orderUpdateEntities) { print(orderUpdateEntity); final pos = editable_urls .removeAt(orderUpdateEntity.oldIndex); editable_urls.insert( orderUpdateEntity.newIndex, pos); } }, lockedIndices: [editable_urls.length], longPressDelay: Duration(milliseconds: 200), builder: (children, scrollController) { return GridView( controller: scrollController, children: children, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 4, mainAxisSpacing: 3, crossAxisSpacing: 3, ), ); }, ), ), ), ), ], ), ), ), ), ); } } class Test { String url; int rand; Test(this.url, this.rand); } ```

I do apologize that the code is not null-safe

Any assistance will greatly be appreciated 😎

karvulf commented 2 years ago

Hi @cavedweller, thank you for opening this issue. That seems to be a special case. I will look into your problem in the next days.

Edit: I found some time now :D

karvulf commented 2 years ago

Ok I found a workaround for your problem. Maybe not the best solution because the children are rebuilt every time you close and reopen the widget but it enables the behavior for reordering.

The explanation why it is currently not working: It looks like the problem is that the children are built at a time when they are not displayed. This has issues when I calculate their offset. All the children are getting Offset = zero. So if I can make sure that the children are shown, when you tap on the button and the animation is finished, then the calculated offset would be correct.

If you add the following lines to your method media:

List<Widget> media() {
  if (_expandAnimation.value != 1.0) {
    return [];
  }
  ...
}

Is that helping you? @cavedweller

cavedweller commented 2 years ago

Hi @karvulf, thank you so much for the quick response, I really do appreciate itπŸ‘πŸ˜Ž

So I added the bit you suggested but it is still blocked from re-ordering. Not sure where I am going wrong here.

The new piece of code is below.

Code ```dart import 'package:flutter/material.dart'; import 'package:flutter_reorderable_grid_view/widgets/reorderable_builder.dart'; class Grids extends StatefulWidget { const Grids({Key? key}) : super(key: key); @override State createState() => _GridsState(); } class _GridsState extends State with SingleTickerProviderStateMixin { List editable_urls = [ Test('https://picsum.photos/id/0/5616/3744', 1), Test('https://picsum.photos/id/1/5616/3744', 2), Test('https://picsum.photos/id/10/2500/1667', 3), ]; late AnimationController _controller; late Animation _expandAnimation; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(milliseconds: 200), vsync: this, ); _expandAnimation = Tween(begin: 0, end: 1.0).animate(_controller); _expandAnimation.addListener(() { setState(() { print('setstate'); }); }); } List media() { if (_expandAnimation.value != 1.0) { return []; } else { List generatedChildren = List.generate( editable_urls.length, (index) { final devicesize = MediaQuery.of(context).size; double _width = devicesize.width; double _height = devicesize.height; return Stack( key: Key(editable_urls[index].url), // children: [ InkWell( onTap: () { print('ola'); }, child: AspectRatio( aspectRatio: 1, child: SizedBox( child: Image.network( editable_urls[index].url, fit: BoxFit.cover, ), ), ), ), ], ); }, ); return generatedChildren; } } @override Widget build(BuildContext context) { final devicesize = MediaQuery.of(context).size; double _width = devicesize.width; double _height = devicesize.height; return SafeArea( child: Scaffold( appBar: AppBar( title: const Text('ola'), ), floatingActionButton: // body: SizedBox( height: _height, width: _width, child: SizedBox.expand( child: Stack( alignment: Alignment.bottomRight, clipBehavior: Clip.none, children: [ InkWell( highlightColor: Colors.transparent, splashColor: Colors.transparent, onTap: () { if (_controller.isCompleted) { _controller.reverse(); } else { _controller.forward(); } }, child: Container( decoration: BoxDecoration( shape: BoxShape.circle, gradient: LinearGradient( begin: Alignment.centerLeft, end: Alignment.centerRight, colors: _controller.isCompleted ? [Colors.red, Colors.red] : [ const Color(0xff283e87), const Color(0xff743351) ]), ), height: _height * .065, width: _height * .065, child: _controller.isCompleted ? const Icon( Icons.close, color: Colors.white, ) : const Icon( Icons.perm_media_outlined, color: Colors.white, ), ), ), Positioned( right: _width * .01, bottom: _height * .08, child: Transform.scale( scale: _expandAnimation.value, child: Container( padding: EdgeInsets.all(_height * .007), clipBehavior: Clip.hardEdge, height: _height * .25, width: _width * .9, decoration: BoxDecoration( color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(_height * .03), ), child: ReorderableBuilder( children: media(), onReorder: (List orderUpdateEntities) { for (final orderUpdateEntity in orderUpdateEntities) { final pos = editable_urls .removeAt(orderUpdateEntity.oldIndex); editable_urls.insert( orderUpdateEntity.newIndex, pos); } }, lockedIndices: [editable_urls.length], longPressDelay: const Duration(milliseconds: 200), builder: (children, scrollController) { return GridView( controller: scrollController, children: children, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 4, mainAxisSpacing: 3, crossAxisSpacing: 3, ), ); }, ), ), ), ), ], ), ), ), ), ); } } class Test { String url; int rand; Test(this.url, this.rand); } ```

Edit: I am sorry, it does appear to work. I will do a full test tonight when I get home and let you know πŸ˜ŽπŸ‘

karvulf commented 2 years ago

Sounds great @cavedweller

cavedweller commented 2 years ago

@karvulf after much testing it appears that its working perfectly, should I have any issues ill let you know 😎 Thanks again for your time and assistance πŸ˜ƒ

karvulf commented 2 years ago

Glad to hear that πŸ‘ @cavedweller