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

Flutter中Toggle Switch切换动画失效问题求助

问题解决:Flutter ToggleSwitch切换动画失效问题

问题原因

你遇到的核心问题是:调用setState会触发整个CardScreen组件重建,导致第三方库的ToggleSwitch被重新初始化,直接打断了它内部的切换动画逻辑。

解决方案

下面提供两种可行的解决方式,核心思路都是避免ToggleSwitch随父组件重建,只更新需要切换的内容区域:

方式一:用ValueNotifier局部更新内容

这种方法不需要修改组件结构,通过ValueNotifier管理状态,只让内容区域响应状态变化,ToggleSwitch完全不受影响:

import 'package:flutter/material.dart';
import 'package:sadapay_clone/screens/homepage.dart';
import 'package:sadapay_clone/widgets/physical_card_item.dart';
import 'package:sadapay_clone/widgets/virtual_card_item.dart';
import 'package:toggle_switch/toggle_switch.dart';

class CardScreen extends StatefulWidget {
  const CardScreen({super.key});

  @override
  State<CardScreen> createState() => _CardScreenState();
}

class _CardScreenState extends State<CardScreen> {
  // 用ValueNotifier替代普通bool,实现局部状态更新
  final ValueNotifier<bool> _showVirtualCard = ValueNotifier(true);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          const SizedBox(height: 75),
          SizedBox(
            width: double.infinity,
            child: Row(
              children: [
                IconButton(
                  onPressed: () {
                    // 优化返回逻辑,直接pop即可
                    Navigator.pop(context);
                  },
                  icon: const Icon(Icons.arrow_back_ios),
                ),
                Container(
                  width: 295,
                  alignment: Alignment.center,
                  child: const Text(
                    'My Cards',
                    style: TextStyle(
                      fontSize: 20,
                      fontWeight: FontWeight.w500,
                    ),
                    textAlign: TextAlign.center,
                  ),
                ),
              ],
            ),
          ),
          const SizedBox(height: 20),
          Container(
            alignment: Alignment.center,
            height: 40,
            width: 365,
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(20),
              color: Colors.grey[300],
            ),
            child: ToggleSwitch(
              minHeight: 30,
              minWidth: 180.0,
              cornerRadius: 20.0,
              activeBgColors: const [
                [Colors.white],
                [Colors.white]
              ],
              activeFgColor: Colors.black,
              inactiveBgColor: Colors.grey[300],
              inactiveFgColor: Colors.black54,
              initialLabelIndex: 0,
              totalSwitches: 2,
              labels: const ['Virtual', 'Physical'],
              radiusStyle: true,
              onToggle: (index) {
                // 直接更新ValueNotifier,不调用setState
                _showVirtualCard.value = index == 0;
              },
            ),
          ),
          // 仅内容区域响应状态变化,重建范围最小化
          ValueListenableBuilder<bool>(
            valueListenable: _showVirtualCard,
            builder: (context, showVirtual, _) {
              return showVirtual ? VirtualCard() : PhysicalCard();
            },
          ),
        ],
      ),
    );
  }
}

方式二:封装ToggleSwitch为独立组件

把ToggleSwitch抽成单独的无状态组件,让它自己维护内部动画状态,父组件只通过回调获取选中状态:

  1. 先创建封装组件:
class CardToggleSwitch extends StatelessWidget {
  final bool initialSelectVirtual;
  final Function(bool isVirtual) onSwitchChanged;

  const CardToggleSwitch({
    super.key,
    required this.initialSelectVirtual,
    required this.onSwitchChanged,
  });

  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      height: 40,
      width: 365,
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(20),
        color: Colors.grey[300],
      ),
      child: ToggleSwitch(
        minHeight: 30,
        minWidth: 180.0,
        cornerRadius: 20.0,
        activeBgColors: const [
          [Colors.white],
          [Colors.white]
        ],
        activeFgColor: Colors.black,
        inactiveBgColor: Colors.grey[300],
        inactiveFgColor: Colors.black54,
        initialLabelIndex: initialSelectVirtual ? 0 : 1,
        totalSwitches: 2,
        labels: const ['Virtual', 'Physical'],
        radiusStyle: true,
        onToggle: (index) {
          onSwitchChanged(index == 0);
        },
      ),
    );
  }
}
  1. 修改原CardScreen:
class CardScreen extends StatefulWidget {
  const CardScreen({super.key});

  @override
  State<CardScreen> createState() => _CardScreenState();
}

class _CardScreenState extends State<CardScreen> {
  bool _showVirtualCard = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          const SizedBox(height: 75),
          SizedBox(
            width: double.infinity,
            child: Row(
              children: [
                IconButton(
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  icon: const Icon(Icons.arrow_back_ios),
                ),
                Container(
                  width: 295,
                  alignment: Alignment.center,
                  child: const Text(
                    'My Cards',
                    style: TextStyle(
                      fontSize: 20,
                      fontWeight: FontWeight.w500,
                    ),
                    textAlign: TextAlign.center,
                  ),
                ),
              ],
            ),
          ),
          const SizedBox(height: 20),
          // 使用封装后的组件,父组件setState不会影响它的内部动画
          CardToggleSwitch(
            initialSelectVirtual: _showVirtualCard,
            onSwitchChanged: (isVirtual) {
              setState(() {
                _showVirtualCard = isVirtual;
              });
            },
          ),
          _showVirtualCard ? VirtualCard() : PhysicalCard(),
        ],
      ),
    );
  }
}

内容的提问来源于stack exchange,提问作者Ahmad Maaz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 09:35:21