diff --git a/CHANGELOG.md b/CHANGELOG.md index 053b646..3c4b71f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,52 +1,48 @@ -#v1.1.0 +## 1.1.1 +### Enhancement +- Improve example app + +## 1.1.0 + +### Enhancement - Fix shrinkwrap support -#v1.0.9 +## 1.0.9 +### Enhancement - Minor changes -#v1.0.8 +## 1.0.8 -# Changelog - -## Enhancement +### Enhancements - Add `isItemSame` callback to compare two items to determine if two items are the same. It should return true if the two compared items are identical. - Add `shrinkWrap` property to allow the widget to size itself to the size of its children in the main axis direction. -#v1.0.7 - -# Changelog +## 1.0.7 -## Bug Fixes +### Bug Fixes - List Will Not Animate When New Item Is Added to the End of the List -## Enhancement +### Enhancement - Allow Disabling Long Press to Start Reorder Gesture -# v1.0.6 +## 1.0.6 -# Changelog - -## Bug Fixes +### Bug Fixes - Add equality check in example app to prevent animation on update of item in list. +## 1.0.5 -# v1.0.5 - -# Changelog - -## Bug Fixes +### Bug Fixes - Fix `onReorderEnd` callback not being called after reordering is completed. -# v1.0.4 - -# Changelog +## 1.0.4 -## Bug Fixes +### Bug Fixes - Fix blink issue -## Enhancements +### Enhancements - Add support of Drag Handler for `TargetPlatformVariant.desktop` diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift index 70693e4..b636303 100644 --- a/example/ios/Runner/AppDelegate.swift +++ b/example/ios/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ import UIKit import Flutter -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, diff --git a/example/lib/home_page.dart b/example/lib/home_page.dart deleted file mode 100644 index 6f644b7..0000000 --- a/example/lib/home_page.dart +++ /dev/null @@ -1,320 +0,0 @@ -import 'dart:ui'; - -import 'package:example/utils/extension.dart'; -import 'package:example/utils/item_card.dart'; -import 'package:example/utils/item_tile.dart'; -import 'package:flutter/material.dart'; -import 'package:animated_reorderable_list/animated_reorderable_list.dart'; - -import 'model/user_model.dart'; - -class HomePage extends StatefulWidget { - const HomePage({super.key}); - - @override - State createState() => _HomePageState(); -} - -class _HomePageState extends State { - AnimationType appliedStyle = AnimationType.fadeIn; - List list = - List.generate(8, (index) => User(name: "User $index", index: index)); - int addedNumber = 9; - bool isGrid = true; - - List animations = []; - - void insert() { - addedNumber += 1; - setState(() { - list.insert(1, User(name: "User $addedNumber", index: addedNumber)); - }); - } - - void remove() { - setState(() { - if (list.isNotEmpty && list.length > 1) list.removeAt(1); - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - backgroundColor: Colors.teal, - title: Row( - children: [ - const SizedBox( - width: 10, - ), - DropdownButtonHideUnderline( - child: DropdownButton( - iconEnabledColor: Colors.black87, - value: appliedStyle, - items: - AnimationType.values.map((AnimationType animationType) { - return DropdownMenuItem( - value: animationType, - child: Text( - animationType.name.capitalize(), - style: const TextStyle( - fontSize: 20, - color: Colors.black87, - fontWeight: FontWeight.w500), - ), - ); - }).toList(), - onChanged: (AnimationType? animationType) { - if (animationType == null) { - return; - } - animations = []; - AnimationEffect animation = - AnimationProvider.buildAnimation(animationType); - animations.add(animation); - setState(() { - appliedStyle = animationType; - }); - }), - ) - ], - ), - actions: [ - TextButton( - onPressed: insert, - child: const Text( - 'ADD', - style: TextStyle( - color: Colors.black, - fontSize: 20, - fontWeight: FontWeight.w500), - )), - TextButton( - onPressed: remove, - child: const Text( - 'DEL', - style: TextStyle( - color: Colors.black, - fontSize: 20, - fontWeight: FontWeight.w500), - )) - ], - ), - body: Padding( - padding: const EdgeInsets.all(16), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.teal, - ), - onPressed: () { - setState(() { - if (isGrid != false) { - isGrid = false; - } - }); - }, - child: const Padding( - padding: EdgeInsets.all(8.0), - child: Text( - 'List', - style: TextStyle(fontSize: 25), - ), - )), - ElevatedButton( - style: - ElevatedButton.styleFrom(backgroundColor: Colors.teal), - onPressed: () { - setState(() { - if (isGrid != true) { - isGrid = true; - } - }); - }, - child: const Padding( - padding: EdgeInsets.all(8.0), - child: Text( - 'Grid', - style: TextStyle(fontSize: 25), - ), - ), - ), - ], - ), - const SizedBox( - height: 16, - ), - Expanded( - child: isGrid - ? AnimatedReorderableGridView( - items: list, - scrollDirection: Axis.vertical, - itemBuilder: (BuildContext context, int index) { - return ItemCard( - key: ValueKey(list[index].index), - index: list[index].index); - }, - sliverGridDelegate: - SliverReorderableGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 4), - enterTransition: animations, - exitTransition: animations, - insertDuration: const Duration(milliseconds: 300), - removeDuration: const Duration(milliseconds: 300), - onReorder: (int oldIndex, int newIndex) { - setState(() { - final User user = list.removeAt(oldIndex); - list.insert(newIndex, user); - }); - }, - onReorderEnd: (int index) { - print(" End index : $index"); - }, - onReorderStart: (int index) { - print(" Start index : $index"); - }, - proxyDecorator: proxyDecorator - - /* A custom builder that is for inserting items with animations. - - insertItemBuilder: (Widget child, Animation animation){ - return ScaleTransition( - scale: animation, - child: child, - ); - }, - - - */ - /* A custom builder that is for removing items with animations. - - removeItemBuilder: (Widget child, Animation animation){ - return ScaleTransition( - scale: animation, - child: child, - ); - }, - */ - ) - : AnimatedReorderableListView( - items: list, - itemBuilder: (BuildContext context, int index) { - return ItemTile( - key: ValueKey(list[index].index), - index: list[index].index); - }, - enterTransition: animations, - exitTransition: animations, - insertDuration: const Duration(milliseconds: 300), - removeDuration: const Duration(milliseconds: 300), - onReorder: (int oldIndex, int newIndex) { - final User user = list.removeAt(oldIndex); - list.insert(newIndex, user); - for (int i = 0; i < list.length; i++) { - list[i] = list[i].copyWith(index: list[i].index); - } - setState(() {}); - }, - proxyDecorator: proxyDecorator, - isSameItem: (a, b) => a.index == b.index - - /* A custom builder that is for inserting items with animations. - - insertItemBuilder: (Widget child, Animation animation){ - return ScaleTransition( - scale: animation, - child: child, - ); - }, - - - */ - /* A custom builder that is for removing items with animations. - - removeItemBuilder: (Widget child, Animation animation){ - return ScaleTransition( - scale: animation, - child: child, - ); - }, - */ - ), - ), - ], - ), - )); - } -} - -Widget proxyDecorator(Widget child, int index, Animation animation) { - return AnimatedBuilder( - animation: animation, - builder: (BuildContext context, Widget? child) { - final double animValue = Curves.easeInOut.transform(animation.value); - final double elevation = lerpDouble(0, 6, animValue)!; - return Material( - elevation: elevation, - color: Colors.grey, - shadowColor: Colors.black, - child: child, - ); - }, - child: child, - ); -} - -enum AnimationType { - fadeIn, - flipInY, - flipInX, - landing, - size, - scaleIn, - scaleInTop, - scaleInBottom, - scaleInLeft, - scaleInRight, - slideInLeft, - slideInRight, - slideInDown, - slideInUp, -} - -class AnimationProvider { - static AnimationEffect buildAnimation(AnimationType animationType) { - switch (animationType) { - case (AnimationType.fadeIn): - return FadeIn(); - case (AnimationType.flipInY): - return FlipInY(); - case (AnimationType.flipInX): - return FlipInX(); - case (AnimationType.landing): - return Landing(); - case (AnimationType.size): - return SizeAnimation(); - case (AnimationType.scaleIn): - return ScaleIn(); - case (AnimationType.scaleInTop): - return ScaleInTop(); - case (AnimationType.scaleInBottom): - return ScaleInBottom(); - case (AnimationType.scaleInLeft): - return ScaleInLeft(); - case (AnimationType.scaleInRight): - return ScaleInRight(); - case (AnimationType.slideInLeft): - return SlideInLeft(); - case (AnimationType.slideInRight): - return SlideInRight(); - case (AnimationType.slideInUp): - return SlideInUp(); - case (AnimationType.slideInDown): - return SlideInDown(); - } - } -} diff --git a/example/lib/main.dart b/example/lib/main.dart index afdc6a8..3267387 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,7 +1,273 @@ +import 'dart:ui'; + +import 'package:animated_reorderable_list/animated_reorderable_list.dart'; +import 'package:example/utils/animation_provider.dart'; +import 'package:example/utils/extension.dart'; +import 'package:example/utils/item_card.dart'; +import 'package:example/utils/item_tile.dart'; import 'package:flutter/material.dart'; -import 'home_page.dart'; +import 'model/user_model.dart'; void main() { runApp(const MaterialApp(title: 'Motion List Example', home: HomePage())); } + +class HomePage extends StatefulWidget { + const HomePage({super.key}); + + @override + State createState() => _HomePageState(); +} + +class _HomePageState extends State { + AnimationType appliedStyle = AnimationType.fadeIn; + List list = + List.generate(8, (index) => User(name: "User $index", index: index)); + int addedNumber = 9; + bool isGrid = true; + + List animations = []; + + void insert() { + addedNumber += 1; + setState(() { + list.insert(1, User(name: "User $addedNumber", index: addedNumber)); + }); + } + + void remove() { + setState(() { + if (list.isNotEmpty && list.length > 1) list.removeAt(1); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.teal, + title: Row( + children: [ + const SizedBox( + width: 10, + ), + DropdownButtonHideUnderline( + child: DropdownButton( + iconEnabledColor: Colors.black87, + value: appliedStyle, + items: + AnimationType.values.map((AnimationType animationType) { + return DropdownMenuItem( + value: animationType, + child: Text( + animationType.name.capitalize(), + style: const TextStyle( + fontSize: 20, + color: Colors.black87, + fontWeight: FontWeight.w500), + ), + ); + }).toList(), + onChanged: (AnimationType? animationType) { + if (animationType == null) { + return; + } + animations = []; + AnimationEffect animation = + AnimationProvider.buildAnimation(animationType); + animations.add(animation); + setState(() { + appliedStyle = animationType; + }); + }), + ) + ], + ), + actions: [ + TextButton( + onPressed: insert, + child: const Text( + 'ADD', + style: TextStyle( + color: Colors.black, + fontSize: 20, + fontWeight: FontWeight.w500), + )), + TextButton( + onPressed: remove, + child: const Text( + 'DEL', + style: TextStyle( + color: Colors.black, + fontSize: 20, + fontWeight: FontWeight.w500), + )) + ], + ), + body: Padding( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.teal, + ), + onPressed: () { + setState(() { + if (isGrid != false) { + isGrid = false; + } + }); + }, + child: const Padding( + padding: EdgeInsets.all(8.0), + child: Text( + 'List', + style: TextStyle(fontSize: 25), + ), + )), + ElevatedButton( + style: + ElevatedButton.styleFrom(backgroundColor: Colors.teal), + onPressed: () { + setState(() { + if (isGrid != true) { + isGrid = true; + } + }); + }, + child: const Padding( + padding: EdgeInsets.all(8.0), + child: Text( + 'Grid', + style: TextStyle(fontSize: 25), + ), + ), + ), + ], + ), + const SizedBox( + height: 16, + ), + Expanded( + child: isGrid + ? AnimatedReorderableGridView( + items: list, + scrollDirection: Axis.vertical, + itemBuilder: (BuildContext context, int index) { + return ItemCard( + key: ValueKey(list[index].index), + index: list[index].index); + }, + sliverGridDelegate: + SliverReorderableGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 4), + enterTransition: animations, + exitTransition: animations, + insertDuration: const Duration(milliseconds: 300), + removeDuration: const Duration(milliseconds: 300), + onReorder: (int oldIndex, int newIndex) { + setState(() { + final User user = list.removeAt(oldIndex); + list.insert(newIndex, user); + }); + }, + onReorderEnd: (int index) { + print(" End index : $index"); + }, + onReorderStart: (int index) { + print(" Start index : $index"); + }, + proxyDecorator: proxyDecorator + + /* A custom builder that is for inserting items with animations. + + insertItemBuilder: (Widget child, Animation animation){ + return ScaleTransition( + scale: animation, + child: child, + ); + }, + + + */ + /* A custom builder that is for removing items with animations. + + removeItemBuilder: (Widget child, Animation animation){ + return ScaleTransition( + scale: animation, + child: child, + ); + }, + */ + ) + : AnimatedReorderableListView( + items: list, + itemBuilder: (BuildContext context, int index) { + return ItemTile( + key: ValueKey(list[index].index), + index: list[index].index); + }, + enterTransition: animations, + exitTransition: animations, + insertDuration: const Duration(milliseconds: 300), + removeDuration: const Duration(milliseconds: 300), + onReorder: (int oldIndex, int newIndex) { + final User user = list.removeAt(oldIndex); + list.insert(newIndex, user); + for (int i = 0; i < list.length; i++) { + list[i] = list[i].copyWith(index: list[i].index); + } + setState(() {}); + }, + proxyDecorator: proxyDecorator, + isSameItem: (a, b) => a.index == b.index + + /* A custom builder that is for inserting items with animations. + + insertItemBuilder: (Widget child, Animation animation){ + return ScaleTransition( + scale: animation, + child: child, + ); + }, + + + */ + /* A custom builder that is for removing items with animations. + + removeItemBuilder: (Widget child, Animation animation){ + return ScaleTransition( + scale: animation, + child: child, + ); + }, + */ + ), + ), + ], + ), + )); + } +} + +Widget proxyDecorator(Widget child, int index, Animation animation) { + return AnimatedBuilder( + animation: animation, + builder: (BuildContext context, Widget? child) { + final double animValue = Curves.easeInOut.transform(animation.value); + final double elevation = lerpDouble(0, 6, animValue)!; + return Material( + elevation: elevation, + color: Colors.grey, + shadowColor: Colors.black, + child: child, + ); + }, + child: child, + ); +} diff --git a/example/lib/utils/animation_provider.dart b/example/lib/utils/animation_provider.dart new file mode 100644 index 0000000..55089fb --- /dev/null +++ b/example/lib/utils/animation_provider.dart @@ -0,0 +1,37 @@ +import 'package:animated_reorderable_list/animated_reorderable_list.dart'; + + +class AnimationProvider { + static AnimationEffect buildAnimation(AnimationType animationType) { + switch (animationType) { + case (AnimationType.fadeIn): + return FadeIn(); + case (AnimationType.flipInY): + return FlipInY(); + case (AnimationType.flipInX): + return FlipInX(); + case (AnimationType.landing): + return Landing(); + case (AnimationType.size): + return SizeAnimation(); + case (AnimationType.scaleIn): + return ScaleIn(); + case (AnimationType.scaleInTop): + return ScaleInTop(); + case (AnimationType.scaleInBottom): + return ScaleInBottom(); + case (AnimationType.scaleInLeft): + return ScaleInLeft(); + case (AnimationType.scaleInRight): + return ScaleInRight(); + case (AnimationType.slideInLeft): + return SlideInLeft(); + case (AnimationType.slideInRight): + return SlideInRight(); + case (AnimationType.slideInUp): + return SlideInUp(); + case (AnimationType.slideInDown): + return SlideInDown(); + } + } +} \ No newline at end of file diff --git a/example/pubspec.lock b/example/pubspec.lock index 2027c45..dc87db7 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -23,7 +23,7 @@ packages: path: ".." relative: true source: path - version: "1.0.9" + version: "1.1.0" args: dependency: transitive description: @@ -198,18 +198,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -246,18 +246,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" package_config: dependency: transitive description: @@ -347,10 +347,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" typed_data: dependency: transitive description: @@ -371,10 +371,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.4" watcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8a8a6fb..75a0464 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: animated_reorderable_list description: A Flutter Reorderable Animated List with simple implementation and smooth transition. -version: 1.1.0 +version: 1.1.1 repository: https://github.com/canopas/animated_reorderable_list environment: