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

如何定制Flutter ExpandableListView实现单条目展开

Solution: Single Expanded Item in Flutter Expandable List

Hey there! The problem with your current code is that every ExpandableListView manages its own local expandFlag state independently. That's why clicking a new item doesn't collapse the others—they don't know about each other's status. Let's fix this by lifting the state up to the parent Home widget, so it can track and control which single item is expanded at any time.

Key Changes:

  • Convert Home to a stateful widget to track the currently expanded item index
  • Add a callback to update the expanded index when an item is tapped
  • Remove the local expandFlag from ExpandableListView and use the parent's state to determine expansion status
  • Pass the expansion state and toggle callback to each child ExpandableListView

Here's the modified full code:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() { runApp(new MaterialApp(home: new Home())); }

// Modified: Changed to StatefulWidget to manage global expansion state
class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  // Added: Track which index is expanded (-1 means no items are open)
  int _expandedIndex = -1;

  // Added: Callback to update expanded index when an item is tapped
  void _toggleExpansion(int index) {
    setState(() {
      // If tapping the already expanded item, collapse it; else expand it
      _expandedIndex = _expandedIndex == index ? -1 : index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      backgroundColor: Colors.grey,
      appBar: new AppBar(
        title: new Text("Expandable List"),
        backgroundColor: Colors.redAccent,
      ),
      body: new ListView.builder(
        itemBuilder: (BuildContext context, int index) {
          // Modified: Pass index, expansion state, and toggle callback to child
          return new ExpandableListView(
            title: "Title $index",
            isExpanded: _expandedIndex == index,
            onToggle: () => _toggleExpansion(index),
          );
        },
        itemCount: 5,
      ),
    );
  }
}

// Modified: Changed to StatelessWidget since state is managed by parent
class ExpandableListView extends StatelessWidget {
  final String title;
  final bool isExpanded; // Added: Receive expansion state from parent
  final VoidCallback onToggle; // Added: Receive toggle action from parent

  const ExpandableListView({
    Key key,
    this.title,
    this.isExpanded,
    this.onToggle,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return new Container(
      margin: new EdgeInsets.symmetric(vertical: 1.0),
      child: new Column(
        children: <Widget>[
          new Container(
            color: Colors.blue,
            padding: new EdgeInsets.symmetric(horizontal: 5.0),
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                new IconButton(
                  icon: new Container(
                    height: 50.0,
                    width: 50.0,
                    decoration: new BoxDecoration(
                      color: Colors.orange,
                      shape: BoxShape.circle,
                    ),
                    child: new Center(
                      child: new Icon(
                        // Modified: Use parent-provided state to set icon
                        isExpanded ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down,
                        color: Colors.white,
                        size: 30.0,
                      ),
                    ),
                  ),
                  onPressed: onToggle, // Modified: Trigger parent's toggle callback
                ),
                new Text(
                  title,
                  style: new TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
                )
              ],
            ),
          ),
          new ExpandableContainer(
            // Modified: Use parent-provided state to control expansion
            expanded: isExpanded,
            child: new ListView.builder(
              itemBuilder: (BuildContext context, int index) {
                return new Container(
                  decoration: new BoxDecoration(border: new Border.all(width: 1.0, color: Colors.grey), color: Colors.black),
                  child: new ListTile(
                    title: new Text(
                      "Cool $index",
                      style: new TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
                    ),
                    leading: new Icon(
                      Icons.local_pizza,
                      color: Colors.white,
                    ),
                  ),
                );
              },
              itemCount: 15,
            ))
        ],
      ),
    );
  }
}

class ExpandableContainer extends StatelessWidget {
  final bool expanded;
  final double collapsedHeight;
  final double expandedHeight;
  final Widget child;

  ExpandableContainer({
    @required this.child,
    this.collapsedHeight = 0.0,
    this.expandedHeight = 300.0,
    this.expanded = true,
  });

  @override
  Widget build(BuildContext context) {
    double screenWidth = MediaQuery.of(context).size.width;
    return new AnimatedContainer(
      duration: new Duration(milliseconds: 500),
      curve: Curves.easeInOut,
      width: screenWidth,
      height: expanded ? expandedHeight : collapsedHeight,
      child: new Container(
        child: child,
        decoration: new BoxDecoration(border: new Border.all(width: 1.0, color: Colors.blue)),
      ),
    );
  }
}

How It Works:

  • The _expandedIndex variable in Home acts as a single source of truth for which item is expanded.
  • When you tap an item, the _toggleExpansion callback updates this index: if the tapped item was already open, it collapses it; otherwise, it sets that index as the active expanded item (automatically closing any previously open item).
  • Each ExpandableListView now relies on the parent's state instead of its own, ensuring all items stay in sync.

内容的提问来源于stack exchange,提问作者Mohsen Emami

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:55:15