지난주에는 위젯 자체와 관련된 유용한 유틸 위젯을 다뤘다면, 오늘은 전반적으로 유용한 위젯들을 다뤄보고자 한다.
여러 페이지를 하나의 페이지에서 슬라이드하여 이용할 수 있도록 하는 위젯이다. 기본적으로 수평 방향으로 슬라이드되며, scrollDirection
옵션을 통해 스크롤 방향을 변경할 수 있다. 또한 PageController
를 통해 해당 뷰페이지와 관련된 설정을 진행할 수 있다.
// PageView 테스트
class PageViewTest extends StatelessWidget {
const PageViewTest({Key? key}) : super(key: key);
Widget build(BuildContext context) {
// PageView와 관련된 설정을 할 수 있는 컨트롤러
// 시작 페이지를 2번째 페이지로 설정
// 각 페이지가 차지하는 영역을 1.5배로 설정
final pageController =
PageController(initialPage: 2, viewportFraction: 1.5);
return Scaffold(
body: PageView(
controller: pageController, // 컨트롤러 지정
scrollDirection: Axis.vertical, // 수직 스크롤
children: [
Container(
color: Colors.red,
child: Center(
child: Image.asset(
'images/pic6.jpeg',
)),
),
Container(
color: Colors.green,
child: Center(
child: Image.asset(
'images/pic7.jpeg',
)),
),
Container(
color: Colors.blue,
child: Center(
child: Image.asset(
'images/pic8.jpeg',
)),
),
],
),
);
}
}
위젯이 드래그 가능하도록 해주는 위젯으로, 위젯을 드래그할 때, 드래그 중일때 배경, 데이터, 타겟 등을 지정할 수 있다.
class DragableTest extends StatefulWidget {
const DragableTest({Key? key}) : super(key: key);
State<DragableTest> createState() => _DragableTestState();
}
class _DragableTestState extends State<DragableTest> {
int acceptedData = 0;
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Draggable( // 드래그 가능한 위젯
data: 10, // 드래그할 때 전달할 데이터
// 드래그 중인 위젯
feedback: Container(
width: 100,
height: 100,
color: Colors.green,
),
// 드래그 중일때 백그라운드에 표시되는 위젯
childWhenDragging: Container(
width: 100,
height: 100,
color: Colors.blue,
),
// 드래그 가능한 위젯
child: Container(
width: 100,
height: 100,
color: Colors.red,
),
),
// 드래그 가능한 위젯이 놓여질 수 있는 곳
// 해당 타겟은 int 타입의 데이터만 받을 수 있음
DragTarget<int>(
builder: (
BuildContext context,
List<dynamic> accepted,
List<dynamic> rejected,
) {
return Container(
height: 100.0,
width: 100.0,
color: Colors.cyan,
child: Center(
child: Text('Value is updated to: $acceptedData'),
),
);
},
onAccept: (int data) { // 드래그 가능한 위젯이 놓여졌을 때 호출
setState(() {
acceptedData += data;
});
},
),
],
),
),
);
}
}
비동기를 위해 사용하는 위젯으로, 나중에 완료될 것으로 가정하고 미리 빌드 후 향후 비동기 함수의 결과가 왔을 때 결과값을 보여줄 수 있도록 한다. 네트워크 통신 등에 자주 사용한다.
아래 예시는 의도적으로 3초를 대기하게 한 뒤 결과를 반환하는 것이다.
class FutureBuilderTest extends StatelessWidget {
const FutureBuilderTest({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FutureBuilder(
future: _getData(), // 비동기 처리를 수행할 함수
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
// 데이터가 수신되었을 때
return Image.asset(snapshot.data);
} else if (snapshot.hasError) {
// 에러가 발생했을 때
return Text('Error: ${snapshot.error}');
} else {
// 데이터가 수신되기 전
return const CircularProgressIndicator();
}
},
),
),
);
}
// 데이터를 가져오는 비동기 함수
// 반환형이 Future이고 함수명 뒤에 async가 붙는다.
Future<String> _getData() async {
await Future.delayed(const Duration(seconds: 3));
return 'images/pic12.jpeg';
}
}
위에서 사용한 FutureBuilder
와 거의 동일하지만 약간 다르다. FutureBuilder
는 일회성으로 발생하는 비동기 처리에 사용하고, StreamBuilder
는 다회성 데이터 획득에 사용된다. 여기서 스트림이라고 하는것은 데이터가 들어오고 나가는 통로를 의미한다.
아래 예시는 여러 이미지가 순서대로 전달되는 것이다.
// StreamBuilder 테스트
class StreamBuilderTest extends StatelessWidget {
const StreamBuilderTest({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: StreamBuilder(
stream: _getStream(), // 비동기 처리를 수행할 함수
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
// 데이터가 수신되었을 때
return Image.asset(snapshot.data);
} else if (snapshot.hasError) {
// 에러가 발생했을 때
return Text('Error: ${snapshot.error}');
} else {
// 데이터가 수신되기 전
return const CircularProgressIndicator();
}
},
),
),
);
}
Stream<String> _getStream() async* {
await Future.delayed(const Duration(seconds: 3));
for (int i = 1; i < 10; i++) {
await Future.delayed(const Duration(seconds: 1));
yield "images/pic$i.jpeg";
}
}
}