본문 바로가기
Flutter/Flutter 위젯

Flutter - ExpansionPanel, ExpansionPanelList, 눌러서 열기, 확장 패널

by 베타코드 2022. 4. 28.
반응형

안녕하세요 독학코딩입니다. 오늘은 앱을 개발할 때 정말 많이 사용하는 눌러서 열기 기능에 대해서 배워보겠습니다. 위젯 이름은 ExpansionPanel인데 한국어로는 정확한 번역이 있는지 모르겠네요. 직역으로는 확장 패널인데 눈으로 보시면 익숙하실 겁니다. 

 

Flutter ExpansionPanel

class FirstRoute extends StatefulWidget {
  const FirstRoute({Key? key}) : super(key: key);

  @override
  State<FirstRoute> createState() => _FirstRouteState();
}

class _FirstRouteState extends State<FirstRoute> {
  bool _expanded = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('App Bar'),
      ),
      body: SingleChildScrollView(
        child: ExpansionPanelList(
          children: [
            ExpansionPanel(
              headerBuilder: (context, isExpanded) {
                return Text("확장패널 헤더");
              },
              body: Text("확장패널 바디"),
              isExpanded: _expanded,
            ),
            ExpansionPanel(
              headerBuilder: (context, isExpanded) {
                return Text("확장패널 헤더");
              },
              body: Text("확장패널 바디"),
              isExpanded: _expanded,
            ),
          ],
          expansionCallback: (panelIndex, isExpanded) {
            setState(() {
              _expanded = !_expanded;
            });
          },
        ),
      ),
    );
  }
}

위가 가장 기본적인 ExpansionPanel입니다. 일반적으로는 여러개의 ExpansionPanel를 사용하기 때문에 ExpansionPanelList를 사용합니다. 그리고 위젯 이름 그대로 expansion, 즉 확장되었다가 축소되었다 하기 때문에 유동적인 화면을 위해서 ScrollView를 사용하는 게 일반적입니다. 여기서는 SingleChildScrollView를 사용하였습니다.

 

위 코드를 복사해서 실행해보셨다면 아시겠지만, 확장패널의 화살표를 누르면 1개의 패널만 열리는 게 아니라 2개의 패널이 전부 다 열리는 걸 확인하실 수 있으실 겁니다. 이는 _expanded를 두 개의 패널이 함께 사용해서 그런데요. 어떻게 수정해야 되는지 알려드리겠습니다. 

 

만약 ExpansionPanel을 많이 사용하지 않거나 다이내믹하게 생성할게 아니라면 단순한 방법이 있습니다. 

 

bool _expanded1 = false;
bool _expanded2 = false;

단순히 _expanded를 필요한 만큼 작성하시고 각각의 ExpansionPanel에 배정해주면 됩니다. 그리고 expansionCallback을 아래와 같이 변경해주시면 됩니다.

 

expansionCallback: (panelIndex, isExpanded) {
  setState(() {
    if (panelIndex == 0) {
      _expanded1 = !_expanded1;
    }
    if (panelIndex == 1) {
      _expanded2 = !_expanded2;
    }
  });
},

하지만 보통은 이런 방법으로 만들지는 않으실 겁니다. 대부분 다이내믹하게 ExpansionPanel을 만들어야 하는 경우가 더 많으니깐요.

 

Futter Dynamic ExpansionPanel

Dynamic이라는건 다른 게 아닙니다. ExpansionPanel을 한 번에 여러 개를 만드는 것입니다.

class FirstRoute extends StatefulWidget {
  const FirstRoute({Key? key}) : super(key: key);

  @override
  State<FirstRoute> createState() => _FirstRouteState();
}

class _FirstRouteState extends State<FirstRoute> {
  late List<Item> _items;

  @override
  void initState() {
    super.initState();
    setState(() {
      _items = _generateItems();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("ExpansionPanel"),
      ),
      body: SingleChildScrollView(
        child: ExpansionPanelList(
          expansionCallback: (int index, bool isExpanded) {
            setState(() {
              _items[index].isExpanded = !isExpanded;
            });
          },
          children: _items.map((item) => _buildExpansionPanel(item))
              .toList(),
        ),
      ),
    );
  }

  List<Item> _generateItems() {
    return List.generate(20, (int index) {
      return Item(
        id: index,
        name: '확장패널 헤더 $index',
        description: '확장 패널 바디 $index',
      );
    });
  }

  ExpansionPanel _buildExpansionPanel(Item item) {
    return ExpansionPanel(
      isExpanded: item.isExpanded,
      headerBuilder: (BuildContext context, bool isExpanded) {
        return Text(item.name);
      },
      body: Text(item.description),
    );
  }
}


class Item {
  bool isExpanded;
  var id;
  String name;
  String description;

  Item({
    this.isExpanded = false,
    required this.id,
    required this.name,
    required this.description,
  });
}

코드가 조금 길지만, 위가 가장 기본적인 예시입니다. 하나하나 설명해드리겠습니다. 하나하나 설명해드리지는 않겠습니다. 차근차근 살펴보시면 이해되실 겁니다. 

 

Flutter ExpansionPanel 옵션

ExpansionPanelList(
  animationDuration: const Duration(seconds: 2),
  expandedHeaderPadding: EdgeInsets.all(10),
  dividerColor: Colors.red,
)

animationDuration은 ExpansionPanel이 펼쳐지고 접히는 시간을 설정합니다. 

expandedHeaderPadding은 ExpansionPanel을 펼쳤을 때 Header의 padding을 설정합니다.

dividerColor는 각각의 ExpansionPanel 사이의 나눔선 색깔입니다.

 

ExpansionPanel(
  backgroundColor: Colors.lightBlueAccent,
  canTapOnHeader: true,
)

backgroundColor는 ExpansionPanel의 색을 설정합니다. 

canTapOnHeader는 ExpansionPanel의 화살표 뿐만 아니라 header자체를 눌러도 펼치고 접기가 가능하게 합니다.

반응형

댓글