728x90
CampusView 기획
- Flutter를 사용해서 SESAC 커뮤니티 어플을 개발 진행중인데 캠퍼스의 정보들을 볼 수 있는 CampusView를 아래 이미지와 같이 기획하였다.
- CampusView는 DynamicTabBar로 구성되어 있어 기능을 개발 하였다.
- DynamicTabBar의 기능
- 사용자가 스크롤을 할 때 감지하여 Tab이 자동으로 이동
- 사용자가 Tab을 눌렀을 때 설정한 곳을 자동 Scroll
DynamicTabBar 개발
일단 Tab을 눌렀을때 해당 위치로 이동하려면 Tab을 관리하는 위젯과 클래스를 찾아야한다.
그렇게 해서 나온 것이 ScrollController 와 DefaultTabController!
https://flutter.github.io/assets-for-api-docs/assets/material/tabs.mp4
ScrollController
- Flutter에서 스크롤 가능한 위젯이 스크롤 위치를 제어하고 이벤트를 감지하는 클래스.
- 현재 스크롤 위치를 가져오거나 원하는 스크롤 위치로 이동할 때 필요한 클래스이다!
- 스크롤이 발생할때마다 스크롤 포지션을 알려준다고 한다!
- 자세한 내용은 공식 문서 하이퍼 링크를 달았으니 공식문서에서 보면 된다..!
DefaultTabController
- 탭 컨트롤러를 TabBar or TabBarView와 공유하는데 사용되는 상속 위젯이다!
- length와 child Widget이 필수값이고 아래 영상 처럼 탭을 눌렀을때 자동으로 이동하는데 사용한다.
https://flutter.github.io/assets-for-api-docs/assets/material/tabs.mp4
Builder
- 빌더는 상위 위젯의 빌드 컨텍스트에 동일한 빌드 메서드를 작성할 경우 사용한다.
- 콜백을 사용하여 위젯의 하위 항목을 생성하는 stateless utility widget이다.
- 새로운 위젯 클래스를 정의하지 않고 Builder를 이용해서 Widget의 child를 만든다.
- 나는 DefaultTabController를 하위 widget 트리에 동일한 기능을 사용하고 싶어 사용했다..!
- 코드를 한 번 봐보자 tabContext에 context를 넣어주고 DefaultTabController를 사용해서 DefaultTabController에 있는 animationTo 메서드를 사용한다.
/// Animate To Tab
void animateToTab() {
late RenderBox box;
for (var i = 0; i < campusCategories.length; i++) {
box = campusCategories[i].currentContext!.findRenderObject() as RenderBox;
Offset position = box.localToGlobal(Offset.zero);
if (scrollController.offset >= position.dy) {
DefaultTabController.of(tabContext!).animateTo(
i,
duration: const Duration(milliseconds: 100),
);
}
}
}
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Builder(
builder: (BuildContext context) {
tabContext = context;
),
);
},
),
);
}
GlobalKey()
- 위젯의 상태를 관리하고 특정 위젯에 고유한 식별자를 제공한다
- 나는 스크롤을 하려는 위젯에게 식별자를 부여해서 Scroll할 대상을 찾는다..!
- 글로벌키는 주로 위젯의 상태를 접근하거나 애니메이션 및 스크롤 동작을 제어하는데 사용된다
- 나는 campusCategories에 3개의 탭을 사용하기 때문에 총 3개의 GlobalKey를 줄 것이다.
/// Categories keys
final List<GlobalKey> campusCategories = [
GlobalKey(),
GlobalKey(),
GlobalKey(),
];
AnimaToTab()
- Tab을 자동으로 이동시키는 함수로 scrollController.offset이 currentContext의 요소 하나(campusCategories[i])가 현재 스크롤 위치보다 낮다면 Tab을 campusCategories[i]에 맞는 요소로 이동시켜주는 함수이다.
/// Animate To Tab
void animateToTab() {
late RenderBox box;
for (var i = 0; i < campusCategories.length; i++) {
box = campusCategories[i].currentContext!.findRenderObject() as RenderBox;
Offset position = box.localToGlobal(Offset.zero);
if (scrollController.offset >= position.dy) {
DefaultTabController.of(tabContext!).animateTo(
i,
duration: const Duration(milliseconds: 100),
);
}
}
}
scrollToIndex()
- Tab을 눌렀을때 **campusCategories[index]**에 맞는 위치로 스크롤 되는 함수이다.
- **ensureVisible()**를 사용해서 스크롤을 하여 주어진 컨테스트가 표시되게 한다. 여기서는 **campusCategories[index]**가 주어진 컨텍스트이니 Tab을 눌렀을때 해당 index를 넘겨주면 된다..!
- onTap: (int index) => scrollToIndex(index)를 TabBar에 넣어줘서 Tab할때마다 자동으로 스크롤 되게 만들었다
void scrollToIndex(int index) async {
scrollController.removeListener(animateToTab);
final categories = campusCategories[index].currentContext!;
await Scrollable.ensureVisible(
categories,
duration: const Duration(milliseconds: 600),
);
scrollController.addListener(animateToTab);
}
TabBar(
indicatorColor: AppColorsTheme().gfMainColor,
labelColor: AppColorsTheme().gfMainColor,
splashFactory: NoSplash.splashFactory,
tabs: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 15),
child: Text(
'위치',
style: AppTextsTheme.main().gfTitle1.copyWith(
color: AppColorsTheme().gfMainColor,
),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 15),
child: Text(
'운영시간',
style: AppTextsTheme.main().gfTitle1.copyWith(
color: AppColorsTheme().gfMainColor,
),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 15),
child: Text(
'공간소개',
style: AppTextsTheme.main().gfTitle1.copyWith(
color: AppColorsTheme().gfMainColor,
),
),
),
],
onTap: (int index) => scrollToIndex(index),
),
위의 메서드들을 이용해서 스크롤시 내가 설정한 컨텍스트 값을 감지하기 위해 _buildTitle에 Key값을 부여했다. 그래서 _buildCategoryTitle이 탭을 누를시 자동으로 스크롤되거나 스크롤시 탭이 이동되는 기능을 만들었다!!
/// Category Title
Widget _buildCategoryTitle(String title, int index) {
return Padding(
key: campusCategories[index],
padding: const EdgeInsets.only(bottom: 8, top: 18),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: AppTextsTheme.main().gfTitle1.copyWith(
color: AppColorsTheme().gfBlackColor,
),
),
],
),
],
),
);
}
NaverMap URL Scheme 사용 방법
CampusView안에는 지도 이미지가 있습니다.
이 이미지를 누르게 되면 네이버 지도로 이동 후 캠퍼스 위치를 표시합니다.
네이버 지도 앱 URL Scheme을 사용하여 사용자 앱이나 웹 페이지에서 네이버 지도 앱 호출함으로써 네이버 지도 기능을 사용할 수 있습니다.
네이버 지도 앱 URL Scheme은 nmap://으로 시작하며, 구문의 형식은 다음과 같습니다.
nmap:// 네이버 지도 앱 접근 URL Scheme 필수
actionPath | 호출 액션 | 필수 |
parameter=value | 호출 액션에 따른 입력 파라미터와 입력 값 | 액션에 따라 다름. 자세한 내용은 URL Scheme 사용 확인 |
사전 설정
- info.plist에 LSApplicationQueriesSchemes를 생성하고 nmap을 추가해 주십시오.
<key>LSApplicationQueriesSchemes</key>
<array>
<string>nmap</string>
</array>
- search?query를 이용해서 검색어를 지정해줍니다. 저는 Uri.encodeCmponenet를 사용하여 String을 Url 인코딩하였습니다 만약 네이버 지도 어플이 없다면 네이버 지도 웹으로 이동하게 하였습니다.
onPressed: () async {
// 주소를 URL 인코딩
var encodedAddress = Uri.encodeComponent(CampusExample().gwanack.address['NaverMapURLScheme']!);
var url = 'nmap://search?query=$encodedAddress';
if (await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url));
} else {
var web = CampusExample().gwanack.address['NaverWebURL']!;
await launchUrl(Uri.parse(web));
}
},
코드
https://github.com/bulmang/green_field
728x90
'Flutter' 카테고리의 다른 글
[Flutter] WhiteScreen(무선빌드) & Load Sequence Flutter UI (2) | 2024.11.21 |
---|---|
[피플] 디자인 QA (0) | 2023.02.24 |
[피플]플러터 Google Map Api 사용 방법 (0) | 2023.02.20 |
[피플] 응원해요 기능 수정 (0) | 2023.02.08 |
[피플] - 이슈 수정 (git, 협업능력) (0) | 2023.02.03 |