draggable

This commit is contained in:
Dmitry 2024-06-13 08:06:50 +03:00
parent 69930bd9df
commit a6a7ea6c8c
13 changed files with 420 additions and 162 deletions

View file

@ -198,7 +198,10 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
final _undoableIndex = Undoable<int?>(null); final _undoableIndex = Undoable<int?>(null);
final Queue<CardSwiperDirection> _directionHistory = Queue(); final Queue<CardSwiperDirection> _directionHistory = Queue();
int? get _currentIndex => _undoableIndex.state; bool usePrevIndex = false;
int? get _currentIndex =>
usePrevIndex ? _undoableIndex.previousState : _undoableIndex.state;
int? get _nextIndex => getValidIndexOffset(1); int? get _nextIndex => getValidIndexOffset(1);
@ -319,13 +322,13 @@ class _CardSwiperState<T extends Widget> extends State<CardSwiper>
onPanUpdate: (tapInfo) { onPanUpdate: (tapInfo) {
if (!widget.isDisabled && if (!widget.isDisabled &&
tapInfo.delta.dx.abs() > 2 * tapInfo.delta.dy.abs()) { tapInfo.delta.dx.abs() > 2 * tapInfo.delta.dy.abs()) {
setState( setState(() {
() => _cardAnimation.update( _cardAnimation.update(
tapInfo.delta.dx, tapInfo.delta.dx,
tapInfo.delta.dy, tapInfo.delta.dy,
_tappedOnTop, _tappedOnTop,
), );
); });
} }
}, },
onPanEnd: (tapInfo) { onPanEnd: (tapInfo) {

View file

@ -1,14 +1,13 @@
import 'dart:async'; import 'dart:async';
import 'dart:developer'; import 'dart:developer';
import 'package:collection/collection.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:mnemo_cards/features/packs/preview_packs_poller.dart';
import 'package:mnemo_cards/features/tests/test_state_holder.dart'; import 'package:mnemo_cards/features/tests/test_state_holder.dart';
import 'package:mnemo_cards/features/tests/test_widgets/test_image.dart'; import 'package:mnemo_cards/features/tests/test_widgets/test_image.dart';
import 'package:mnemo_cards/features/packs/pack_cache_manager.dart'; import 'package:mnemo_cards/features/packs/pack_cache_manager.dart';
import 'package:mnemo_cards/main.dart'; import 'package:mnemo_cards/main.dart';
import 'package:mnemo_cards/theme/themes.dart'; import 'package:mnemo_cards/theme/themes.dart';
import 'package:mnemo_cards/utils/iterable_helper.dart';
import 'package:mnemo_cards/utils/string_helper.dart'; import 'package:mnemo_cards/utils/string_helper.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart'; import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
@ -56,7 +55,22 @@ class TestManager extends ChangeNotifier {
(tests) async => pollTests(packId: packId) (tests) async => pollTests(packId: packId)
.catchError((_) => Future.value(<TestDto>[])), .catchError((_) => Future.value(<TestDto>[])),
) )
.where((test) => test.isNotEmpty); .where((test) => test.isNotEmpty)
.distinct((p, n) {
if (p.length == n.length) {
for (int i = 0; i < p.length; i++) {
if (p[i].id != n[i].id ||
p[i].version != n[i].version ||
!ListEquality().equals(
p[i].statistics?.words.words,
n[i].statistics?.words.words,
)) {
return false;
}
}
}
return false;
});
} }
TestQuestionState? getState(int id) => testHolder.testQuestionState(id); TestQuestionState? getState(int id) => testHolder.testQuestionState(id);

View file

@ -66,10 +66,10 @@ class _TestProgressWidgetState extends State<TestProgressWidget> {
super.initState(); super.initState();
locator.testManager.pageController.removeListener(_updateDataListener); locator.testManager.pageController.removeListener(_updateDataListener);
locator.testManager.pageController.addListener(_updateDataListener); locator.testManager.pageController.addListener(_updateDataListener);
timer = Stream.periodic( // timer = Stream.periodic(
Duration(seconds: 1), // Duration(seconds: 1),
(tick) => Duration(seconds: 300 - tick - 1), // (tick) => Duration(seconds: 300 - tick - 1),
).startWith(Duration(seconds: 300)).take(300); // ).startWith(Duration(seconds: 300)).take(300);
_updateData(); _updateData();
} }

View file

@ -11,7 +11,6 @@ extension NullableIterableHelper<T> on Iterable<T?> {
} }
extension ListHelper<T> on List<T> { extension ListHelper<T> on List<T> {
// List<T> shuffle([int? seed]) { // List<T> shuffle([int? seed]) {
// Random r = Random(seed); // Random r = Random(seed);
// var items = [...this]; // var items = [...this];
@ -26,4 +25,37 @@ extension ListHelper<T> on List<T> {
// return items; // return items;
// } // }
} void move(int from, int to) {
RangeError.checkValidIndex(from, this, "from", length);
RangeError.checkValidIndex(to, this, "to", length);
var element = this[from];
if (from < to) {
this.setRange(from, to, this, from + 1);
} else {
this.setRange(to + 1, from + 1, this, to);
}
this[to] = element;
}
void sortInOrder<P>(List<P> order, P valueGetter(T v)) => sort((p, n) {
final pVal = valueGetter(p);
final nVal = valueGetter(n);
final pIndex = order.indexOf(pVal);
final nIndex = order.indexOf(nVal);
if (pIndex == -1) {
return nIndex == -1
? () {
if (pVal is num) {
return pVal.compareTo(nVal as num);
} else if (pVal is String) {
return pVal.compareTo(nVal as String);
}
return pVal.toString().compareTo(nVal.toString());
}.call()
: 1;
} else if (nIndex == -1) {
return -1;
}
return pIndex.compareTo(nIndex);
});
}

View file

@ -11,6 +11,7 @@ import '../../features/card_flipper/flip_card_controllers.dart';
import '../../features/card_swiper/src/allowed_swipe_direction.dart'; import '../../features/card_swiper/src/allowed_swipe_direction.dart';
import '../../features/card_swiper/src/card_swiper.dart'; import '../../features/card_swiper/src/card_swiper.dart';
import '../../features/card_swiper/src/card_swiper_controller.dart'; import '../../features/card_swiper/src/card_swiper_controller.dart';
import '../../features/card_swiper/src/enums.dart';
import '../../main.dart'; import '../../main.dart';
import '../game_card_widget.dart'; import '../game_card_widget.dart';
@ -84,9 +85,7 @@ class CardGameMainWidget extends StatelessWidget {
cardsCount: length, cardsCount: length,
numberOfCardsDisplayed: numberOfCardsDisplayed, numberOfCardsDisplayed: numberOfCardsDisplayed,
allowedSwipeDirection: allowedSwipeDirection:
AllowedSwipeDirection.symmetric( AllowedSwipeDirection.all(),
horizontal: true,
),
scale: initialScale, scale: initialScale,
backCardOffset: Offset(0, cardHeight * 0.1 / 2), backCardOffset: Offset(0, cardHeight * 0.1 / 2),
isLoop: false, isLoop: false,

View file

@ -52,9 +52,9 @@ class _CardGamePageState extends State<CardGamePage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// todo move to controller // todo move to controller
final timer = Stream.periodic(Duration(seconds: 1), (tick) { // final timer = Stream.periodic(Duration(seconds: 1), (tick) {
return Duration(seconds: tick + 1); // return Duration(seconds: tick + 1);
}).asBroadcastStream().take(3600).startWith(Duration.zero); // }).asBroadcastStream().take(3600).startWith(Duration.zero);
return Scaffold( return Scaffold(
body: PopScope( body: PopScope(
@ -96,7 +96,6 @@ class _CardGamePageState extends State<CardGamePage> {
), ),
shuffledCards.length, shuffledCards.length,
widget.color ?? borderGray, widget.color ?? borderGray,
timer: timer,
popText: widget.infinite ? 'к теме' : null, popText: widget.infinite ? 'к теме' : null,
key: ValueKey('CARDS PROGRESS'), key: ValueKey('CARDS PROGRESS'),
); );

View file

@ -4,8 +4,12 @@ import 'package:collection/collection.dart';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:mnemo_cards/main.dart';
import 'package:mnemo_cards/utils/iterable_helper.dart';
import 'package:mnemo_cards/widgets/fade_in_widget.dart'; import 'package:mnemo_cards/widgets/fade_in_widget.dart';
import 'package:mnemo_cards/widgets/feedback_builder.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart'; import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:reorderables/reorderables.dart';
import 'package:yandex_mobileads/mobile_ads.dart'; import 'package:yandex_mobileads/mobile_ads.dart';
import '../../domain/router/app_router.gr.dart'; import '../../domain/router/app_router.gr.dart';
import '../../features/analytics/analytics.dart'; import '../../features/analytics/analytics.dart';
@ -21,6 +25,7 @@ import 'cards_view.dart';
import 'cards_view_controller.dart'; import 'cards_view_controller.dart';
final _mainScrollController = ScrollController(); final _mainScrollController = ScrollController();
final _reorderableTests = GlobalKey();
class AvailablePack extends StatelessWidget { class AvailablePack extends StatelessWidget {
final CardPackDto cardPack; final CardPackDto cardPack;
@ -65,24 +70,20 @@ class AvailablePack extends StatelessWidget {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
StreamBuilder( StreamBuilder<bool>(
stream: _cardsViewController.asStream, stream: _cardsViewController.asStream
.map((s) => s.expanded),
builder: (context, snapshot) { builder: (context, snapshot) {
return AnimatedRotation( return _DownButton(
duration: Duration(milliseconds: 300), !(snapshot.data ?? false),
turns: (snapshot.data?.expanded ?? false) () async {
? 0.5 if (snapshot.hasData) {
: 0.0, _cardsViewController.setExpanded(
child: _Button( !snapshot.requireData,
'down.png', );
() async { }
if (snapshot.hasData) { },
_cardsViewController.setExpanded( key: ValueKey('DownButton'),
!snapshot.requireData.expanded,
);
}
},
),
); );
}), }),
_Button('shuffle.png', () async { _Button('shuffle.png', () async {
@ -168,16 +169,78 @@ class _TestsSection extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final order = (globalSharedPreferences
.getString('tests_order_${packId}')
?.decodeList<String>() ??
<String>[])
.toList();
void saveOrder() {
globalSharedPreferences.setString(
'tests_order_${packId}',
order.encode(),
);
}
return StreamBuilder( return StreamBuilder(
stream: locator.testManager.loadTestsStream(packId: packId), stream: locator.testManager.loadTestsStream(packId: packId),
builder: (context, snapshot) { builder: (context, snapshot) {
return Column( final tests = snapshot.data ?? [];
children: [ if (tests.isEmpty) {
if (snapshot.hasData) return SizedBox.shrink();
...snapshot.data!.mapIndexed( }
(int index, TestDto dto) => FadeInWidget(
delay: Duration(milliseconds: index * 200), if (order.isEmpty) {
animationDuration: const Duration(milliseconds: 200), order.addAll(tests.map((t) => t.name));
saveOrder();
}
return FadeInWidget(
animationDuration: Duration(milliseconds: 400),
child: StatefulBuilder(builder: (context, setState) {
tests..sortInOrder(order, (t) => t.name);
return ReorderableListView.builder(
key: _reorderableTests,
itemExtent: 130.h + 2 * 5.h,
padding: EdgeInsets.symmetric(
horizontal: 10.w,
vertical: 5.h,
),
shrinkWrap: true,
scrollController: _mainScrollController,
// physics: NeverScrollableScrollPhysics(),
proxyDecorator: (_, index, animation) {
final dto = tests[index];
return Material(
elevation: 5,
borderRadius: BorderRadius.circular(16),
color: Colors.transparent,
child: _TestButton(
title: dto.name,
subtitle: null,
stats: dto.statistics,
color: dto.color?.asColor ?? borderGray,
time: dto.time,
timeSubtitle: dto.timeSubtitle,
onTap: () {},
),
);
},
onReorder: (int from, int to) {
if (to - from > 1) {
to--;
}
order.move(from, to);
saveOrder();
setState(() {});
},
itemCount: tests.length,
itemBuilder: (context, index) {
final dto = tests[index];
return Padding(
key: ValueKey(dto.id),
padding: EdgeInsets.symmetric(
vertical: 5.0.h,
),
child: _TestButton( child: _TestButton(
title: dto.name, title: dto.name,
subtitle: null, subtitle: null,
@ -192,9 +255,10 @@ class _TestsSection extends StatelessWidget {
); );
}, },
), ),
), );
), },
], );
}),
); );
}, },
); );
@ -261,6 +325,40 @@ class _Button extends StatelessWidget {
} }
} }
class _DownButton extends StatelessWidget {
final VoidCallback onTap;
final bool isDown;
_DownButton(bool this.isDown, this.onTap, {super.key});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Container(
width: 110.w,
height: 60.h,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: borderGray),
borderRadius: BorderRadius.circular(16.0),
),
alignment: Alignment.center,
child: AnimatedRotation(
duration: Duration(milliseconds: 300),
turns: isDown ? 0.0 : 0.5,
child: Image.asset(
'icons/down.png',
width: 29.w,
// height: 29.h,
fit: BoxFit.scaleDown,
),
),
),
);
}
}
class _TestButton extends StatelessWidget { class _TestButton extends StatelessWidget {
final VoidCallback onTap; final VoidCallback onTap;
final String title; final String title;
@ -283,99 +381,94 @@ class _TestButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Padding( return GestureDetector(
padding: EdgeInsets.symmetric( onTap: onTap,
vertical: 5.0.h, child: Container(
horizontal: 10.w, height: 130.h,
), alignment: Alignment.center,
child: InkWell( decoration: BoxDecoration(
onTap: onTap, color: Colors.white,
child: Container( border: Border.all(color: borderGray),
height: 120.h, borderRadius: BorderRadius.circular(16.0),
decoration: BoxDecoration( ),
color: Colors.white, padding: EdgeInsets.only(
border: Border.all(color: borderGray), top: 12.0.h,
borderRadius: BorderRadius.circular(16.0), left: 10.0.w,
), right: 10.0.w,
padding: EdgeInsets.only( bottom: 20.h,
top: 12.0.h, ),
left: 10.0.w, child: Row(
right: 10.0.w, children: [
bottom: 20.h, Expanded(
), child: Column(
child: Row( crossAxisAlignment: CrossAxisAlignment.start,
children: [ mainAxisAlignment: MainAxisAlignment.spaceBetween,
Expanded( children: [
child: Column( Text(
crossAxisAlignment: CrossAxisAlignment.start, title,
mainAxisAlignment: MainAxisAlignment.spaceBetween, style: TextStyle(
children: [ fontSize: 25.sp,
Text( fontWeight: FontWeight.w500,
title, height: 0.85,
style: TextStyle( ),
fontSize: 25.sp, ),
fontWeight: FontWeight.w500, if (subtitle != null)
height: 0.85, Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
subtitle!,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w300,
height: 0.85,
),
), ),
), ),
if (subtitle != null) Spacer(),
Padding( if (stats != null)
padding: const EdgeInsets.only(top: 8.0), Padding(
child: Text( padding: const EdgeInsets.only(right: 50.0),
subtitle!, child: Row(
style: TextStyle( children: [
fontSize: 16.sp, Expanded(
fontWeight: FontWeight.w300, child: HorizontalProgressWidget(
height: 0.85, stats!.words.incorrect.round(),
), stats!.words.total.round(),
), color,
), hasBorder: true,
Spacer(),
if (stats != null)
Padding(
padding: const EdgeInsets.only(right: 50.0),
child: Row(
children: [
Expanded(
child: HorizontalProgressWidget(
stats!.words.incorrect.round(),
stats!.words.total.round(),
color,
hasBorder: true,
),
), ),
], ),
), ],
), ),
], ),
), ],
), ),
Container( ),
width: 75.w, Container(
child: Column( width: 75.w,
mainAxisAlignment: MainAxisAlignment.center, child: Column(
children: [ mainAxisAlignment: MainAxisAlignment.center,
if (time != null) children: [
Text( if (time != null)
time!, Text(
style: TextStyle( time!,
fontSize: 40.sp, style: TextStyle(
fontWeight: FontWeight.w400, fontSize: 40.sp,
height: 0.85), fontWeight: FontWeight.w400,
), height: 0.85),
if (timeSubtitle != null) ),
Text( if (timeSubtitle != null)
timeSubtitle!, Text(
style: TextStyle( timeSubtitle!,
fontSize: 24.sp, style: TextStyle(
fontWeight: FontWeight.w400, fontSize: 24.sp,
height: 0.85), fontWeight: FontWeight.w400,
), height: 0.85),
], ),
), ],
), ),
], ),
), ],
), ),
), ),
); );

View file

@ -1,23 +1,27 @@
import 'dart:async'; import 'dart:async';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:mnemo_cards/features/packs/images_holder.dart'; import 'package:mnemo_cards/features/packs/images_holder.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart'; import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:reorderables/reorderables.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import '../../di/locator.dart'; import '../../di/locator.dart';
import '../../utils/string_helper.dart'; import '../../utils/string_helper.dart';
import '../../domain/router/app_router.gr.dart'; import '../../domain/router/app_router.gr.dart';
import '../../main.dart'; import '../../main.dart';
import '../feedback_builder.dart';
import '../game_card_widget.dart'; import '../game_card_widget.dart';
import 'cards_view_controller.dart'; import 'cards_view_controller.dart';
class CardsView extends StatefulWidget { class CardsView extends StatefulWidget {
final CardPackDto cardPack; final CardPackDto cardPack;
final CardsViewController controller; final CardsViewController controller;
final List<int> cardsOrder = [];
CardsView(this.controller, this.cardPack); CardsView(this.controller, this.cardPack);
@ -150,34 +154,42 @@ class _CardsViewState extends State<CardsView>
((packCards.length / 3).ceil() * 4 / 3 * cardWidth - ((packCards.length / 3).ceil() * 4 / 3 * cardWidth -
cardWidth * 4 / 3) * cardWidth * 4 / 3) *
animation.value, animation.value,
child: Wrap( child: Builder(builder: (context) {
key: wrapKey, return ReorderableWrap(
children: List.generate(packCards.length, (index) { key: wrapKey,
final card = packCards[index]; onReorder: (int from, int to) {
final memoryImage = card.memoryImage; controller.onOrderChanged(from, to);
if (memoryImage == null) { },
return SizedBox(); buildDraggableFeedback: FeedbackBuilder.simple,
} padding: EdgeInsets.symmetric(
return Container( horizontal: (cardWidth - imageWidth - 1),
height: cardWidth * 4 / 3, vertical: (cardWidth - imageWidth - 1),
width: cardWidth, ),
padding: EdgeInsets.symmetric( runSpacing: (cardWidth - imageWidth - 1) * 2,
horizontal: cardWidth - imageWidth - 1, spacing: (cardWidth - imageWidth - 1) * 2,
vertical: cardWidth - imageWidth - 1, children: List.generate(packCards.length, (index) {
), final card = packCards[index];
alignment: Alignment.topCenter, final memoryImage = card.memoryImage;
child: Container( if (memoryImage == null) {
height: imageWidth * 4 / 3, return SizedBox();
child: _CardBuilder( }
cardPack, return Container(
card, height: 2 * imageWidth - cardWidth * 2 / 3 + 2,
memoryImage, width: 2 * imageWidth - cardWidth + 2,
(card) => onCardTap(packCards, card), alignment: Alignment.topCenter,
child: Container(
height: imageWidth * 4 / 3,
child: _CardBuilder(
cardPack,
card,
memoryImage,
(card) => onCardTap(packCards, card),
),
), ),
), );
); }),
}), );
), }),
), ),
), ),
], ],

View file

@ -3,6 +3,8 @@ import 'dart:math';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:mnemo_cards/features/packs/images_holder.dart'; import 'package:mnemo_cards/features/packs/images_holder.dart';
import 'package:mnemo_cards/main.dart';
import 'package:mnemo_cards/utils/iterable_helper.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart'; import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
@ -13,6 +15,10 @@ class CardsViewController extends ChangeNotifier {
bool _favorite = false; bool _favorite = false;
double imageWidth = 0.0; double imageWidth = 0.0;
List<GameCardDto> cards = []; List<GameCardDto> cards = [];
List<int> allCardsOrder = [];
List<int> favoriteCardsOrder = [];
List<int> get cardsOrder => _favorite ? favoriteCardsOrder : allCardsOrder;
final CardPackDto cardPack; final CardPackDto cardPack;
@ -57,6 +63,61 @@ class CardsViewController extends ChangeNotifier {
} }
} }
Future<void> _saveCardsOrder() async {
if (_favorite) {
await globalSharedPreferences.setString(
'sort_fav_${cardPack.id}',
favoriteCardsOrder.encode(),
);
} else {
await globalSharedPreferences.setString(
'sort_${cardPack.id}',
allCardsOrder.encode(),
);
}
}
void _loadCardsOrder() {
favoriteCardsOrder = globalSharedPreferences
.getString(
'sort_fav_${cardPack.id}',
)
?.decodeList<int>() ??
[];
allCardsOrder = globalSharedPreferences
.getString(
'sort_${cardPack.id}',
)
?.decodeList<int>() ??
[];
if (allCardsOrder.isEmpty) {
allCardsOrder = cardPack.cards.map((c) => c.id).toList();
}
if (favoriteCardsOrder.isEmpty) {
favoriteCardsOrder = allCardsOrder.toList();
}
}
void setCardsOrder(List<int> order) {
if (_favorite) {
favoriteCardsOrder = order.toList();
} else {
allCardsOrder = order.toList();
}
_saveCardsOrder();
_updateCards();
}
void onOrderChanged(int from, int to) {
if (_favorite) {
favoriteCardsOrder.move(from, to);
} else {
allCardsOrder.move(from, to);
}
_saveCardsOrder();
_updateCards();
}
void setFavorite(bool favorite) { void setFavorite(bool favorite) {
_favorite = favorite; _favorite = favorite;
_updateCards(); _updateCards();
@ -75,11 +136,23 @@ class CardsViewController extends ChangeNotifier {
CardsViewController(this.cardPack) { CardsViewController(this.cardPack) {
addListener(_addToStream); addListener(_addToStream);
_loadCardsOrder();
_updateCards(); _updateCards();
} }
void _updateCards() { void _updateCards() {
cards = _filterPackCards(); cards = _filterPackCards();
cards
..sort((p, n) {
final pIndex = cardsOrder.indexOf(p.id);
final nIndex = cardsOrder.indexOf(n.id);
if (pIndex == -1) {
return nIndex == -1 ? p.id.compareTo(n.id) : 1;
} else if (nIndex == -1) {
return -1;
}
return pIndex.compareTo(nIndex);
});
notifyListeners(); notifyListeners();
} }

View file

@ -11,6 +11,7 @@ class FadeInWidget extends StatelessWidget {
this.delay = const Duration(milliseconds: 200), this.delay = const Duration(milliseconds: 200),
this.future, this.future,
this.animationDuration = const Duration(milliseconds: 200), this.animationDuration = const Duration(milliseconds: 200),
super.key,
}) : assert(future != null || delay != null); }) : assert(future != null || delay != null);
@override @override

View file

@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
class FeedbackBuilder {
static Widget tests(Widget child, int index, Animation<double> animation) =>
Transform(
transform: new Matrix4.rotationZ(0),
child: Material(
child: child,
elevation: 1.0,
color: Colors.transparent,
borderRadius: BorderRadius.circular(16.0),
),
);
static Widget simple(
BuildContext context,
BoxConstraints constraints,
Widget child,
) {
return Transform(
transform: new Matrix4.rotationZ(0.05),
// alignment: FractionalOffset.topLeft,
child: Material(
child: child,
elevation: 6.0,
color: Colors.transparent,
borderRadius: BorderRadius.circular(12.0),
),
);
}
}

View file

@ -168,6 +168,7 @@ class _PackButton4 extends StatelessWidget {
), ),
); );
}, },
borderRadius: BorderRadius.circular(12.0),
child: Container( child: Container(
margin: EdgeInsets.all(4.0.h), margin: EdgeInsets.all(4.0.h),
alignment: Alignment.center, alignment: Alignment.center,