위 GIF처럼 터치했을 때 TextBox가 펼쳐지며 내부의 내용을 볼 수 있고 다시 터치하면 접히는 걸 구현해 보겠습니다.
- Flutter v3.22.2
- Dart v 3.4.3
- DevTools 2.34.3
우선 저기 있는 이용약관 자체는 굉장히 길기 때문에 파일을 하나 따로 만들어두도록 하겠습니다.
제 생각에는 지금만 이렇게 주저리주저리 글을 적어놓지 나중에 제대로 된 앱을 만들려면 서버든 DB든 JSON형태로 파싱 해서 텍스트를 쓰지 않으려나.. 하는 추측이 있습니다..
왜냐하면 저는 아직 초보기 때문에 모릅니다..
String agreeConditionText =
'이용약관 동의 내용\n...';
String personalAgreeText =
'개인정보수집이용 동의 내용\n...';
String thirdAgreeText =
'개인정보 제 3자 동의 내용\n...';
사실 이렇게 작은 문자열들을 넣을 경우에는 굳이 따로 리팩터링 하여 파일을 따로 만들 필요는 없으나..
언젠간 MVVM이나 cleanArchitecture 같은 것도 도전해보고 싶어서 조금이나마 습관화하려고 일부러 파일을 나누고 있습니다.
ExpansionTile
ExpansionTile(
title: Text("내용보기"),
children: [
SizedBox(
height: 400,
child: SingleChildScrollView(
child: Text(
agreeConditionText,
style: TextStyle(color: Colors.black54),
),
),
)
],
)
ExpansionTile은 title을 필수로 받고 있습니다.
여기서 title이라 함은
title : Text("내용보기"),
처럼 커버제목을 뜻합니다.
제목을 입력하고 하위로 children을 생성하여
"SizeBox"를 높이 400으로 맞춰줍니다.
만약 높이를 400으로 맞춰주지 않는다면
이러한 형태처럼 텍스트의 길이만큼 한정 없이 늘어납니다.
그렇게 되면 사용감에 불편을 야기할 수 있겠다 생각이 들어 높이를 400으로 고정해준 뒤
이 "SizedBox"에서만 해당하는 "SingleChildScrollView"를 부여해 주어 스크롤이 가능하게 해 줍니다.
그리고
Text내부에다 아까 String으로 선언해 두었던 그 긴 문장들을 데려오도록 합니다.
만약 그 긴 문장을 Text("제 1조 어쩌구 저쩌구...") 이런 식으로 길어졌으면 코드길이가 길어져 가독성이 안 좋아졌을 겁니다.
ExpansionTile의 속성
backgroundColor : Colors.blue,
title을 포함한 색상을 변경합니다.
shape: OutlineInputBorder(
borderRadius: BorderRadius.circular(20)),
모서리의 둥근 정도를 변경합니다.
leading: Icon(CupertinoIcons.doc),
왼쪽 부분의 아이콘이나 위젯을 넣을 수 있습니다.
활성화 시 현재 적용되어 있는 Theme의 색상으로 표기됩니다.
trailing: Icon(CupertinoIcons.doc),
오른쪽에 있는 화살표의 아이콘 및 위젯을 변경합니다.
리딩과 동일하게 활성화 시 현재 Theme의 색상으로 표기됩니다.
childrenPadding: EdgeInsets.all(50),
내부 Text에 Padding을 적용시킵니다.
subtitle: Text("data"),
서브타이틀을 추가합니다.
등등 여러 가지 속성을 가지고 있으니 한 번씩 써보면서 적용시켜도 좋을 것 같지만
기본적으로 제공해주고 있는 것만으로도 충분히 가독성 좋은 ExpansionTile이라 굳이 많은 속성들을 넣지 않아도 될 것 같습니다.
이상 아래로는 최종코드 올리면서 마무리하도록 하겠습니다.
블로그에 글을 쓰는 게 목적이 아닌지라 이래저래 공부한다고 엄청 잡다한 코드가 많이 들어가 있으니 그냥 그렇구나 하고 넘어가시면 좋을 것 같습니다..
// agreement.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:social_login_namap/sign_in.dart';
import 'agreement_condition_text.dart';
import 'login_page.dart';
class Agreement extends StatefulWidget {
const Agreement({super.key});
@override
State<Agreement> createState() => AgreementState();
}
class AgreementState extends State<Agreement> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(40.0),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Title(
color: Colors.black,
child: Text(
"약관 동의",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
),
Divider(
color: Colors.black,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"회원가입 약관에 모두 동의합니다",
style: TextStyle(fontSize: 15),
),
CupertinoSwitch(
value: isAllChecked,
onChanged: (bool value) {
setState(() {
isAllChecked = value;
isConditionChecked = value;
isInfoChecked = value;
isThirdChecked = value;
isRequireChecked = value;
});
},
)
],
),
Divider(),
Row(
children: [
Text(
"이용약관 동의",
style: TextStyle(fontSize: 15),
),
Text(
"(필수)",
style: TextStyle(fontSize: 15, color: Colors.red),
),
Spacer(),
CupertinoSwitch(
value: isConditionChecked,
onChanged: (bool value) {
setState(() {
isConditionChecked = value;
updateRequireChecked();
updateAllChecked();
});
},
),
],
),
ExpansionTile(
subtitle: Text("data"),
title: Text("내용보기"),
children: [
SizedBox(
height: 400,
child: SingleChildScrollView(
child: Text(
agreeConditionText,
style: TextStyle(color: Colors.black54),
),
),
)
],
),
Divider(),
Row(
children: [
Text(
"개인정보 수집 및 이용 동의",
style: TextStyle(fontSize: 15),
),
Text(
"(필수)",
style: TextStyle(fontSize: 15, color: Colors.red),
),
Spacer(),
CupertinoSwitch(
value: isInfoChecked,
onChanged: (bool value) {
setState(() {
isInfoChecked = value;
updateRequireChecked();
updateAllChecked();
});
},
),
],
),
ExpansionTile(
title: Text("내용보기"),
children: [
SizedBox(
height: 400,
child: SingleChildScrollView(
child: Text(
personalAgreeText,
style: TextStyle(color: Colors.black54),
),
),
)
],
),
Divider(),
Row(
children: [
Text(
"개인정보 제3자 제공 동의",
style: TextStyle(fontSize: 15),
),
Text(
"(선택)",
style: TextStyle(fontSize: 15, color: Colors.grey),
),
Spacer(),
CupertinoSwitch(
value: isThirdChecked,
onChanged: (bool value) {
setState(() {
isThirdChecked = value;
updateAllChecked();
});
},
),
],
),
ExpansionTile(
title: Text("내용보기"),
children: [
SizedBox(
child: SingleChildScrollView(
child: Text(
thirdAgreeText,
style: TextStyle(color: Colors.black54),
),
),
)
],
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Expanded(
child: ElevatedButton(
onPressed: () {
resetState();
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => LoginPage(),
),
(Route<dynamic> route) => false,
);
},
style: ButtonStyle(
overlayColor:
WidgetStatePropertyAll(Colors.grey[200]),
backgroundColor:
WidgetStatePropertyAll(Colors.white)),
child:
Text("취소", style: TextStyle(color: Colors.black)),
),
),
SizedBox(width: 10),
Expanded(
child: ElevatedButton(
onPressed: isRequireChecked
? () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SignIn()),
);
}
: null,
style: ButtonStyle(
overlayColor:
WidgetStatePropertyAll(Colors.indigo[600]),
backgroundColor: isRequireChecked
? WidgetStatePropertyAll(Colors.indigo[900])
: WidgetStatePropertyAll(Colors.grey)),
child:
Text("확인", style: TextStyle(color: Colors.white)),
),
),
],
),
],
),
),
),
),
);
}
}
'Flutter > App Making Learn' 카테고리의 다른 글
[플러터 Flutter] 소셜로그인 카카오톡 1 social login with Kakao 1 (0) | 2024.06.18 |
---|---|
[플러터 Flutter] 약관 동의 페이지 만들기1 (on/off 토글키 및 bool타입 선언) (0) | 2024.06.12 |
플러터 당근마켓 앱 카피코딩3 (피드정리) (0) | 2024.05.06 |
플러터 당근마켓 앱 카피코딩2 (좋아요 기능 구현, 피드리스트) (0) | 2024.05.02 |
플러터 당근마켓 앱 카피코딩1 (appBar, body, floatingActionButton, bottomNavigationBar) (4) | 2024.04.30 |