이번에는 PageView 의 내부에 address page 를 추가해보겠습니다.
개발환경 : 윈도우11, 안드로이드 스튜디오, flutter 3.0.1
화면 구성은 아래와 같습니다. 자세한 기능은 모델링도 필요하여 차후에 구현하겠습니다.
상단부터 TextFormField, TextButton.icon, Expanded&ListView,builder&ListTile 로 구성되어 있습니다.
주요 파일은 아래와 같습니다.
./src/screens/auth_screen.dart
./src/screens/start/intro_page.dart
./src/screens/start/address_page.dart
./src/screens/auth_screen.dart
import 'package:flutter/material.dart';
import 'start/address_page.dart';
import 'start/intro_page.dart';
class AuthScreen extends StatelessWidget {
AuthScreen({Key? key}) : super(key: key);
final PageController _pageController = PageController();
@override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
controller: _pageController,
// 이부분이 활성화 되면 사용자가 화면을 좌/우로 스크롤하지 못하게 설정 가능
// physics: const NeverScrollableScrollPhysics(),
children: <Widget>[
IntroPage(pageController: _pageController),
const AddressPage(), // 새로 추가된 화면을 호출하는 부분
Container(color: Colors.accents[4]),
],
),
);
}
}
./src/screens/start/intro_page.dart - 약간의 화면수정과 PageController 를 인자로 전달받기 위해 수정함
import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart';
import '../../constants/common_size.dart';
import '../../utils/logger.dart';
class IntroPage extends StatelessWidget {
final PageController pageController;
const IntroPage({Key? key, required this.pageController}) : super(key: key);
@override
Widget build(BuildContext context) {
FocusScope.of(context).unfocus();
// 모바일 화면의 비율을 정할때 편한 위젯
return LayoutBuilder(
builder: (context, constraints) {
Size size = MediaQuery.of(context).size;
final imgSize = size.width - padding_16 * 2;
final sizeOfPosImg = imgSize * 0.1;
return SafeArea(
// 상태바 아래부터 , 하단 버튼위로 위젯이 위치시킴
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: padding_16),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(
'Apple Market',
style: Theme.of(context)
.textTheme
.headline3!
.copyWith(color: Theme.of(context).colorScheme.primary),
),
SizedBox(
width: imgSize,
height: imgSize,
child: Stack(
children: <Widget>[
ExtendedImage.asset('assets/imgs/carrot_intro.png'),
// Stack 안에서만 사용 가능함
Positioned(
// 가로 세로 길이 설정, 사이즈 설정
width: sizeOfPosImg,
height: sizeOfPosImg,
// 왼쪽과 위쪽의 간격 설정
left: imgSize * 0.45,
top: imgSize * 0.45,
child: ExtendedImage.asset(
'assets/imgs/carrot_intro_pos.png')),
],
),
),
Text(
'우리 동네 중고 직거래 사과마켓',
style: Theme.of(context).textTheme.headline6,
),
Text(
'사과 마켓은 동네 직거래 마켓이에요\n'
'내 동네를 설정하고 시작해보세요',
style: Theme.of(context).textTheme.subtitle1,
),
Column(
// 버튼을 글씨에 맞추지않고 최대한 늘리는 설정
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding:
const EdgeInsets.symmetric(horizontal: padding_16),
child: TextButton(
onPressed: () async {
logger.d('on Intro page, text Button Clicked !!!');
pageController.animateToPage(1,
duration: const Duration(milliseconds: 500),
curve: Curves.ease);
},
style: TextButton.styleFrom(
backgroundColor: Theme.of(context).primaryColor),
child: Text(
'내 동네 설정하고 시작하기',
style: Theme.of(context).textTheme.button,
),
),
),
],
),
],
),
),
);
},
);
}
}
./src/screens/start/address_page.dart
import 'package:extended_image/extended_image.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import '../../constants/common_size.dart';
import '../../utils/logger.dart';
class AddressPage extends StatefulWidget {
const AddressPage({Key? key}) : super(key: key);
@override
State<AddressPage> createState() => _AddressPageState();
}
class _AddressPageState extends State<AddressPage> {
final TextEditingController _addressController = TextEditingController();
final _isGettingLocation = false;
@override
void dispose() {
_addressController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
logger.d("AddressPage >> build");
return SafeArea(
// padding 대신에 minimum 으로 설정 가능, 위/아래 글씨가 잘리는것도 방지하자
minimum: const EdgeInsets.symmetric(horizontal: padding_16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TextFormField(
controller: _addressController,
onFieldSubmitted: onClickTextField,
decoration: InputDecoration(
// icon 으로도 가능하지만 여기서는 prefixIcon 으로 설정함
prefixIcon: const Icon(
Icons.search,
color: Colors.grey,
),
// 아이콘 주변 공간을 조절 가능
prefixIconConstraints:
const BoxConstraints(minWidth: 24, maxHeight: 24),
// 문자 입력후 밑줄이 생기게 설정
border: const UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey)),
// focusedBorder: const UnderlineInputBorder(borderSide: BorderSide(color: Colors.grey)),
hintText: '도로명으로 검색...',
hintStyle: TextStyle(color: Theme.of(context).hintColor),
),
),
const SizedBox(height: padding_08),
TextButton.icon(
label: Text(
_isGettingLocation ? '위치 찾는중 ~~' : '현재위치 찾기',
style: Theme.of(context).textTheme.button,
),
onPressed: () {},
icon: _isGettingLocation
? const SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(color: Colors.white))
: const Icon(CupertinoIcons.compass,
color: Colors.white, size: 20),
),
Expanded(
child: ListView.builder(
padding: const EdgeInsets.symmetric(vertical: padding_16),
itemCount: 30,
itemBuilder: (context, index) {
debugPrint('index: $index');
return ListTile(
leading: const Icon(Icons.image_aspect_ratio),
trailing: ExtendedImage.asset('assets/imgs/apple.jpg'),
title: Text('address $index'),
subtitle: Text('address $index'),
);
},
),
),
],
),
);
}
void onClickTextField(text) async {
setState(() {});
}
}
'Flutter > 12 Clone 'Used Goods app'' 카테고리의 다른 글
[Flutter] Clone - 당근마켓8(authorization-login) (0) | 2022.07.21 |
---|---|
[Flutter] Clone - 당근마켓7(PageView - auth page) (0) | 2022.07.20 |
[Flutter] Clone - 당근마켓5(PageView - intro page) (0) | 2022.07.15 |
[Flutter] Clone - 당근마켓4(theme) (0) | 2022.07.15 |
[Flutter] Clone - 당근마켓3(PageView) (0) | 2022.07.15 |