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

Flutter自定义开关联动异常求助:如何实现互斥切换?

Flutter自定义Switch互斥切换UI不同步问题解决

问题场景

使用两个自定义CustomSwitch实现互斥逻辑:切换其中一个开关时,另一个自动切换状态,确保始终只有一个开关处于开启状态。目前变量值已能正确更新,但开关的UI状态无法同步变化,需要在保留自定义Switch的前提下修复该问题。

问题原因

自定义CustomSwitch的内部动画状态仅在initState中初始化,当父组件传递的value属性发生变化时,内部未更新动画的对齐状态和控制器状态,导致UI无法跟随外部变量同步更新。

解决步骤

在_CustomSwitchState类中添加didUpdateWidget方法,当外部传入的value变化时,重新配置动画的起始/结束对齐值,并调整动画控制器的状态,确保UI与外部值同步。

修改后的CustomSwitch代码

library custom_switch;

import 'package:flutter/material.dart';

class CustomSwitch extends StatefulWidget {
  final bool value;
  final ValueChanged<bool> onChanged;
  final Color activeColor;
  final Color inactiveColor;
  final String activeText;
  final String inactiveText;
  final Color activeTextColor;
  final Color inactiveTextColor;

  const CustomSwitch({
    required this.value,
    required this.onChanged,
    required this.activeColor,
    this.inactiveColor = Colors.grey,
    this.activeText = '',
    this.inactiveText = '',
    this.activeTextColor = Colors.white70,
    this.inactiveTextColor = Colors.white70,
  });

  @override
  _CustomSwitchState createState() => _CustomSwitchState();
}

class _CustomSwitchState extends State<CustomSwitch>
    with SingleTickerProviderStateMixin {
  late Animation _circleAnimation;
  late AnimationController _animationController;

  @override
  void initState() {
    super.initState();
    _animationController =
        AnimationController(vsync: this, duration: const Duration(milliseconds: 60));
    _circleAnimation = AlignmentTween(
      begin: widget.value ? Alignment.centerRight : Alignment.centerLeft,
      end: widget.value ? Alignment.centerLeft : Alignment.centerRight,
    ).animate(CurvedAnimation(
      parent: _animationController,
      curve: Curves.linear,
    ));
    // 初始化时根据value设置动画状态
    if (widget.value) {
      _animationController.forward();
    } else {
      _animationController.reverse();
    }
  }

  @override
  void didUpdateWidget(covariant CustomSwitch oldWidget) {
    super.didUpdateWidget(oldWidget);
    // 当外部value变化时更新动画
    if (oldWidget.value != widget.value) {
      // 重新创建动画tween
      _circleAnimation = AlignmentTween(
        begin: widget.value ? Alignment.centerRight : Alignment.centerLeft,
        end: widget.value ? Alignment.centerLeft : Alignment.centerRight,
      ).animate(CurvedAnimation(
        parent: _animationController,
        curve: Curves.linear,
      ));
      // 根据新value调整动画控制器状态
      if (widget.value) {
        _animationController.forward();
      } else {
        _animationController.reverse();
      }
    }
  }

  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _animationController,
      builder: (context, child) {
        return GestureDetector(
          onTap: () {
            if (_animationController.isCompleted) {
              _animationController.reverse();
            } else {
              _animationController.forward();
            }
            widget.onChanged(!widget.value);
          },
          child: Container(
            width: 70.0,
            height: 35.0,
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(20.0),
              color: _circleAnimation.value == Alignment.centerLeft
                  ? const Color(0xffD4D4D4)
                  : null,
              gradient: _circleAnimation.value == Alignment.centerRight
                  ? const LinearGradient(
                      begin: Alignment.topCenter,
                      end: Alignment.bottomCenter,
                      colors: [
                        Color(0xffFF5A5A),
                        Color(0x505AB0FF),
                      ],
                    )
                  : null,
            ),
            child: Padding(
              padding: const EdgeInsets.only(
                top: 4.0,
                bottom: 4.0,
                right: 4.0,
                left: 4.0,
              ),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: <Widget>[
                  _circleAnimation.value == Alignment.centerRight
                      ? Padding(
                          padding: const EdgeInsets.only(left: 34.0, right: 0),
                          child: Text(
                            widget.activeText,
                            style: TextStyle(
                              color: widget.activeTextColor,
                              fontWeight: FontWeight.w900,
                              fontSize: 16.0,
                            ),
                          ),
                        )
                      : Container(),
                  Align(
                    alignment: _circleAnimation.value,
                    child: Container(
                      width: 25.0,
                      height: 25.0,
                      decoration: const BoxDecoration(
                        shape: BoxShape.circle,
                        color: Colors.white,
                      ),
                    ),
                  ),
                  _circleAnimation.value == Alignment.centerLeft
                      ? Padding(
                          padding: const EdgeInsets.only(left: 0, right: 34.0),
                          child: Text(
                            widget.inactiveText,
                            style: TextStyle(
                              color: widget.inactiveTextColor,
                              fontWeight: FontWeight.w900,
                              fontSize: 16.0,
                            ),
                          ),
                        )
                      : Container(),
                ],
              ),
            ),
          ),
        );
      },
    );
  }
}

说明

原页面的代码无需修改,仅修改自定义CustomSwitch的实现即可。修改后,当父组件通过setState更新_Location或OutAndAbout变量时,自定义Switch会通过didUpdateWidget感知到value的变化,自动调整内部动画状态,实现UI与变量值的同步。

内容的提问来源于stack exchange,提问作者Umer Yaseen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 15:12:34