방명록
- [Flutter] "Do it! 플러터 앱 프로그래밍" - iOS 스타일로 플러터 앱 만들기 | 쿠퍼티노 위젯2022년 02월 14일 01시 04분 38초에 업로드 된 글입니다.작성자: DandyNow728x90반응형
"조준수. (2021). Do it! 플러터 앱 프로그래밍. 이지스퍼블리싱", 6장을 실습하였다. iOS 스타일을 적용할 수 있는 쿠퍼티노 위젯을 다루었다. 5장에서 작성한 코드를 활용하여 실습하였기 때문에 5장과 6장의 코드가 섞여있다. 주석을 볼 때 이를 감안해야 한다. "동물 추가하기 화면 완성하기" 실습의 158쪽 2단계 CuperthinoSegmentedControl 이후 코드에서 에러가 발생해 애를 많이 먹었다. 본문의 코드와 여러 차례 대조해봤지만 별 문제가 없어 답답했다. 결국 저자의 깃허브를 참조했다.
dependencies 설정
pubspec.yaml의 dependencies 설정은 패키지를 가져와서 사용할 수 있도록 하는 방법이다. 쿠퍼티노 아이콘 라이브러리 패키지는 pubspec.yaml에 기본으로 세팅되어 있다.
// pubspec.yaml dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.2 // 쿠퍼티노 아이콘 라이브러리
쿠퍼티노 탭바
// cupertinoMain.dart import 'package:flutter/cupertino.dart'; // iOS 스타일 화면을 구현하기 위한 쿠퍼티노 준비 class CupertinoMain extends StatefulWidget { @override State<StatefulWidget> createState() { return _CupertinoMain(); } } class _CupertinoMain extends State<CupertinoMain> { CupertinoTabBar? tabBar; @override void initState() { super.initState(); tabBar = CupertinoTabBar(items: <BottomNavigationBarItem>[ BottomNavigationBarItem(icon: Icon(CupertinoIcons.home)), BottomNavigationBarItem(icon: Icon(CupertinoIcons.add)), ]); } @override Widget build(BuildContext context) { // iOS 스타일은 MaterialApp 대신 CupertinoApp 사용 return CupertinoApp( // 쿠퍼티노 위젯의 이름에는 항상 'Cupertino' 접두어가 붙는다. home: CupertinoTabScaffold( tabBar: tabBar!, tabBuilder: (context, value) { if (value == 0) { return Container( child: Center( child: Text('cupertino tab 1 '), ), ); } else { return Container( child: Center( child: Text('cupertino tab 2'), ), ); } }), ); } }
// main.dart import 'package:flutter/material.dart'; import './animalItem.dart'; import 'sub/firstPage.dart'; import 'sub/secondPage.dart'; import './cupertinoMain.dart'; // iOS 디자인 쿠퍼티노 적용 // void main() => runApp(MyApp()); void main() => runApp(CupertinoMain()); // iOS 디자인 쿠퍼티노 적용 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(); } }
쿠퍼티노 리스트뷰
// cupertinoMain.dart import 'package:flutter/cupertino.dart'; import 'animalItem.dart'; import 'iosSub/cupertinoFirstPage.dart'; // iOS 스타일 화면을 구현하기 위한 쿠퍼티노 준비 class CupertinoMain extends StatefulWidget { @override State<StatefulWidget> createState() { return _CupertinoMain(); } } class _CupertinoMain extends State<CupertinoMain> { CupertinoTabBar? tabBar; List<Animal> animalList = List.empty(growable: true); @override void initState() { super.initState(); tabBar = CupertinoTabBar(items: <BottomNavigationBarItem>[ BottomNavigationBarItem(icon: Icon(CupertinoIcons.home)), BottomNavigationBarItem(icon: Icon(CupertinoIcons.add)), ]); 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) { // iOS 스타일은 MaterialApp 대신 CupertinoApp 사용 return CupertinoApp( // 쿠퍼티노 위젯의 이름에는 항상 'Cupertino' 접두어가 붙는다. home: CupertinoTabScaffold( tabBar: tabBar!, tabBuilder: (context, value) { if (value == 0) { // return Container( // child: Center( // child: Text('cupertino tab 1 '), // ), // ); return CupertinoFirstPage( animalList: animalList, ); } else { return Container( child: Center( child: Text('cupertino tab 2'), ), ); } }), ); } }
// iosSub/cupertinoFirstPage.dart import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import '../animalItem.dart'; class CupertinoFirstPage extends StatelessWidget { final List<Animal> animalList; const CupertinoFirstPage({Key? key, required this.animalList}) : super(key: key); @override Widget build(BuildContext context) { return CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( middle: Text('동물 리스트'), ), child: ListView.builder( itemBuilder: (context, index) { // 쿠퍼티노에는 카드 위젯이 없다. 따라서 Container를 이용한다. return Container( padding: EdgeInsets.all(5), height: 100, child: Column( children: <Widget>[ Row( children: <Widget>[ Image.asset( animalList[index].imagePath!, height: 80, width: 80, fit: BoxFit.contain, ), Text(animalList[index].animalName!) ], ), Container( height: 2, color: CupertinoColors.black, ), ], ), ); }, itemCount: animalList.length, )); } }
세그먼트 위젯
세그먼트 위젯은 머티리얼의 라디오 버튼처럼 여럿 중 하나를 선택할 수 있다.
// cupertinoMain.dart import 'package:flutter/cupertino.dart'; import 'package:flutter_listview_example/iosSub/cupertinoSecondPage.dart'; import 'animalItem.dart'; import 'iosSub/cupertinoFirstPage.dart'; // iOS 스타일 화면을 구현하기 위한 쿠퍼티노 준비 class CupertinoMain extends StatefulWidget { @override State<StatefulWidget> createState() { return _CupertinoMain(); } } class _CupertinoMain extends State<CupertinoMain> { CupertinoTabBar? tabBar; List<Animal> animalList = List.empty(growable: true); @override void initState() { super.initState(); tabBar = CupertinoTabBar(items: <BottomNavigationBarItem>[ BottomNavigationBarItem(icon: Icon(CupertinoIcons.home)), BottomNavigationBarItem(icon: Icon(CupertinoIcons.add)), ]); 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) { // iOS 스타일은 MaterialApp 대신 CupertinoApp 사용 return CupertinoApp( // 쿠퍼티노 위젯의 이름에는 항상 'Cupertino' 접두어가 붙는다. home: CupertinoTabScaffold( tabBar: tabBar!, tabBuilder: (context, value) { if (value == 0) { // return Container( // child: Center( // child: Text('cupertino tab 1 '), // ), // ); return CupertinoFirstPage( animalList: animalList, ); } else { // return Container( // child: Center( // child: Text('cupertino tab 2'), // ), // ); return CupertinoSecondPage( animalList: animalList, ); } }), ); } }
// iosSub/cupertinoSecondPage.dart import 'package:flutter/cupertino.dart'; import '../animalItem.dart'; class CupertinoSecondPage extends StatefulWidget { final List<Animal>? animalList; const CupertinoSecondPage({Key? key, required this.animalList}) : super(key: key); @override State<StatefulWidget> createState() { return _CupertinoSecondPage(); } } class _CupertinoSecondPage extends State<CupertinoSecondPage> { TextEditingController? _textController; // 동물 이름 입력할 텍스트 필드 int _kindChoice = 0; // 동물 종류 bool _flyExist = false; // 날개 유무 String? _imagePath; // 동물 이미지 // Map으로 세그먼트 내용 구성 Map<int, Widget> segmentWidgets = { // SizedBox는 영역을 만들어 주는 위젯 0: SizedBox( child: Text('양서류', textAlign: TextAlign.center), width: 80, // 텍스트만 넣으면 세그먼트가 너무 좁기 때문에 width를 주었다. ), 1: SizedBox( child: Text('조류', textAlign: TextAlign.center), width: 80, ), 2: SizedBox( child: Text('포유류', textAlign: TextAlign.center), width: 80, ), }; @override void initState() { super.initState(); _textController = TextEditingController(); } @override Widget build(BuildContext context) { return CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( middle: Text('동물 추가'), ), child: Container( child: Center( child: Column( children: <Widget>[ Padding( padding: EdgeInsets.all(10), child: CupertinoTextField( // iOS 스타일의 입력 창 controller: _textController, // _textController는 iniState() 함수에 선언하였다. keyboardType: TextInputType.text, maxLines: 1, // 최대로 입력할 수 있는 줄 수 ), ), CupertinoSegmentedControl( padding: EdgeInsets.only(bottom: 20, top: 20), groupValue: _kindChoice, children: segmentWidgets, onValueChanged: (int? value) { setState(() { _kindChoice = value!; }); }), Row( children: <Widget>[ Text('날개가 존재합니까?'), CupertinoSwitch( value: _flyExist, onChanged: (value) { setState(() { _flyExist = value; }); }) ], mainAxisAlignment: MainAxisAlignment.center, ), 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'; }, ), ], ), ), CupertinoButton( child: Text('동물 추가하기'), onPressed: () { widget.animalList!.add(Animal( animalName: _textController!.value.text, kind: getKind(_kindChoice), imagePath: _imagePath!, flyExist: _flyExist)); }) ], mainAxisAlignment: MainAxisAlignment.center, ), ), ), ); } getKind(int kindChoice) { switch (kindChoice) { case 0: return "양서류"; case 1: return "파충류"; case 2: return "포유류"; } } }
그 외 쿠퍼티노 위젯
Cupertino (iOS-style) widgets에서 다양한 쿠퍼티노 위젯에 대한 소개와 사용법을 볼 수 있다. 책에서는 자주 사용하는 쿠퍼티노 위젯 몇 가지를 다음과 같이 소개하고 있다.
- 버튼: CupertinoButton
- 로딩 표시기: CupertinoActivityIndicator
- 알림 창: CupertinoAlertDialog
- 액션 시트: CupertinoActionSheet
- 피커: CupertinoPicker
- 내비게이션바: CupertinoNavigationBar
- 슬라이더: CupertinoSlider
728x90반응형'언어·프레임워크 > Flutter' 카테고리의 다른 글
다음글이 없습니다.이전글이 없습니다.댓글