본문 바로가기

Flutter Web

[Flutter Web] Side bar 만들기

콘솔 종류의 프로그램을 만들경우 가장 기본적으로 필요한 것이 Side Bar 메뉴입니다.

콘솔 필요그램이 작업이 필요해서 간단히 작업해 보았습니다.

 

Flutter Awsome 등에도 비슷한 프로젝트가 다수 검색되기 때문에 이들 중 괜찮아 보이는 프로젝트의 코드를 사용하시는 것도 방법일 것 같습니다.

하지만 저는 여러가지를 찾아보다가 pub.dev 에 있는 sidebarx 플러그인을 사용하기로 했습니다.

https://pub.dev/packages/sidebarx

 

sidebarx | Flutter Package

flutter multiplatform navigation sidebar / side navigationbar / drawer widget

pub.dev

이런 플러그인들이 많아진다는 것은 flutter 를 사용하는 입장에서 매우 반가운 일인것 같습니다.

 

우선 앱의 기본 프레임워크가 필요합니다.

저는 GetX를 사용하는 프로젝트를 좋아하기 때문에 GetMaterialApp으로 시작해 보겠습니다.

 

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

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'Test Console',
      debugShowCheckedModeBanner: false,
      theme: AppTheme.basic,
      initialBinding: InitBinding(),
      initialRoute: '/',
      getPages: [
        GetPage(name: '/', page: () => const Home(),
        ),
        GetPage(name: '/user', page: () => UserInfo(),
        ),
      ],
    );
  }
}

 

이제 앱이 시작되었으니 Home 에 해당하는 코드를 넣어볼게요.

Home은 Scaffold로 구성이 되어 있습니다.

앱웹을 지원하기 위해 화면사이즈가 600보다 작을 경우에는 small screen으로 인식하고 앱바를 보여줄수 있도록 하였습니다.

그리고 Side + Screen을 Row로 구성하여 메뉴가 변경될때 스크린만 변경되는 형태로 작업하면 될것 같습니다.

 

class _HomeState extends State<Home> {
  final _controller = SidebarXController(selectedIndex: 0, extended: true);
  final _key = GlobalKey<ScaffoldState>();
  final PageIndexController pageIndexController = Get.find();

  @override
  Widget build(BuildContext context) {
    return Builder(
      builder: (context) {
        final isSmallScreen = MediaQuery.of(context).size.width < 600;
        return Scaffold(
          key: _key,
          appBar: isSmallScreen
              ? AppBar(
            backgroundColor: canvasColor,
            title: Text(pageIndexController.getTitleByIndex(_controller.selectedIndex)),
            leading: IconButton(
              onPressed: () {
                _controller.setExtended(true);
                _key.currentState?.openDrawer();
              },
              icon: const Icon(Icons.menu),
            ),
          )
              : null,
          drawer: MenuSideBar(controller: _controller,),
          body: Row(
            children: [
              if (!isSmallScreen) MenuSideBar(controller: _controller),
              Expanded(
                child: Center(
                  child: MainScreen(
                    controller: _controller,
                  ),
                ),
              ),
            ],
          ),
        );
      }
    );
  }
}

 

화면은 MenuSideBar와 MainScreen으로 구성되어 있습니다.

먼저 MenuSideBar를 보겠습니다.

 

class MenuSideBar extends StatelessWidget {
  const MenuSideBar({Key? key, required SidebarXController controller})
      : _controller = controller,
        super(key: key);
  final SidebarXController _controller;

  @override
  Widget build(BuildContext context) {
    return SidebarX(
      controller: _controller,
      theme: SidebarXTheme(
        margin: const EdgeInsets.all(10),
        decoration: BoxDecoration(
          color: canvasColor,
          borderRadius: BorderRadius.circular(20),
        ),
        hoverColor: scaffoldBackgroundColor,
        textStyle: TextStyle(color: Colors.white.withOpacity(0.7)),
        selectedTextStyle: const TextStyle(color: Colors.white),
        itemTextPadding: const EdgeInsets.only(left: 30),
        selectedItemTextPadding: const EdgeInsets.only(left: 30),
        itemDecoration: BoxDecoration(
          borderRadius: BorderRadius.circular(10),
          border: Border.all(color: canvasColor),
        ),
        selectedItemDecoration: BoxDecoration(
          borderRadius: BorderRadius.circular(10),
          border: Border.all(
            color: actionColor.withOpacity(0.37),
          ),
          gradient: const LinearGradient(
            colors: [accentCanvasColor, canvasColor],
          ),
          boxShadow: [
            BoxShadow(
              color: Colors.black.withOpacity(0.28),
              blurRadius: 30,
            )
          ],
        ),
        iconTheme: IconThemeData(
          color: Colors.white.withOpacity(0.7),
          size: 20,
        ),
        selectedIconTheme: const IconThemeData(
          color: Colors.white,
          size: 20,
        ),
      ),
      extendedTheme: const SidebarXTheme(
        width: 200,
        decoration: BoxDecoration(
          color: canvasColor,
        ),
      ),
      footerDivider: divider,
      headerBuilder: (context, extended) {
        return Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            SizedBox(
              height: 50,
              child: Padding(
                padding: const EdgeInsets.only(top: 16, bottom: 16, right: 16),
                child: Image.asset('icons/icon_no_background.png'),
              ),
            ),
            // Text('hibike console', style: TextStyle(fontSize: 17),)
          ],
        );
      },
      items: [
        SidebarXItem(
          icon: Icons.home,
          label: 'Home',
          onTap: () {
            debugPrint('Home');
          },
        ),
        const SidebarXItem(
          icon: Icons.search,
          label: 'Search',
        ),
        const SidebarXItem(
          icon: Icons.people,
          label: 'People',
        ),
        const SidebarXItem(
          icon: Icons.favorite,
          label: 'Favorite',
        ),
        const SidebarXItem(
          iconWidget: FlutterLogo(size: 20),
          label: 'Flutter',
        ),
      ],
    );
  }
}

 

SideBarX에서 제공하는 필드중에 필요한 필드들로 간단히 세팅해 보았습니다.

MainScreen은 선택된 메뉴의 이름을 보여주는 간단한 코드입니다.

 

class MainScreen extends StatelessWidget {
  MainScreen({Key? key, required this.controller,}) : super(key: key);
  final SidebarXController controller;

  final PageIndexController pageIndexController = Get.find();

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return AnimatedBuilder(
      animation: controller,
      builder: (context, child) {
        final pageTitle = pageIndexController.getTitleByIndex(controller.selectedIndex);
        switch (controller.selectedIndex) {
          case 0:
            return ListView.builder(
              padding: const EdgeInsets.only(top: 10),
              itemBuilder: (context, index) => Container(
                height: 100,
                width: double.infinity,
                margin: const EdgeInsets.only(bottom: 10, right: 10, left: 10),
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(20),
                  color: Theme.of(context).canvasColor,
                  boxShadow: const [BoxShadow()],
                ),
              ),
            );
          default:
            return Text(
              pageTitle,
              style: theme.textTheme.headline5,
            );
        }
      },
    );
  }
}

 

 

여기까지 작업된 화면입니다. 전체화면에 메뉴가 잘 보이고 있습니다.

 

메뉴를 접은 상태입니다. 아이콘이 깔끔하게 잘 나오고 있네요

 

화면의 사이즈를 줄여보았습니다. 앱바가 나오면서 선택된 메뉴가 잘 보이고 있네요.

이제 페이지 별로 필요한 작업을 하면 간단한 나만의 콘솔을 만들 수 있을 것 같습니다.