TabBar를 이용한 페이지 분기처리
1. lib/sub/firstPage.dart 파일 생성
TabBar_example이라는 이름으로 새로운 플러터 프로젝트 생성 -> lib 폴더 아래에 sub 폴더를 추가한 후 firstPage.dart파일을 만듦
2. lib/sub/firstPage.dart 파일 작성
import 'package:flutter/material.dart';
class FirstApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: Text('첫 번째 페이지'),
),
),
);
}
}
Stateless Widget을 상속받는 FirstApp 클래스를 생성한다.
그 다음 스캐폴드 위젯 안에 body Text에 '첫 번째 페이지' 라고 입력.
3. lib/sub/secondPage.dart 파일 작성
sub 폴더에 secondPage.dart 파일을 만들고 다음과 같이 코드 작성.
import 'package:flutter/material.dart';
class SecondApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: Text('두 번째 페이지'),
),
),
);
}
}
4. lib/main.dart 탭 컨드롤러 선언
TabBar 위젯을 사용하려면 탭 컨드롤러가 필요하다
=> main.dart 파일에서 작업
class _MyHomePageState extends State<MyHomePage> {
TabController controller;
@override
void initState() {
super.initState();
controller = TabController(length: 2, vsync: this);
TabController에서 length에 몇 개의 탭을 만들지 정하고,
vsync에는 탭이 이동했을 때 호출되는 콜백 함수를 어디서 처리할지 지정하도록 한다.
현재 코드에서 vsync:this 코드에서는 오류가 발생하는데 탭 컨트롤러는 여러 화면을 이동하는 역할을 하기 때문에 기본적으로 애니메이션을 사용한다. 그런데 this가 가리키는 _MyHomePageState 클래스에 이와 관련된 코드가 빠졌기 때문에 오류가 발생하는 것이다.
=> _MyHomePageState 클래스에 with 키워드를 추가하고 SingleTickerProviderStateMixin 클래스를 지정해준다.
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin{
TabController controller;
@override
void initState() {
super.initState();
controller = TabController(length: 2, vsync: this);
controller.addListener(() {
if(!controller.indexIsChanging){
print("이전 index, ${controller.previousIndex}");
print("현재 index, ${controller.index}");
print("전체 탭 길이, ${controller.length}");
}
});
}
with를 이용해 SingleTickerProviderStateMixin 클래스를 추가로 상속
=> 탭을 눌렀을 때 _MyHomePageState 클래스에서 애니메이션 동작을 처리할 수 있게 함
만약, SingleTickerProviderStateMixin 클래스를 상속에 포함하지 않으면 _MyHomePageState 클래스에서 TabController를 만들 수 없기 때문에 주의해야 한다.
TabBar를 이용한 페이지 분기처리 실습
main.dart
import 'package:flutter/material.dart';
import 'sub/firstPage.dart';
import 'sub/secondPage.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin{
TabController controller;
@override
void initState() {
super.initState();
controller = TabController(length: 2, vsync: this);
controller.addListener(() {
if(!controller.indexIsChanging){
print("이전 index, ${controller.previousIndex}");
print("현재 index, ${controller.index}");
print("전체 탭 길이, ${controller.length}");
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('TabBar Example'),
),
body: TabBarView(
children: <Widget>[FirstApp(), SecondApp()],
controller: controller,
),
bottomNavigationBar: TabBar(tabs: <Tab>[
Tab(icon: Icon(Icons.looks_one, color: Colors.blue),) ,
Tab(icon: Icon(Icons.looks_two, color: Colors.blue),)
], controller: controller,
)
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
이런식으로 length에 설정한 길이랑 TabBar에 있는 children의 페이지의 개수가 맞는게 중요!
맞지 않으면 에러가 발생할 수 있다.
리스트뷰를 이용한 앱 구현
1. 리스트 뷰를 이용한 앱 구현 절차
1. 이미지 추가
2. Animal 클래스 만들기
=> lib 폴더에 animalItem.dart 파일을 만듦
import 'package:flutter/material.dart';
class Animal{
String imagePath;
String animalName;
String kind;
bool flyExist = false;
Animal({ @required this.animalName,@required this.kind,@required this.imagePath, this.flyExist});
}
flyExist 논리형 변수 : false를 기본값으로 해놓고 새로운 Animal 객체를 선언할 때 이를 입력 받음.
매개변수 앞에 붙은 애너테이션 @required는 함수를 호출할 때 꼭 전달해야 하는 값이라는 뜻.
3. List 선언
List를 선언할 때 처음에는 빈값이므로 List.empty, 그리고 growable:true로 선언한다.
growable : 리스트가 가변적으로 증가할 수 있다는 것을 의미
4. Animal 객체 생성
=> 각 동물의 정보를 입력해 Animal 객체를 생성하고 동물목록 animalList에 추가한다.
@override
void initState() {
super.initState();
controller = TabController(length: 2, vsync: this);
animalList.add(Animal(animalName: "벌", kind: "곤충",
imagePath: "repo/images/bee.png"));
animalList.add(Animal(animalName: "고양이", kind: "포유류",
imagePath: "repo/images/cat.png"));
animalList.add(Animal(animalName: "젖소", kind: "포유류",
imagePath: "repo/images/cow.png"));
animalList.add(Animal(animalName: "강아지", kind: "포유류",
imagePath: "repo/images/dog.png"));
animalList.add(Animal(animalName: "여우", kind: "포유류",
imagePath: "repo/images/fox.png"));
animalList.add(Animal(animalName: "원숭이", kind: "영장류",
imagePath: "repo/images/monkey.png"));
animalList.add(Animal(animalName: "돼지", kind: "포유류",
imagePath: "repo/images/pig.png"));
animalList.add(Animal(animalName: "늑대", kind: "포유류",
imagePath: "repo/images/wolf.png"));
5. ListView.builder 사용
itemBuilder : BuildContext와 int를 반환
BuildContext : 위젯 트리에서 위젯의 위치를 알려줌
Int : 아이템의 순번을 의미
리스트뷰를 이용한 앱 구현 실습
lib/sub/firstPage.dart
import 'package:flutter/material.dart';
import '../animalItem.dart';
class FirstApp extends StatelessWidget {
final List<Animal> list; // Animal List 선언
FirstApp({Key key, this.list}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: ListView.builder(
itemBuilder: (context, position) {
return GestureDetector(
child: Card(
child: Row(
children: <Widget>[
Image.asset(list[position].imagePath , height: 100, width: 100, fit: BoxFit.contain,),
Text(list[position].animalName),
],
),
),
onTap: (){
AlertDialog dialog = AlertDialog(
content: Text('이 동물은 ${list[position].kind} 입니다' , style: TextStyle(fontSize: 30.0),),
);
showDialog(
context: context, builder: (BuildContext context) => dialog);
},
onLongPress: (){
list.removeAt(position);
},
);
},
itemCount: list.length),
),
),
);
}
}
lib/sub/secondPage.dart
import 'package:flutter/material.dart';
import '../animalItem.dart';
class SecondApp extends StatefulWidget {
List<Animal> list;
SecondApp({Key key, @required this.list}) : super(key: key);
@override
State<StatefulWidget> createState() => _SecondApp();
}
class _SecondApp extends State<SecondApp> {
int _radioValue = 0;
final nameController = TextEditingController();
bool flyExist = false;
var _imagePath;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
controller: nameController,
keyboardType: TextInputType.text,
maxLines: 1,
),
Row(children: <Widget>[
Radio(
value: 0, groupValue: _radioValue, onChanged: _radioChange),
Text('양서류'),
Radio(
value: 1, groupValue: _radioValue, onChanged: _radioChange),
Text('포충류'),
Radio(
value: 2, groupValue: _radioValue, onChanged: _radioChange),
Text('포유류'),
], mainAxisAlignment: MainAxisAlignment.spaceAround),
Row(children: <Widget>[
Text('날수 있나요?'),
Checkbox(
value: flyExist,
onChanged: (check) {
setState(() {
flyExist = check;
});
})
], mainAxisAlignment: MainAxisAlignment.spaceAround),
Container(
height: 100,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
GestureDetector(
child: Image.asset('repo/images/cow.png', width: 80),
onTap: () {
_imagePath = 'repo/images/cow.png';
},
),
GestureDetector(
child: Image.asset('repo/images/pig.png', width: 80),
onTap: () {
_imagePath = 'repo/images/pig.png';
},
),
GestureDetector(
child: Image.asset('repo/images/bee.png', width: 80),
onTap: () {
_imagePath = 'repo/images/bee.png';
},
),
GestureDetector(
child: Image.asset('repo/images/cat.png', width: 80),
onTap: () {
_imagePath = 'repo/images/cat.png';
},
),
GestureDetector(
child: Image.asset('repo/images/fox.png', width: 80),
onTap: () {
_imagePath = 'repo/images/fox.png';
},
),
GestureDetector(
child: Image.asset('repo/images/monkey.png', width: 80),
onTap: () {
_imagePath = 'repo/images/monkey.png';
},
),
],
),
),
RaisedButton(
child: Text('동물 추가하기'),
onPressed: () {
var animal = Animal(
animalName: nameController.value.text,
kind: getKind(_radioValue),
imagePath: _imagePath,
flyExist: flyExist);
AlertDialog dialog = AlertDialog(
title: Text('동물 추가하기'),
content: Text(
'이 동물은 ${animal.animalName} 입니다 또 동물의 종류는 ${animal.kind}입니다.\n 이 동물을 추가하시겠습니까?',
style: TextStyle(fontSize: 30.0),
),
actions: [
RaisedButton(onPressed: (){
widget.list.add(animal);
Navigator.of(context).pop();
} , child: Text('예'),),
RaisedButton(onPressed: (){
Navigator.of(context).pop();
} , child: Text('아니요'),),
],
);
showDialog(
context: context,
builder: (BuildContext context) => dialog);
})
],
),
),
),
);
}
_radioChange(int value) {
setState(() {
_radioValue = value;
});
}
getKind(int radioValue) {
switch (radioValue) {
case 0:
return "양서류";
case 1:
return "파충류";
case 2:
return "포유류";
}
}
}
lib/main.dart
import 'package:flutter/material.dart';
import 'animalItem.dart';
import 'sub/firstPage.dart';
import 'sub/secondPage.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin{
TabController controller;
List<Animal> animalList = List();
@override
void initState() {
super.initState();
controller = TabController(length: 2, vsync: this);
animalList.add(Animal(animalName: "벌", kind: "곤충",
imagePath: "repo/images/bee.png"));
animalList.add(Animal(animalName: "고양이", kind: "포유류",
imagePath: "repo/images/cat.png"));
animalList.add(Animal(animalName: "젖소", kind: "포유류",
imagePath: "repo/images/cow.png"));
animalList.add(Animal(animalName: "강아지", kind: "포유류",
imagePath: "repo/images/dog.png"));
animalList.add(Animal(animalName: "여우", kind: "포유류",
imagePath: "repo/images/fox.png"));
animalList.add(Animal(animalName: "원숭이", kind: "영장류",
imagePath: "repo/images/monkey.png"));
animalList.add(Animal(animalName: "돼지", kind: "포유류",
imagePath: "repo/images/pig.png"));
animalList.add(Animal(animalName: "늑대", kind: "포유류",
imagePath: "repo/images/wolf.png"));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Listview Example'),
),
body: TabBarView(
children: <Widget>[
FirstApp(list: animalList),
SecondApp(list: animalList)
],
controller: controller,
),
bottomNavigationBar: TabBar(tabs: <Tab>[
Tab(icon: Icon(Icons.looks_one, color: Colors.blue),) ,
Tab(icon: Icon(Icons.looks_two, color: Colors.blue),)
], controller: controller,
)
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
'앱 프로그래밍 > 플러터(Flutter)' 카테고리의 다른 글
Firebase 설정 및 적용 (2) | 2025.01.24 |
---|---|
서브페이지와 네트워크 통신 구현하기 (3) | 2025.01.20 |
앱 레이아웃 구성하기 (1) | 2025.01.15 |
플러터 기초 위젯 사용하기 (1) | 2025.01.13 |
다트 문법 기초 (0) | 2025.01.13 |