[flutter] stack을 활용한 모달창

jini.choi·2024년 3월 27일
0

flutter

목록 보기
3/9

stack, ListView 사용시 주의

  • 콘텐츠 위에 콘텐츠를 쌓으려면 stack 위젯 사용

  • Stack -> Positioned 구조로 이루어진다.

  • Positioned은 Stack의 직접적인 자식 요소로만 사용가능하며,
    Positioned을 padding으로 감싸는등 직접적인 자식으로 두지 않을 경우 에러가 난다.

  • ListView는 Expanded위젯으로 감싸서 넘쳐도 에러가 나지않게 한다.

모달 상태 관리

  • 리스트 당 하나의 모달만 갖고있는 상태

  • bool isModalOne = false; 으로 초기 상태 선언

  • GestureDetector위젯으로 아이콘 onTap시 state값 변경 (IconButton 활용법도 있다고 함)

GestureDetector(
  onTap: () {
    setState(() {
      isModalOne = !isModalOne;
    });
  },
  child: const Icon(
    Icons.error_outline_sharp,
    color: Color(0xFF6a6d6c),
    size: 30,
  ),
),
  • if (isModalOne == true) Positioned(모달UI)로 isModalOne이 true일때만 모달UI가 보이게끔 작성

체크박스 상태 관리

  • 각 항목의 체크 상태를 저장할 List<bool>를 사용
  • ListView.builder를 통해 각 항목을 렌더링할 때, 해당 항목의 체크 상태를 해당 구조에서 찾아 CheckboxListTile에 전달
  • CheckboxListTile의 onChanged 콜백에서 해당 항목의 체크 상태를 업데이트하고, setState를 호출하여 UI를 갱신

filled()

  • 모든 요소가 주어진 초기 값으로 채워진 리스트를 생성할 수 있다.

  • List<T> filled(int length, T fill, {bool growable = false})

    • length: 생성할 리스트의 길이입니다.
    • fill: 리스트의 모든 요소에 할당할 값입니다.
    • growable: 기본값은 false로, 이 경우 생성된 리스트의 길이는 고정됩니다. true로 설정하면 리스트의 길이를 변경할 수 있습니다.
  • var myList = List<bool>.filled(5, false);
    • 모든 요소가 false인 길이가 5인 리스트를 생성하고자 할 때는 위와 같이 filled 함수를 사용
    • myList라는 이름의 리스트를 생성하며, 이 리스트는 [false, false, false, false, false]와 같은 값을 가진다.

전체 코드

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

final List<Map<String, String>> productHelpu = [
  {
    'img': 'assets/images/biki.png',
    'title': '[헬퓨] D데이팩',
    'delivery': '무료배송',
    'price': '62,400',
  },
];

final List<Map<String, String>> productHelpuTwo = [
  {
    'img': 'assets/images/biki.png',
    'title': '[헬퓨] D데이팩2',
    'delivery': '무료배송',
    'price': '52,400',
  },
  {
    'img': 'assets/images/biki.png',
    'title': '[헬퓨] D데이팩3',
    'delivery': '무료배송',
    'price': '12,400',
  },
];

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

  @override
  State<HelpuScreen> createState() => _HelpuScreenState();
}

class _HelpuScreenState extends State<HelpuScreen> {
  List<bool> isCheckedListOne = List.filled(productHelpu.length, false);
  List<bool> isCheckedListTwo = List.filled(productHelpuTwo.length, false);

  bool isModalOne = false;
  bool isModalTwo = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.symmetric(
            vertical: 20,
            horizontal: 20,
          ),
          child: Column(
            children: [
              Expanded(
                child: Stack(
                  children: [
                    Positioned(
                      child: Column(
                        children: [
                          Row(
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
                            crossAxisAlignment: CrossAxisAlignment.end,
                            children: [
                              const Text(
                                '맞춤 멀티팩',
                                style: TextStyle(
                                  fontSize: 16,
                                  fontWeight: FontWeight.w600,
                                ),
                              ),
                              GestureDetector(
                                onTap: () {
                                  setState(() {
                                    isModalOne = !isModalOne;
                                  });
                                },
                                child: const Icon(
                                  Icons.error_outline_sharp,
                                  color: Color(0xFF6a6d6c),
                                  size: 30,
                                ),
                              ),
                            ],
                          ),
                          const SizedBox(
                            height: 20,
                          ),
                          Expanded(
                            child: ListView.builder(
                              // shrinkWrap: true,
                              itemCount: productHelpu.length,
                              itemBuilder: (context, index) {
                                final productHelpuItem = productHelpu[index];
                                return Stack(
                                  children: [
                                    Padding(
                                      padding:
                                          const EdgeInsets.only(bottom: 10),
                                      child: Container(
                                        decoration: BoxDecoration(
                                            borderRadius:
                                                BorderRadius.circular(8),
                                            border: Border.all(
                                              width: 1,
                                              color: const Color(0xFFd9d9d9),
                                            )),
                                        child: Padding(
                                          padding: const EdgeInsets.all(5.0),
                                          child: CheckboxListTile(
                                            value: isCheckedListOne[
                                                index], // 체크박스의 상태를 관리하는 변수
                                            onChanged: (bool? value) {
                                              setState(() {
                                                isCheckedListOne[index] =
                                                    value!;
                                              });
                                            },
                                            title: SizedBox(
                                              width: double.infinity,
                                              child: Row(
                                                children: [
                                                  Container(
                                                    decoration:
                                                        const BoxDecoration(
                                                      color: Color(0xFFd9d9d9),
                                                    ),
                                                    child: Image.asset(
                                                      productHelpuItem['img']!,
                                                      fit: BoxFit.cover,
                                                      width: 50,
                                                      height: 50,
                                                    ),
                                                  ),
                                                  const SizedBox(
                                                    width: 10,
                                                  ),
                                                  Expanded(
                                                    child: Column(
                                                      crossAxisAlignment:
                                                          CrossAxisAlignment
                                                              .start,
                                                      children: [
                                                        Text(productHelpuItem[
                                                            'title']!),
                                                        Text(productHelpuItem[
                                                            'delivery']!)
                                                      ],
                                                    ),
                                                  ),
                                                ],
                                              ),
                                            ),

                                            secondary: Text(
                                                '${productHelpuItem['price']!} 원'),
                                            controlAffinity:
                                                ListTileControlAffinity.leading,
                                          ),
                                        ),
                                      ),
                                    ),
                                  ],
                                );
                              },
                            ),
                          ),
                        ],
                      ),
                    ),
                    if (isModalOne == true)
                      Positioned(
                        top: 55,
                        child: Padding(
                          padding: const EdgeInsets.fromLTRB(10, 3, 0, 5),
                          child: Container(
                            width: 340,
                            height: 75,
                            decoration: BoxDecoration(
                              border: Border.all(
                                width: 1,
                                color: Colors.black,
                              ),
                              color: Colors.white,
                              borderRadius: BorderRadius.circular(8),
                            ),
                            child: Padding(
                              padding: const EdgeInsets.all(10),
                              child: Row(
                                mainAxisAlignment:
                                    MainAxisAlignment.spaceBetween,
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                                  const Column(
                                    mainAxisAlignment:
                                        MainAxisAlignment.spaceBetween,
                                    crossAxisAlignment:
                                        CrossAxisAlignment.start,
                                    children: [
                                      Text(
                                        '맞춤 멀티팩',
                                        style: TextStyle(
                                            fontSize: 18,
                                            fontWeight: FontWeight.w800),
                                      ),
                                      SizedBox(
                                        height: 5,
                                      ),
                                      Text('구매할 멀티팩을 선택해보세요.'),
                                    ],
                                  ),
                                  GestureDetector(
                                    onTap: () {
                                      setState(() {
                                        isModalOne = !isModalOne;
                                      });
                                    },
                                    child: const Icon(Icons.close),
                                  )
                                ],
                              ),
                            ),
                          ),
                        ),
                      ),
                  ],
                ),
              ),
              Expanded(
                child: Stack(
                  children: [
                    Positioned(
                      child: Column(
                        children: [
                          Row(
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
                            crossAxisAlignment: CrossAxisAlignment.end,
                            children: [
                              const Text(
                                '단일 멀티팩',
                                style: TextStyle(
                                  fontSize: 16,
                                  fontWeight: FontWeight.w600,
                                ),
                              ),
                              GestureDetector(
                                onTap: () {
                                  setState(() {
                                    isModalTwo = !isModalTwo;
                                  });
                                },
                                child: const Icon(
                                  Icons.error_outline_sharp,
                                  color: Color(0xFF6a6d6c),
                                  size: 30,
                                ),
                              ),
                            ],
                          ),
                          const SizedBox(
                            height: 20,
                          ),
                          Expanded(
                            child: ListView.builder(
                              // shrinkWrap: true,
                              itemCount: productHelpuTwo.length,
                              itemBuilder: (context, index) {
                                final productHelpuItemTwo =
                                    productHelpuTwo[index];
                                return Stack(
                                  children: [
                                    Padding(
                                      padding:
                                          const EdgeInsets.only(bottom: 10),
                                      child: Container(
                                        decoration: BoxDecoration(
                                            borderRadius:
                                                BorderRadius.circular(8),
                                            border: Border.all(
                                              width: 1,
                                              color: const Color(0xFFd9d9d9),
                                            )),
                                        child: Padding(
                                          padding: const EdgeInsets.all(5.0),
                                          child: CheckboxListTile(
                                            value: isCheckedListTwo[
                                                index], // 체크박스의 상태를 관리하는 변수
                                            onChanged: (bool? value) {
                                              setState(() {
                                                isCheckedListTwo[index] =
                                                    value!;
                                              });
                                            },
                                            title: SizedBox(
                                              width: double.infinity,
                                              child: Row(
                                                children: [
                                                  Container(
                                                    decoration:
                                                        const BoxDecoration(
                                                      color: Color(0xFFd9d9d9),
                                                    ),
                                                    child: Image.asset(
                                                      productHelpuItemTwo[
                                                          'img']!,
                                                      fit: BoxFit.cover,
                                                      width: 50,
                                                      height: 50,
                                                    ),
                                                  ),
                                                  const SizedBox(
                                                    width: 10,
                                                  ),
                                                  Expanded(
                                                    child: Column(
                                                      crossAxisAlignment:
                                                          CrossAxisAlignment
                                                              .start,
                                                      children: [
                                                        Text(
                                                            productHelpuItemTwo[
                                                                'title']!),
                                                        Text(
                                                            productHelpuItemTwo[
                                                                'delivery']!)
                                                      ],
                                                    ),
                                                  ),
                                                ],
                                              ),
                                            ),

                                            secondary: Text(
                                                '${productHelpuItemTwo['price']!} 원'),
                                            controlAffinity:
                                                ListTileControlAffinity.leading,
                                          ),
                                        ),
                                      ),
                                    ),
                                  ],
                                );
                              },
                            ),
                          ),
                        ],
                      ),
                    ),
                    if (isModalTwo == true)
                      Positioned(
                        top: 55,
                        child: Padding(
                          padding: const EdgeInsets.fromLTRB(10, 3, 0, 5),
                          child: Container(
                            width: 340,
                            height: 120,
                            decoration: BoxDecoration(
                              border: Border.all(
                                width: 1,
                                color: Colors.black,
                              ),
                              color: Colors.white,
                              borderRadius: BorderRadius.circular(8),
                            ),
                            child: Padding(
                              padding: const EdgeInsets.all(10),
                              child: Row(
                                mainAxisAlignment:
                                    MainAxisAlignment.spaceBetween,
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                                  const Column(
                                    mainAxisAlignment:
                                        MainAxisAlignment.spaceBetween,
                                    crossAxisAlignment:
                                        CrossAxisAlignment.start,
                                    children: [
                                      Text(
                                        '맞춤 영양제',
                                        style: TextStyle(
                                            fontSize: 18,
                                            fontWeight: FontWeight.w800),
                                      ),
                                      SizedBox(
                                        height: 10,
                                      ),
                                      Text('멀티팩과 함께 섭취할 제품을 선택해보세요.'),
                                      Text('혹은 단품만 선택하여 구매가 가능합니다.'),
                                    ],
                                  ),
                                  GestureDetector(
                                    onTap: () {
                                      setState(() {
                                        isModalTwo = !isModalTwo;
                                      });
                                    },
                                    child: const Icon(Icons.close),
                                  )
                                ],
                              ),
                            ),
                          ),
                        ),
                      ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
profile
개발짜🏃‍♀️

0개의 댓글