최근 REAFLA에서 구글 코리아에 제출할 페이플 사용사례로 활용하기 위해 토이프로젝트를 시작하였다.. 꼽사리로 같이 끼게 됨.
겸사겸사 개인 프로젝트로 비슷한 Stack을 사용하여 만들어 보고자 한다.
맨 먼저 중앙에 동그란 버튼이 있는 BottomNavi를 구현해 보자.
스스로 위젯을 만들어보는 것도 도움이 되겠지만 그건 앱 사이클을 완전히 만들어보고 나서 생각하는 거로~!
고로 멋쟁이 개발자님들이 만들어 주신 BottomNavi를 설치하여 GetX와 함께 사용해 보자.
여기에서 원하는 BottomNavi 골라 잡아~!
installing 탭에 들어가 설치하자.
property | description |
---|---|
backgroundColor | Bar의 배경색 |
gradient | Bar 배경색을 그라데이션 할 수 있다. |
height | Bar의 높이 |
color | Tab Icon과 Text의 색상 |
activeColor | 활성화 된 Tab Icon/Text의 색상 |
curveSize | convex 의 크기 |
top | AppBar를 기준으로 볼록한 모양의 제일 윗단 |
cornerRadius | Bar의 양 끝단의 굴곡도 |
style | 여기에서 supported style를 확인! |
chipBuilder | ConvexAppBar.badge를 커스텀하여 쓸 수 있다. |
//bottom_nav_view.dart
import 'package:convex_bottom_bar/convex_bottom_bar.dart';//import this
import 'package:get/get.dart';// for GetX
class BottomNavView extends GetView<BottomNavController> {
const BottomNavView({
Key? key,
required this.onTapPressed,
}) : super(key: key);
final Function(int index) onTapPressed;
Widget build(BuildContext context) {
return StyleProvider(
style: ConvexStyleProvider(),
child: ConvexAppBar(
curve: null,
color: system100,
backgroundColor: Colors.white,
style: TabStyle.fixedCircle,
activeColor: primary000,
cornerRadius: 20,
height: 60,
items: [
TabItem(
title: translation(context).home,
icon: Icons.home,
),
TabItem(
title: translation(context).cafe,
icon: Icons.assistant_photo_rounded,
),
TabItem(
title: translation(context).story,
icon: Icons.add_reaction_rounded,
),
TabItem(
title: translation(context).store,
icon: Icons.add_shopping_cart_rounded,
),
TabItem(
title: translation(context).my_info,
icon: Icons.account_box,
),
],
onTap: (int index) {
onTapPressed(index);
},
),
);
}
}
class ConvexStyleProvider extends StyleHook {
double get activeIconSize => 40;
double get activeIconMargin => 10;
double get iconSize => 20;
TextStyle textStyle(Color color, String? fontFamily) {
return text9Bold.copyWith(color: color);
}
}
Theme 에서 내용과 비교하자면
backgroundColor: Colors.white, // 배경색 white
color: system100,//Tab icon/text 색상
activeColor: primary000,//활성화된 Tab icon/text 색상
cornerRadius: 20,//양 끝단의 굴곡
style: TabStyle.fixedCircle, //가운데 동그라미 버튼 고정
아래와 같은 모습의 Bottom Navi가 완성된다.
소스코드를 들여다보면 onTab()에 index를 받아 onTapPressed를 호출하는 callBack 함수로 되어 있다. 이제 BottomNavView를 호출하는 곳을 살펴보자.
// bottom_nav_screen.dart
class BottomNavigationScreen extends StatelessWidget {
BottomNavigationScreen({
Key? key,
}) : super(key: key);
final pages = [
HomePage(),
CafePage(),
StoryPage(),
StorePage(),
MyInfoPage()
];//Bottomnavi에 물려있는 Page들
BottomNavController bottomNavController = Get.put(BottomNavController());
Widget build(BuildContext context) {
return Scaffold(
body: Obx(//here
() => IndexedStack(//here
children: pages,
index: bottomNavController.selectedIndex.value,
),
),
bottomNavigationBar: BottomNavView(
onTapPressed: (int index) {
bottomNavController.ChangeIdex(index);//here
},
),
);
}
}
IndexedStack를 통해 BottomNavi가 Tab 이동마다 rebuild되는 현상을 막을 수 있다.
BottomNavController 의 ChangeIdex를 호출하여 index 의 현재값을 바꾸어 주고 Obx를 이용하여 값을 notify 한다.
//bottom_nav_controller.dart
import 'package:get/get.dart';
class BottomNavController extends GetxController {
var selectedIndex = 1.obs;//BottomNavigationScreen의 Obs에서 바라본 값이 요놈이다.
var textValue = 0.obs;
void ChangeIdex(int index) {
selectedIndex.value = index;
}
void IncreaseValue() {
textValue.value++;
}//이건 나중에 swipe로 page 전환하려고 미리 맹글어 놓음.
}
//app_pages.dart
...
GetPage(
name: _Paths.BOTTOM_NAV,
page: () => BottomNavigationScreen(),
binding: BottomNavBinding(),
children: [
GetPage(
name: _Paths.HOME,
page: () => const HomePage(),
binding: HomeBinding(),
),
GetPage(
name: _Paths.MY_INFO,
page: () => const MyInfoPage(),
binding: MyInfoBinding(),
),
GetPage(
name: _Paths.STORE,
page: () => const StorePage(),
binding: StoreBinding(),
),
GetPage(
name: _Paths.STORY,
page: () => const StoryPage(),
binding: StoryBinding(),
),
GetPage(
name: _Paths.CAFE,
page: () => const CafePage(),
binding: CafeBinding(),
),
],
),
...
마지막으로 BottomNavigation에 물려있는 Page들을 children에 물려(?)준다. ㅎㅎ 이렇게 하면 StatelessWidget가 아닌 GetView 를 extends 받은 View 를 만들 수 있다. 이와 같이 구성한 이유는 각각의 controller를 만들어주기 위함이다.. 물론 만드는 사람에 따라 BottomNavController을 전달할 수도 있겠지만...
이대로 진행하다가 문제시 수정하러옴 ㅜ.ㅜ