aspe

Flutter 프로젝트 '남음' - Bottom sheet 구현하기 본문

Flutter/프로젝트

Flutter 프로젝트 '남음' - Bottom sheet 구현하기

aspe 2023. 5. 25. 10:37

현재 내 주변에 어떤 가게들이 있는지 확인하기 위해 Google map을 사용합니다.

Google map위에 있는 Marker를 클릭하거나, sheet을 드래그하면 화면의 하단에서 sheet이 올라오게 구현 할 예정입니다.

(Marker를 클릭하는 것은 간단하니 우선 드래그를 구현해봅니다)

 

 

우선 Google map이 있는 tab을 Stack으로 감싸줍니다. 그래야 Positioned를 사용해 bottom에 sheet를 위치 시킬 수 있습니다.

 

Widget _iOSBuilder(BuildContext context) {
return SafeArea(
child: Stack(
children: [
Column(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: userLocation == null
? const Center(
child: Text("Loading..."),
)
: GoogleMap(
markers: Set.from(_markers),
mapType: MapType.normal,
initialCameraPosition: userLocation!,
myLocationEnabled: true,
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
)),
],
),
const MapBottomSheet()
],
),
);
}

24 line이 우리가 구현 할 위젯입니다.

MapBottomSheet은 StatefulWidget으로 설정합니다. BottomSheet을 AnimatedContainer로 구현 할 것이기 때문에 state가 필요합니다.

기본 동작은 다음과 같습니다. Bottom sheet을 드래그 하여 높이가 100 ~ 500 사이이면 높이를 600으로 설정합니다. 그리고 다시 유저의 드래그로 500 ~ 550 사이로 들어오면  높이를 100으로 설정합니다.

 

import 'package:flutter/material.dart';
class MapBottomSheet extends StatefulWidget {
const MapBottomSheet({super.key});
@override
State<MapBottomSheet> createState() => _MapBottomSheetState();
}
class _MapBottomSheetState extends State<MapBottomSheet> {
late double _height;
final double _lowLimit = 50;
final double _highLimit = 600;
final double _upThresh = 100;
final double _boundary = 500;
final double _downThresh = 550;
/// 100 -> 600, 550 -> 100 으로 애니메이션이 진행 될 때,
/// 드래그로 인한 _height의 변화 방지
bool _longAnimation = false;
@override
void initState() {
super.initState();
_height = _lowLimit;
}
@override
Widget build(BuildContext context) {
return Positioned(
bottom: 0.0,
child: GestureDetector(
onVerticalDragUpdate: ((details) {
// delta: y축의 변화량, 우리가 보기에 위로 움직이면 양의 값, 아래로 움직이면 음의 값
double? delta = details.primaryDelta;
if (delta != null) {
/// Long Animation이 진행 되고 있을 때는 드래그로 높이 변화 방지,
/// 그리고 low limit 보다 작을 때 delta가 양수,
/// High limit 보다 크거나 같을 때 delta가 음수이면 드래그로 높이 변화 방지
if (_longAnimation ||
(_height <= _lowLimit && delta > 0) ||
(_height >= _highLimit && delta < 0)) return;
setState(() {
/// 600으로 높이 설정
if (_upThresh <= _height && _height <= _boundary) {
_height = _highLimit;
_longAnimation = true;
}
/// 100으로 높이 설정
else if (_boundary <= _height && _height <= _downThresh) {
_height = _lowLimit;
_longAnimation = true;
}
/// 기본 작동
else {
_height -= delta;
}
});
}
}),
child: AnimatedContainer(
curve: Curves.bounceOut,
onEnd: () {
if (_longAnimation) {
setState(() {
_longAnimation = false;
});
}
},
duration: const Duration(milliseconds: 400),
decoration: const BoxDecoration(
boxShadow: [BoxShadow(blurRadius: 6, spreadRadius: 0.7)],
color: Colors.white,
borderRadius:
BorderRadius.vertical(top: Radius.circular(20))),
width: MediaQuery.of(context).size.width,
height: _height,
child: Column(
children: [
const SizedBox(
height: 20,
),
Container(
width: 70,
height: 4.5,
decoration: const BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.all(Radius.circular(10))),
),
],
),
)));
}
}

AnimatedContainer의 onEnd에서 _longAnimation에 대한 처리를 꼭 해줘야 정상적으로 작동합니다.

 

가게 정보들이 해당 AnimatedContainer에 들어오는 건 곧 포스팅 합니다.

'Flutter > 프로젝트' 카테고리의 다른 글

Flutter - 남음  (0) 2023.05.25
Flutter 프로젝트 - Nameum  (0) 2023.02.17
Flutter - Nameum - 추천 검색바 만들기  (0) 2023.02.17
Comments