You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter实现选项点击滑动填充语法测验空缺需求及代码

Solution: Replace Drag-and-Drop with Click-Triggered Slide Animation

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 Duration in AnimationController to 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.pixels to adjust the screen positions).
  • ChoiceHolder Consistency: Ensure your ChoiceHolder widget has a fixed size so the animation calculates correctly.

内容的提问来源于stack exchange,提问作者Ramy Gomaa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 11:27:43