본문 바로가기

Flutter

플러터(Flutter) - 이미지 메뉴(Image Menu)

반응형

Flutter를 이용한 아이콘 목록의 메뉴를 만들어 보겠습니다.

메뉴 선택 시 선택된 아이콘은 그림자를 주어 표시되는 방식입니다.

 

상태 관리는 GetX를 사용했습니다.

https://pub.dev/packages/get

 

get | Flutter Package

Open screens/snackbars/dialogs without context, manage states and inject dependencies easily with GetX.

pub.dev

 

pubspec.yaml 설정

dependencies:
  flutter:
    sdk: flutter

  get: ^4.6.1

flutter:
  uses-material-design: true

  assets:
    - images/
dependencies: 에 getX를  assets:에 images 폴더를 설정해 줍니다.
 

그리고 images 폴더를 하나 만들어 테스용 이미지를 하나 넣어 주세요 대충 64 x 64 사이즈 크기의 이미지면 될 듯합니다.

 

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const Home(),
    );
  }
}

GetX 사용을 위해 MaterialApp을 GetMaterialApp으로 변경해 줬습니다. (본인의 상태 관리에 맞게 설정하세요.)

 

class MenuState extends GetxController {
  int _menuIndex = 0;

  get menuIndex => _menuIndex;

  void menuIndexState(int _index) {
    _menuIndex = _index;
    update();
  }
}

현재 클릭된 또는 선택된 메뉴의 Index 위치를 상태 관리를 해 줍니다.

 

const List<String> menuIcon = [
  'test.png',
  'test.png',
  'test.png',
  'test.png',
  'test.png',
  'test.png',
];

menuIcon은 실제 메뉴에서 사용 아이콘들의 목록입니다. 위에서 만든 images 폴더에 들어 있는 이미지 이름과 일치

해야 하며 List의 등록 위치에 따라 메뉴 위치가 변경됩니다.

 

class Home extends StatelessWidget {
  const Home({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    Get.put(MenuState());

    return Scaffold(
      body: SafeArea(
        child: Column(
          children: [
            const SizedBox(
              width: double.infinity,
            ),
            SizedBox(
              height: 190,
              child: GridView.builder(
                itemCount: menuIcon.length,
                shrinkWrap: true,
                scrollDirection: Axis.horizontal,
                gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 2,
                  mainAxisSpacing: 30,
                ),
                itemBuilder: (context, index) => listMenu(menuIcon, index),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Widget listMenu(List<String> menu, int index) {
  return Padding(
    padding: const EdgeInsets.all(4.0),
    child: GetBuilder<MenuState>(
      builder: (item) => Column(
        children: [
          InkWell(
            onTap: () {
              item.menuIndexState(index);
            },
            child: Material(
              color: item.menuIndex == index ? const Color(0xfff5f5f5) : Colors.transparent,
              elevation: item.menuIndex == index ? 6.0 : 0,
              borderRadius: BorderRadius.circular(12.0),
              child: Image.asset(
                'images/${menuIcon[index]}',
                width: 46,
                height: 46,
              ),
            ),
          ),
          const SizedBox(height: 8.0),
          const Text('제목'),
        ],
      ),
    ),
  );
}

Get.put(MenuState());

메뉴 상태 관리를 위해 만든 MenuState를 사용하기 위해 불어옵니다.

 

SizedBox의 height를 190을 주어 메뉴들이 들어갈 위치를 잡아 줍니다.

GridView.builder - 2단 가로 배치를 위해 GridView를 사용했습니다.

itemCount - List로 만들어 두었던 menuIcon의 length 값으로 수량이 정해 집니다.

shrinkWrap: true - 말 그대로 GridView의 목록들을 수축시켜 스크롤 현상이 일어나지 않게 도와주며 아이콘들을 Center정렬을 시켜 줍니다.  (테스트할 때 false로 해보시면 바로 이해됩니다.)

scrollDirection: Axis.hotizontal - 스크롤 방향을 가로로 잡아 줘야 위 이미지와 같이 아이콘 모양이 나오며 이역시 vertical방향으로 테스트해보시면 아실 수 있습니다.

gridDelegate: 2단 배치 및 아이템의 간격을 30만큼 주었습니다.

itemBuilder => listMenu(menuIcon, index) - 실제 아이콘 메뉴 하나의 기능과 디자인이 구현되는 곳입니다.

 

child: GetBuilder<MenuState> - 메뉴 클릭 시 상태 체크를 위한 GetX를 활성화시켜줍니다.

builder: (item) - item을 통해 MenuState의 menuIndex에 접근하게 됩니다.

InkWell -> item.menuIndexState(index) - 현재 클릭된 아이콘의 위치를  MenuState로 넘겨줍니다.

color: item.menuIndex == index ? const Color(0xfff5f5f5) : Colors.transparent,
elevation: item.menuIndex == index ? 6.0 : 0,
borderRadius: BorderRadius.circular(12.0),

 

color: - 아이콘의 이미지가 투명 배경을 가지 png 파일 일 때의 효과입니다.

사용하실 이미지에 따라 color: 쪽은 필요 없을 수 있습니다.

InkWell에서 아이콘 메뉴 클릭 시 보이는 아이콘들 중 클릭된 아이콘의 index와 같은 아이콘은 색상을 주고 나머진 투명하게 만들어 줍니다. 

여기서 색상은 아이콘의 색상이 아닌 투명한 부분을 채울 색상입니다.

elevation: - 선택된 아이콘만 그림자를 표현하며 나머지는 그림자를 없애 버립니다.

borderRadius: 모든 아이콘에 라운드를 줍니다. 이역시 투명 배경이면 위쪽의 샘플 이미지처럼 선택된 아이콘에만 라운드가 표시됩니다.

Text('제목') - 아이콘의 하단에 메뉴명을 주실 때 사용하시면 됩니다. 

여기에선 테스트로 이렇게 주었지만 menuIcon처럼 List를 만들어 적용해 주시면 됩니다.

 

아이콘 메뉴의 하단이 잘리는 현상이 생긴다면 GridView를 감싸고 있는 SizedBox의 height의 값을 조절해 주시면 됩니다.

이상으로 아이콘 메뉴 만들기였습니다.

반응형