【问题标题】:Flutter multiple ExpansionTile's collapses simultaneouslyFlutter 多个 ExpansionTile\'s 同时折叠
【发布时间】:2022-11-12 18:24:17
【问题描述】:

我有一个自定义 Card 小部件,其中 ExpansionTile 作为子小部件,它根据从 API 获取的数据显示多个下拉按钮。

但是,当我使用ListView.builder 构建 N 数量的上述自定义小部件时,它们都会同时运行,例如,当我折叠 ExpansionTile 时,所有打开的 ExpansionTiles 同时折叠并重置 Dropdownbuttons 内的数据(当 ExpansionTile 折叠时重置数据预期结果,但只有折叠的 ExpansionTile 应该重置它的子下拉按钮,而不是所有打开的 ExpansionTile 子)。

这是我的建设者。

var items = ["Apartment 1", "Apartment 2", "Apartment 3", "Apartment 4"];

class MapPage extends StatelessWidget {
  const MapPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView.builder(
          key: ValueKey(items),
          scrollDirection: Axis.vertical,
          itemCount: items.length,
          padding: const EdgeInsets.only(top: 8),
          itemBuilder: (context, index) {
            return MapCard(
              building: items[index],
              floor: 4,
              key: Key(items[index].toString()),
            );
          }),
    );
  }
}

和我的CustomCard

class MapCard extends StatefulWidget {
  final String building;
  final int floor;
  const MapCard({super.key, required this.building, required this.floor});

  @override
  State<MapCard> createState() => _MapCardState();
}

class _MapCardState extends State<MapCard> {

  @override
  Widget build(BuildContext context) {
    PageStorageKey key = PageStorageKey('${widget.key}');

    return Center(
        child: Consumer<MapCardViewModel>(
      builder: (context, mapCardViewModel, child) => Card(
        color: Colors.white,
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
        child: SizedBox(
            width: MediaQuery.of(context).size.width * 0.9,
            child: Padding(
              padding: const EdgeInsets.only(bottom: 12),
              child: ExpansionTile(
                key: key,
                onExpansionChanged: (changed) {
                  if (!changed) {
                    mapCardViewModel.setAreaVisibility(false);
                    mapCardViewModel.setButtonVisibility(false);
                    mapCardViewModel.setIsFloorChosen(false);
                    mapCardViewModel.setAreaVisibility(false);
                    mapCardViewModel.area = mapCardViewModel.areas[0];
                    mapCardViewModel.floorNumber = mapCardViewModel.floors[0];
                  }
                },
                title: Row(
                  children: [
                    Container(
                        padding:
                            const EdgeInsets.only(top: 8, bottom: 8, right: 8),
                        child: Image.asset(
                          "assets/images/example.png",
                          height: 80,
                          width: 80,
                        )),
                    Flexible(
                      child: Container(
                        padding: const EdgeInsets.fromLTRB(0, 8, 8, 8),
                        child: Column(
                          children: [
                            Text("${widget.building} Apartment \n"
                                "Floor Count ${widget.floor} ")
                          ],
                        ),
                      ),
                    )
                  ],
                ),
                children: [
                  const Text("Choose Floor"),
                  Padding(
                    padding: const EdgeInsets.only(right: 24, left: 24),
                    child: DropdownButton(
                        isExpanded: true,
                        value: mapCardViewModel.isFloorChosen == false
                            ? mapCardViewModel.floors[0]
                            : mapCardViewModel.floorNumber,
                        items: mapCardViewModel.floors
                            .map<DropdownMenuItem<int>>((int i) {
                          return DropdownMenuItem<int>(
                            value: i,
                            child: Text(i.toString()),
                          );
                        }).toList(),
                        onChanged: (int? value) {
                          mapCardViewModel.setFloorNumber(value!);
                          mapCardViewModel.setIsFloorChosen(true);
                          mapCardViewModel.setAreaVisibility(true);
                        }),
                  ),
                  Visibility(
                    visible: mapCardViewModel.isAreaVisible,
                    child: Column(
                      children: [
                        const Text("Choose an Area to map"),
                        Padding(
                          padding: const EdgeInsets.only(right: 24, left: 24),
                          child: DropdownButton(
                              isExpanded: true,
                              value: mapCardViewModel.isAreaChosen == false
                                  ? mapCardViewModel.areas[0]
                                  : mapCardViewModel.area,
                              items: mapCardViewModel.areas
                                  .map<DropdownMenuItem<String>>(
                                      (String value) {
                                return DropdownMenuItem<String>(
                                  value: value,
                                  child: Text(value),
                                );
                              }).toList(),
                              onChanged: (String? value) {
                                mapCardViewModel.setArea(value!);
                                mapCardViewModel.setIsAreaChosen(true);
                                mapCardViewModel.setButtonVisibility(true);
                              }),
                        ),
                      ],
                    ),
                  ),
                  Visibility(
                    visible: mapCardViewModel.isButtonsVisible,
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                      children: [
                        TextButton(
                            onPressed: () {
                              showDialog(
                                  context: context,
                                  builder: (BuildContext context) {
                                    return CustomDialog(
                                        title: "Mapping Status",
                                        content:
                                            "This area hasn't been mapped yet",
                                        page: Container(),
                                        buttonColor: MainColors().mainBlue);
                                  });
                            },
                            child: const Text("Show Area Map")),
                        ElevatedButton(
                            onPressed: () {
                              Navigator.of(context).push(
                                MaterialPageRoute(
                                  builder: (context) => const MappedPage(),
                                ),
                              );
                            },
                            style: ElevatedButton.styleFrom(
                                backgroundColor: MainColors().mainBlue),
                            child: const Text(
                              "Map The Area",
                              style: TextStyle(color: Colors.white),
                            ))
                      ],
                    ),
                  )
                ],
              ),
            )),
      ),
    ));
  }
}

我尝试使用 StatefulWidget 为每个 ExpansionTile 和自定义 MapCard 小部件分配键,但我无法解决我的问题。

【问题讨论】:

  • 您能否在不依赖其他数据的情况下提供它的最小版本。也可以试试AutomaticKeepAliveClientMixin

标签: flutter dart


【解决方案1】:

我删除了所有第三个依赖项并尝试调整您的 MapCard 小部件的解决方案。

var items = ["Apartment 1", "Apartment 2", "Apartment 3", "Apartment 4"];
final floors = ['Floor 1', 'Floor 2'];
final areas = ['Area 1', 'Area 2'];

class MapCard extends StatefulWidget {
  final String building;
  final int floor;

  const MapCard({Key? key, required this.building, required this.floor}): super(key: key);

  @override
  State<MapCard> createState() => _MapCardState();
}

class _MapCardState extends State<MapCard> {
  @override
  Widget build(BuildContext context) {
    PageStorageKey key = PageStorageKey('${widget.key}');

    return Center(
        child: Card(
        color: Colors.white,
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
        child: SizedBox(
            width: MediaQuery.of(context).size.width * 0.9,
            child: Padding(
              padding: const EdgeInsets.only(bottom: 12),
              child: ExpansionTile(
                key: key,
                onExpansionChanged: (changed) {
                  if (!changed) {
                    print('!changed');
                  }
                },
                title: Row(
                  children: [
                    Container(
                        padding:
                            const EdgeInsets.only(top: 8, bottom: 8, right: 8),
                        child: Placeholder(
                          fallbackHeight: 80,
                          fallbackWidth: 80,
                        )),
                    Flexible(
                      child: Container(
                        padding: const EdgeInsets.fromLTRB(0, 8, 8, 8),
                        child: Column(
                          children: [
                            Text("${widget.building} Apartment 
"
                                "Floor Count ${widget.floor} ")
                          ],
                        ),
                      ),
                    )
                  ],
                ),
                children: [
                  const Text("Choose Floor"),
                  Padding(
                    padding: const EdgeInsets.only(right: 24, left: 24),
                    child: DropdownButton(
                        isExpanded: true,
                        value: floors.first,
                        items: floors
                            .map<DropdownMenuItem<String>>((String i) {
                          return DropdownMenuItem<String>(
                            value: i,
                            child: Text(i.toString()),
                          );
                        }).toList(),
                        onChanged: (String? value) {
                          // code here
                        }),
                  ),
                  Visibility(
                    visible: true,
                    child: Column(
                      children: [
                        const Text("Choose an Area to map"),
                        Padding(
                          padding: const EdgeInsets.only(right: 24, left: 24),
                          child: DropdownButton(
                              isExpanded: true,
                              value: areas.first,
                              items: areas
                                  .map<DropdownMenuItem<String>>(
                                      (String value) {
                                return DropdownMenuItem<String>(
                                  value: value,
                                  child: Text(value),
                                );
                              }).toList(),
                              onChanged: (String? value) {
                                // code here
                              }),
                        ),
                      ],
                    ),
                  ),
                  Visibility(
                    visible: true,
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                      children: [
                        TextButton(
                            onPressed: () {
                              showDialog(
                                  context: context,
                                  builder: (BuildContext context) {
                                    return AlertDialog(content: Text('Alert dialog'),);
                                  });
                            },
                            child: const Text("Show Area Map")),
                        ElevatedButton(
                            onPressed: () {
                              print('navigate');
                            },
                            child: const Text(
                              "Map The Area",
                              style: TextStyle(color: Colors.white),
                            ))
                      ],
                    ),
                  )
                ],
              ),
            )),
      ),
    );
  }
}

但我想你的问题就在于此

Consumer<MapCardViewModel>

因为每次当你改变它时,这将适用于每个创建卡。所以你必须把它放在你的ListView.builder之上,并将它作为参数传递给你的卡片

【讨论】:

    猜你喜欢
    • 2018-12-24
    • 2018-08-02
    • 2020-02-12
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-02
    相关资源
    最近更新 更多