Flutter实现选项点击滑动填充语法测验空缺需求及代码
Hey there! Let's tweak your existing code to swap the drag-and-drop interaction for a click-to-slide animation. Here's a step-by-step implementation that achieves exactly what you're looking for:
Step 1: Add Animation Variables to Your State
First, we'll need animation controllers and tracking variables to handle the slide motion. Update your _QuestionsScreenState like this:
class _QuestionsScreenState extends State<QuestionsScreen> with SingleTickerProviderStateMixin { String userAnswer = "_"; int indexOfDragPlace = QuestionBrain.getQuesitonText().indexOf("_"); // Animation-related variables AnimationController? _animationController; Animation<Offset>? _slideAnimation; String? _selectedChoice; bool _isAnimating = false; final GlobalKey _choiceKey = GlobalKey(); // Tracks the tapped choice's position final GlobalKey _targetKey = GlobalKey(); // Tracks the blank space's position @override void initState() { super.initState(); _animationController = AnimationController( duration: const Duration(milliseconds: 500), // Adjust speed as needed vsync: this, ); } @override void dispose() { _animationController?.dispose(); super.dispose(); }
Step 2: Add the Click Handler & Animation Logic
Next, create a function to handle the choice tap, calculate the slide path, and trigger the animation:
void _onChoiceTap(String choice) { if (_isAnimating) return; // Prevent duplicate taps during animation setState(() { _selectedChoice = choice; _isAnimating = true; }); // Get position data for the tapped choice and target blank space final choiceRenderBox = _choiceKey.currentContext?.findRenderObject() as RenderBox?; final targetRenderBox = _targetKey.currentContext?.findRenderObject() as RenderBox?; if (choiceRenderBox == null || targetRenderBox == null) return; final choiceScreenPos = choiceRenderBox.localToGlobal(Offset.zero); final targetScreenPos = targetRenderBox.localToGlobal(Offset.zero); // Calculate how far the choice needs to slide final slideDx = targetScreenPos.dx - choiceScreenPos.dx; final slideDy = targetScreenPos.dy - choiceScreenPos.dy; // Define the slide animation _slideAnimation = Tween<Offset>( begin: Offset.zero, end: Offset( slideDx / choiceRenderBox.size.width, slideDy / choiceRenderBox.size.height, ), ).animate(CurvedAnimation( parent: _animationController!, curve: Curves.easeInOut, // Smooth easing curve )); // Start the animation, then update state once it finishes _animationController!.forward().then((_) { setState(() { userAnswer = choice; _isAnimating = false; _selectedChoice = null; }); _animationController!.reset(); }); }
Step 3: Update the Target Blank Space
Modify the _buildDragTarget method to add the _targetKey (we can also remove the DragTarget logic since we're no longer using drag-and-drop):
Widget _buildDragTarget() { return Material( key: _targetKey, child: Container( padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), width: MediaQuery.of(context).size.width * 0.20, height: MediaQuery.of(context).size.height * 0.05, color: userAnswer != "_" ? Colors.orange : Colors.grey, child: FittedBox( child: Text( userAnswer, style: TextStyle( fontSize: 12, color: Colors.white, fontWeight: FontWeight.bold), ), ), ), ); }
Step 4: Replace Draggable Choices with Clickable Animated Widgets
Update the choices Wrap to use GestureDetector instead of DraggableChoiceBox, and add the slide animation for the tapped choice:
Wrap( children: [ ...QuestionBrain.choices.map((choice) { if (choice == userAnswer) { return ChoiceHolder( choice: "", backGroundColor: Colors.black12, ); } // Show animated slide for the selected choice during animation if (_isAnimating && _selectedChoice == choice) { return SlideTransition( position: _slideAnimation!, child: ChoiceHolder( key: _choiceKey, choice: choice, ), ); } // Regular clickable choice for non-selected/non-animating states return GestureDetector( key: _choiceKey, onTap: () => _onChoiceTap(choice), child: ChoiceHolder( choice: choice, ), ); }).toList() ], ),
Key Notes & Improvements
- Animation Speed: Adjust the
DurationinAnimationControllerto make the slide faster or slower. - Multiple Blanks: If your app supports multiple blank spaces, you'll need to extend the logic to track which blank is being filled (e.g., using a list of target keys and positions).
- Scroll Handling: If your question text is in a scrollable area, you'll need to adjust the position calculation to account for scroll offset (use
Scrollable.of(context).position.pixelsto adjust the screen positions). - ChoiceHolder Consistency: Ensure your
ChoiceHolderwidget has a fixed size so the animation calculates correctly.
内容的提问来源于stack exchange,提问作者Ramy Gomaa

