Flutter/Rive:复选框动画显示视觉切换,但仍然无法更新状态

问题描述 投票:0回答:1

背景

我正在开发一个 Flutter 应用程序,它使用 Rive 动画 作为复选框。

虽然点击时复选框视觉上可以正确切换,但编程状态不会反映这些更改。具体来说,

submittedMissions
列表无法正确更新 - 当取消选中复选框时,它无法删除 ID,从而导致应用程序逻辑中的状态表示不准确。

问题说明

尽管取消选中 UI 中的两个框,应用程序仍然报告 2 个框已选中,这表明状态管理失败:

Incorrect state representation

相反,用 Flutter 的原生 Checkbox 小部件替换 Rive 的复选框可以解决此问题,强调 Rive 如何处理状态更改的特定问题:

Correct behavior with native Checkbox

最小可重现示例

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rive/rive.dart';

void main() => runApp(MaterialApp(home: MissionListScreen(), debugShowCheckedModeBanner: false));

class MissionListScreen extends StatefulWidget {
  @override
  _MissionListScreenState createState() => _MissionListScreenState();
}

class _MissionListScreenState extends State<MissionListScreen> {
  List<MissionItem> missions = [
    MissionItem(missionId: '1', missionName: 'Mission 1'),
    MissionItem(missionId: '2', missionName: 'Mission 2'),
    MissionItem(missionId: '3', missionName: 'Mission 3'),
  ];

  List<String> submittedMissions = [];

  @override
  void initState() {
    super.initState();
    loadRiveFiles();
  }

  void loadRiveFiles() async {
    for (var mission in missions) {
      final data = await rootBundle.load('assets/checkbox.riv');
      final file = RiveFile.import(data);
      final artboard = file.mainArtboard;
      var controller = StateMachineController.fromArtboard(artboard, 'State Machine 1');
      if (controller != null) {
        artboard.addController(controller);
        mission.checkButtonInput = controller.findInput<bool>('Tap');
        mission.checkButtonArtboard = artboard;
        mission.checkButtonInput?.value = submittedMissions.contains(mission.missionId);
      }
    }
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Text('There are ${submittedMissions.length} boxes checked.', style: TextStyle(fontSize: 20, fontWeight: FontWeight bold)),
          Expanded(
            child: ListView.builder(
              itemCount: missions.length,
              itemBuilder: (context, index) => buildMissionListTile(missions[index]),
            ),
          ),
        ],
      ),
    );
  }

  Widget buildMissionListTile(MissionItem mission) {
    return ListTile(
      title: Text(mission.missionName),
      trailing: GestureDetector(
        onTap: () => toggleMission(mission),
        child: SizedBox(height: 32, width: 32, child: Rive(artboard: mission.checkButtonArtboard!)),
      ),
    );
  }

  void toggleMission(MissionItem mission) {
    bool currentState = mission.checkButtonInput?.value ?? false;
    setState(() {
      mission.checkButtonInput?.value = !currentState;
      if (currentState) {
        submittedMissions.removeWhere((id) => id == mission.missionId);
      } else {
        if (!submittedMissions.contains(mission.missionId)) {
          submittedMissions.add(mission.missionId);
        }
      }
    });
    print("Submitted Missions: $submittedMissions"); // Debugging
  }
}

class MissionItem {
  final String missionId;
  final String missionName;
  SMIInput<bool>? checkButtonInput;
  Artboard? checkButtonArtboard;

  MissionItem({required this.missionId, required this.missionName});
}

问题

如何确保 Flutter 中由 Rive 动画触发的状态变化被正确识别和处理?

尽管有视觉指示表明复选框正在切换,但内部状态并未反映这些变化。

注释

您可以从 Rive 官方网站 或从 Google Drive 下载此 rive (.riv) 文件。

不要忘记将上述文件添加到您的资产文件夹中并更新

pubspec.yaml
文件。

flutter checkbox flutter-dependencies flutter-animation rive
1个回答
0
投票

您以错误的方式更新数据,每次点击开关时都需要更新

missions
,因为您的
buildMissionListTile
missions
一起使用。试试这个:

Widget buildMissionListTile(MissionItem mission, int index) {
    return ListTile(
      title: Text(mission.missionName),
      trailing: GestureDetector(
        onTap: () => toggleMission(mission, index),
        child: SizedBox(height: 32, width: 32, child: Rive(artboard: mission.checkButtonArtboard!)),
      ),
    );
  }

  void toggleMission(MissionItem mission,int index) {
    bool currentState = mission.checkButtonInput?.value ?? false;
    setState(() {
      
      mission.checkButtonInput?.value = !currentState;
      MissionItem newMission= mission;
      if (currentState) {
        submittedMissions.removeWhere((id) => id == mission.missionId);
        
      } else {
        if (!submittedMissions.contains(mission.missionId)) {
          submittedMissions.add(mission.missionId);
        }
      }

      missions.removeAt(index);
      missions.insert(index, newMission);
    });
    print("Submitted Missions: $submittedMissions"); // Debugging
  }

在这种方法中,我将

index
传递给
toggleMission
,并在每次
missions
有新状态时更新
mission

© www.soinside.com 2019 - 2024. All rights reserved.