Почему мой виджет сохраняет состояние, когда я прокручиваю карты? ⇐ Android
-
Anonymous
Почему мой виджет сохраняет состояние, когда я прокручиваю карты?
Scrolling through the cards requires the additional information to be closed again. how do I accomplish this?
I tried using a setter and tried adding a callback, it didn't help, maybe I was doing something wrong?
I am using flutter_card_swiper to scroll through user cards Tinder style. I generate a list of users using candidates = List.generate and a list of cards using cards = candidates.map(ExampleCard.new).toList() and then pass them to CardSwiper. ExampleCard itself has an AnimatedContainer that expands or contracts depending on the value of _isExpanded. If I expand the container and switch the card, the container will jump, that is, contract first and then expand, and I need the container to stay contract and not expand until the user explicitly expands it.
This is full code:
import 'dart:math'; import 'package:brew_mate/core/models/person.dart'; import 'package:flutter/material.dart'; import 'package:flutter_card_swiper/flutter_card_swiper.dart'; class SwipeCardsWidget extends StatefulWidget { const SwipeCardsWidget({super.key}); @override State createState() => _SwipeCardsWidgetState(); } class _SwipeCardsWidgetState extends State { final CardSwiperController controller = CardSwiperController(); final cards = candidates.map(ExampleCard.new).toList(); @override void dispose() { controller.dispose(); super.dispose(); } bool _onSwipe( int previousIndex, int? currentIndex, CardSwiperDirection direction, ) { debugPrint( 'The card $previousIndex was swiped to the ${direction.name}. Now the card $currentIndex is on top', ); return true; } bool _onUndo( int? previousIndex, int currentIndex, CardSwiperDirection direction, ) { debugPrint( 'The card $currentIndex was undod from the ${direction.name}', ); return true; } @override Widget build(BuildContext context) { return Scaffold( body: SafeArea( child: Column( children: [ Flexible( child: CardSwiper( allowedSwipeDirection: const AllowedSwipeDirection.only(left: true, right: true ), controller: controller, cardsCount: cards.length, onSwipe: _onSwipe, onUndo: _onUndo, numberOfCardsDisplayed: 3, backCardOffset: const Offset(40, 40), padding: const EdgeInsets.all(24.0), cardBuilder: ( context, index, horizontalThresholdPercentage, verticalThresholdPercentage, ) => cards[index], ), ), Padding( padding: const EdgeInsets.all(16.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ FloatingActionButton( onPressed: controller.undo, child: const Icon(Icons.rotate_left), ), FloatingActionButton( onPressed: () => controller.swipe(CardSwiperDirection.left), child: const Icon(Icons.keyboard_arrow_left), ), FloatingActionButton( onPressed: () => controller.swipe(CardSwiperDirection.right), child: const Icon(Icons.keyboard_arrow_right), ), FloatingActionButton( onPressed: () => controller.swipe(CardSwiperDirection.top), child: const Icon(Icons.keyboard_arrow_up), ), FloatingActionButton( onPressed: () => controller.swipe(CardSwiperDirection.bottom), child: const Icon(Icons.keyboard_arrow_down), ), ], ), ), ], ), ), ); } } class MyLinePainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = Colors.black ..strokeWidth = 3.0 ..strokeCap = StrokeCap.round; final start = Offset(0, size.height / 2); final end = Offset(size.width, size.height / 2); canvas.drawLine(start, end, paint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return false; } } class ExampleCard extends StatefulWidget { final Person candidate; const ExampleCard( this.candidate, { super.key, }); @override State createState() => _ExampleCardState(); } class _ExampleCardState extends State { bool _isExpanded = false; @override Widget build(BuildContext context) { return GestureDetector( onVerticalDragUpdate: (details) { if (details.delta.dy < 0) { setState(() { _isExpanded = true; }); } if (details.delta.dy > 0) { setState(() { _isExpanded = false; }); } }, child: Container( clipBehavior: Clip.hardEdge, decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(10)), color: Colors.white, boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.2), spreadRadius: 3, blurRadius: 7, offset: const Offset(0, 3), ), ], ), alignment: Alignment.center, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Flexible( child: Container( decoration: BoxDecoration( image: DecorationImage( image: NetworkImage(widget.candidate.imageUrl), fit: BoxFit.cover), gradient: const LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Color(0xFFD7E9F7), Color(0xFFD7E9F7)], ), ), ), ), AnimatedContainer( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, height: _isExpanded ? MediaQuery.of(context).size.height * 0.30 : MediaQuery.of(context).size.height * 0.15, child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Center( child: CustomPaint( painter: MyLinePainter(), size: const Size(40, 3), ), ), Text( '${widget.candidate.name}, ${widget.candidate.age}', style: const TextStyle( color: Colors.black, fontWeight: FontWeight.bold, fontSize: 20, ), ), const SizedBox(height: 5), Text( 'Preferred drink: ${widget.candidate.preferredDrink.name}', style: const TextStyle( color: Colors.grey, fontSize: 15, ), ), const SizedBox(height: 5), Text( 'City: ${widget.candidate.location}', style: const TextStyle(color: Colors.grey), ), const SizedBox(height: 10), if (_isExpanded) Expanded( child: Text( 'About me: ${widget.candidate.aboutMe}', style: const TextStyle(color: Colors.grey, fontSize: 15), ), ), ], ), ), ), ], ), ), ); } } class ExampleCandidateModel { final String name; final String job; final String city; final List color; ExampleCandidateModel({ required this.name, required this.job, required this.city, required this.color, }); } const List drinkingFrequencies = DrinkingFrequency.values; const List preferredDrinks = PreferredDrink.values; final List candidates = List.generate( 10, (index) => Person( id: index + 1, name: 'Person ${index + 1}', age: Random().nextInt(40) + 18, location: 'Location ${index + 1}', drinkingFrequency: drinkingFrequencies[Random().nextInt(drinkingFrequencies.length)], preferredDrink: preferredDrinks[Random().nextInt(preferredDrinks.length)], aboutMe: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', imageUrl: 'https://thispersondoesnotexist.com/', ), );
Источник: https://stackoverflow.com/questions/780 ... wipe-cards
Scrolling through the cards requires the additional information to be closed again. how do I accomplish this?
I tried using a setter and tried adding a callback, it didn't help, maybe I was doing something wrong?
I am using flutter_card_swiper to scroll through user cards Tinder style. I generate a list of users using candidates = List.generate and a list of cards using cards = candidates.map(ExampleCard.new).toList() and then pass them to CardSwiper. ExampleCard itself has an AnimatedContainer that expands or contracts depending on the value of _isExpanded. If I expand the container and switch the card, the container will jump, that is, contract first and then expand, and I need the container to stay contract and not expand until the user explicitly expands it.
This is full code:
import 'dart:math'; import 'package:brew_mate/core/models/person.dart'; import 'package:flutter/material.dart'; import 'package:flutter_card_swiper/flutter_card_swiper.dart'; class SwipeCardsWidget extends StatefulWidget { const SwipeCardsWidget({super.key}); @override State createState() => _SwipeCardsWidgetState(); } class _SwipeCardsWidgetState extends State { final CardSwiperController controller = CardSwiperController(); final cards = candidates.map(ExampleCard.new).toList(); @override void dispose() { controller.dispose(); super.dispose(); } bool _onSwipe( int previousIndex, int? currentIndex, CardSwiperDirection direction, ) { debugPrint( 'The card $previousIndex was swiped to the ${direction.name}. Now the card $currentIndex is on top', ); return true; } bool _onUndo( int? previousIndex, int currentIndex, CardSwiperDirection direction, ) { debugPrint( 'The card $currentIndex was undod from the ${direction.name}', ); return true; } @override Widget build(BuildContext context) { return Scaffold( body: SafeArea( child: Column( children: [ Flexible( child: CardSwiper( allowedSwipeDirection: const AllowedSwipeDirection.only(left: true, right: true ), controller: controller, cardsCount: cards.length, onSwipe: _onSwipe, onUndo: _onUndo, numberOfCardsDisplayed: 3, backCardOffset: const Offset(40, 40), padding: const EdgeInsets.all(24.0), cardBuilder: ( context, index, horizontalThresholdPercentage, verticalThresholdPercentage, ) => cards[index], ), ), Padding( padding: const EdgeInsets.all(16.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ FloatingActionButton( onPressed: controller.undo, child: const Icon(Icons.rotate_left), ), FloatingActionButton( onPressed: () => controller.swipe(CardSwiperDirection.left), child: const Icon(Icons.keyboard_arrow_left), ), FloatingActionButton( onPressed: () => controller.swipe(CardSwiperDirection.right), child: const Icon(Icons.keyboard_arrow_right), ), FloatingActionButton( onPressed: () => controller.swipe(CardSwiperDirection.top), child: const Icon(Icons.keyboard_arrow_up), ), FloatingActionButton( onPressed: () => controller.swipe(CardSwiperDirection.bottom), child: const Icon(Icons.keyboard_arrow_down), ), ], ), ), ], ), ), ); } } class MyLinePainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = Colors.black ..strokeWidth = 3.0 ..strokeCap = StrokeCap.round; final start = Offset(0, size.height / 2); final end = Offset(size.width, size.height / 2); canvas.drawLine(start, end, paint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return false; } } class ExampleCard extends StatefulWidget { final Person candidate; const ExampleCard( this.candidate, { super.key, }); @override State createState() => _ExampleCardState(); } class _ExampleCardState extends State { bool _isExpanded = false; @override Widget build(BuildContext context) { return GestureDetector( onVerticalDragUpdate: (details) { if (details.delta.dy < 0) { setState(() { _isExpanded = true; }); } if (details.delta.dy > 0) { setState(() { _isExpanded = false; }); } }, child: Container( clipBehavior: Clip.hardEdge, decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(10)), color: Colors.white, boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.2), spreadRadius: 3, blurRadius: 7, offset: const Offset(0, 3), ), ], ), alignment: Alignment.center, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Flexible( child: Container( decoration: BoxDecoration( image: DecorationImage( image: NetworkImage(widget.candidate.imageUrl), fit: BoxFit.cover), gradient: const LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Color(0xFFD7E9F7), Color(0xFFD7E9F7)], ), ), ), ), AnimatedContainer( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, height: _isExpanded ? MediaQuery.of(context).size.height * 0.30 : MediaQuery.of(context).size.height * 0.15, child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Center( child: CustomPaint( painter: MyLinePainter(), size: const Size(40, 3), ), ), Text( '${widget.candidate.name}, ${widget.candidate.age}', style: const TextStyle( color: Colors.black, fontWeight: FontWeight.bold, fontSize: 20, ), ), const SizedBox(height: 5), Text( 'Preferred drink: ${widget.candidate.preferredDrink.name}', style: const TextStyle( color: Colors.grey, fontSize: 15, ), ), const SizedBox(height: 5), Text( 'City: ${widget.candidate.location}', style: const TextStyle(color: Colors.grey), ), const SizedBox(height: 10), if (_isExpanded) Expanded( child: Text( 'About me: ${widget.candidate.aboutMe}', style: const TextStyle(color: Colors.grey, fontSize: 15), ), ), ], ), ), ), ], ), ), ); } } class ExampleCandidateModel { final String name; final String job; final String city; final List color; ExampleCandidateModel({ required this.name, required this.job, required this.city, required this.color, }); } const List drinkingFrequencies = DrinkingFrequency.values; const List preferredDrinks = PreferredDrink.values; final List candidates = List.generate( 10, (index) => Person( id: index + 1, name: 'Person ${index + 1}', age: Random().nextInt(40) + 18, location: 'Location ${index + 1}', drinkingFrequency: drinkingFrequencies[Random().nextInt(drinkingFrequencies.length)], preferredDrink: preferredDrinks[Random().nextInt(preferredDrinks.length)], aboutMe: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', imageUrl: 'https://thispersondoesnotexist.com/', ), );
Источник: https://stackoverflow.com/questions/780 ... wipe-cards
Мобильная версия