방명록
- [Flutter] "Do it! 플러터 앱 프로그래밍" - 탭바와 리스트 만들기 | 탭바, 리스트뷰2022년 02월 13일 01시 50분 04초에 업로드 된 글입니다.작성자: DandyNow728x90반응형
플러터 위젯 사용법
"조준수. (2021). Do it! 플러터 앱 프로그래밍. 이지스퍼블리싱", 5장을 실습하였다. 탭바와 리스트뷰는 스마트폰 화면에 정보를 효과적으로 보여주는 방법이다.
탭바
탭바는 화면 위나 아래에 놓을 수 있다. 모든 내용과 기능을 한 화면에 보여줄 수 없을 때 탭(tab)으로 연결한 탭바를 이용하면 관련 있는 내용끼리 묶을 수 있다.
// 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: 'TabBar Example', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { @override State<StatefulWidget> createState() { return _MyHomePageState(); } } // SingleTickerProviderStateMixin는 _MyHomePageState 클래스에서 애니메이션 동작을 처리할 수 있게 한다. vsync: this 코드의 오류가 사라진다. class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { TabController? controller; @override // initState함수를 이용해 TabController를 선언한다. void initState() { super.initState(); // length는 탭수, vsync는 탭이 이동했을 때 호출되는 콜백 함수를 어디서 처리할지 지정한다. controller = TabController(length: 2, vsync: this); } @override Widget build(BuildContext context) { // Scaffold로 appBar, body, bottomNavigation를 선언한다. return Scaffold( appBar: AppBar( title: Text('TabBar Example'), ), // body에 TabBarView을 bottomNavigationBar에 TabBar 위젯을 넣는다. 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(); } }
// 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('첫 번째 페이지'), ), ), ); } }
// 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('두 번째 페이지'), ), ), ); } }
리스트뷰
대부분의 앱이 세로로 긴 목록으로 화면을 구성한다. 이때 리스트뷰를 이용해 많은 정보를 보여 줄 수 있다.
// 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: 'Listview Example', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { @override State<StatefulWidget> createState() { return _MyHomePageState(); } } // SingleTickerProviderStateMixin는 _MyHomePageState 클래스에서 애니메이션 동작을 처리할 수 있게 한다. vsync: this 코드의 오류가 사라진다. class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { TabController? controller; List<Animal> animalList = new List.empty( growable: true); // growable 이 리스트가 가변적으로 증가할 수 있다는 것을 의미한다. // animalList를 추가한 initState() 함수 @override void initState() { super.initState(); // length는 탭수, vsync는 탭이 이동했을 때 호출되는 콜백 함수를 어디서 처리할지 지정한다. controller = TabController(length: 2, vsync: this); animalList.add(Animal( animalName: "고양이", kind: "포유류", imagePath: "repo/images/cat.png")); animalList.add(Animal( animalName: "병아리", kind: "조류", imagePath: "repo/images/chicken.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/duck.png")); animalList.add(Animal( animalName: "개구리", kind: "양서류", imagePath: "repo/images/frog.png")); animalList.add(Animal( animalName: "팬더", kind: "포유류", imagePath: "repo/images/panda.png")); animalList.add(Animal( animalName: "돼지", kind: "포유류", imagePath: "repo/images/pig.png")); animalList.add(Animal( animalName: "토끼", kind: "포유류", imagePath: "repo/images/rabbit.png")); } @override Widget build(BuildContext context) { // Scaffold로 appBar, body, bottomNavigation를 선언한다. return Scaffold( appBar: AppBar( title: Text('Listview Example'), ), // 탭바 처리 body: TabBarView( children: <Widget>[ FirstApp(list: animalList), 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(); } }
// animalItem.dart import 'package:flutter/material.dart'; // 동물 추가 클래스 class Animal { String? imagePath; String? animalName; String? kind; bool? flyExist = false; // false를 기본값을 지정 // Animal 생성자, required는 함수 호출시 꼭 전달해야 하는 값을 의미한다. Animal( {required this.animalName, required this.kind, required this.imagePath, this.flyExist}); }
// sub/firstPage.dart import 'package:flutter/material.dart'; import '../animalItem.dart'; // Animal 클래스를 사용하기 위해 import class FirstApp extends StatelessWidget { final List<Animal>? list; FirstApp({Key? key, this.list}) : super(key: key); // 이 생성자가 없으면 final의 list에 오류가 표시된다. @override Widget build(BuildContext context) { return Scaffold( body: Container( child: Center( // 전달 받은 list를 통해서 리스트뷰를 만든다. ListView.builder를 사용하려면 itemBuilder가 필요하다. child: ListView.builder( itemBuilder: (context, position) { // GestureDetector로 터치 이벤트 처리 return GestureDetector( child: Card( child: Row(children: <Widget>[ Image.asset( // position은 리스트에서 아이템의 위치를 나타낸다. list![position].imagePath!, height: 100, width: 100, fit: BoxFit.contain, // 이미지 안 잘리는 최대 크기, 비율 유지 ), Text(list![position].animalName!) ]), ), // GestureDetector 위젯의 onTap으로 한 번 터치했을 때 알림 창(showDialog)을 띄우도록 한다. onTap: () { AlertDialog dialog = AlertDialog( content: Text( '이 동물은 ${list![position].kind}입니다.', style: TextStyle(fontSize: 30.0), ), ); showDialog( context: context, builder: (BuildContext context) => dialog); }, ); }, // 아이템 개수만큼만 스크롤할 수 있게 제한 itemCount: list!.length, ), ), ), ); } }
// sub/secondPage.dart import 'package:flutter/material.dart'; import '../animalItem.dart'; // Animal 클래스를 사용하기 위해 import class SecondApp extends StatefulWidget { @override State<SecondApp> createState() => _SecondApp(); List<Animal>? list; SecondApp({Key? key, @required this.list}) : super(key: key); } class _SecondApp extends State<SecondApp> { final nameController = TextEditingController(); int? _radioValue = 0; bool? flyExist = false; String? _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( mainAxisAlignment: MainAxisAlignment.spaceAround, // 위젯들을 양쪽 여백 사이에 균일하게 배치 children: <Widget>[ // 라디오 버튼을 만들려면 value(인덱스 값), groupValue(그룹화: 라디오 버튼은 값은 그룹에서 하나만 선택할 수 있다), onChanged(이벤트 처리) 값을 지정해야 한다. Radio( value: 0, groupValue: _radioValue, onChanged: _radioChange), Text('양서류'), Radio( value: 1, groupValue: _radioValue, onChanged: _radioChange), Text('조류'), Radio( value: 2, groupValue: _radioValue, onChanged: _radioChange), Text('포유류'), ], ), // 날 수 있는지 선택 체크박스 Row( mainAxisAlignment: MainAxisAlignment.spaceAround, // 위젯들을 양쪽 여백 사이에 균일하게 배치 children: <Widget>[ Text('날 수 있나요?'), Checkbox( value: flyExist, onChanged: (bool? check) { setState( () { flyExist = check; }, ); }), ], ), // 기존 Row를 Container로 교체한다. 스크롤 달아 overflow 해결하기 위함이다. // Row( // mainAxisAlignment: MainAxisAlignment.spaceAround, // 위젯들을 양쪽 여백 사이에 균일하게 배치 Container( height: 100, child: ListView( // 수평 스크롤 scrollDirection: Axis.horizontal, children: <Widget>[ GestureDetector( child: Image.asset( 'repo/images/cat.png', width: 80, ), onTap: () { _imagePath = 'repo/images/cat.png'; }, ), GestureDetector( child: Image.asset( 'repo/images/cow.png', width: 80, ), onTap: () { _imagePath = 'repo/images/cow.png'; }, ), GestureDetector( child: Image.asset( 'repo/images/dog.png', width: 80, ), onTap: () { _imagePath = 'repo/images/dog.png'; }, ), GestureDetector( child: Image.asset( 'repo/images/duck.png', width: 80, ), onTap: () { _imagePath = 'repo/images/duck.png'; }, ), GestureDetector( child: Image.asset( 'repo/images/frog.png', width: 80, ), onTap: () { _imagePath = 'repo/images/frog.png'; }, ), GestureDetector( child: Image.asset( 'repo/images/panda.png', width: 80, ), onTap: () { _imagePath = 'repo/images/panda.png'; }, ), GestureDetector( child: Image.asset( 'repo/images/pig.png', width: 80, ), onTap: () { _imagePath = 'repo/images/pig.png'; }, ), GestureDetector( child: Image.asset( 'repo/images/rabbit.png', width: 80, ), onTap: () { _imagePath = 'repo/images/rabbit.png'; }, ), ], ), ), ElevatedButton( 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: [ ElevatedButton( onPressed: () { widget.list?.add(animal); Navigator.of(context).pop(); }, child: Text('예'), ), ElevatedButton( onPressed: () { Navigator.of(context).pop(); }, child: Text('아니오'), ), ], ); showDialog( context: context, builder: (BuildContext context) => dialog); }), ], ), ), ), ); } // Radio의 onChanged에서 사용하는 함수 _radioChange(int? value) { setState( () { _radioValue = value; }, ); } getKind(int? radioValue) { switch (radioValue) { case 0: return "양서류"; case 1: return "조류"; case 2: return "포유류"; } } }
728x90반응형'언어·프레임워크 > Flutter' 카테고리의 다른 글
다음글이 없습니다.이전글이 없습니다.댓글