카테고리 없음
비밀듣는 고양이 앱 제작
신승호.
2023. 2. 18. 02:53
- Assignment
- 제공되는 패키지 secrets_cat_sdk를 활용하여 다음의 기대 결과물을 따라 만드세요.
- 이번 과제는 최대한 다음의 결과물과 다른 디자인으로 제작하는데 목표를 두세요.

- Requirements
- 앱 이름은 [비밀듣는 고양이]가 아닌 다른 이름으로 진행하세요.
- 앱 내에서 사용될 폰트는 다음과 같습니다.
- 플러터에 폰트 등록 방법을 검색하여 앱내에 적용할 수 있도록 하세요.
- neo.ttf
- 매인 캐릭터 또한 다음의 링크에서 마음에 드는 이미지를 골라서 진행하세요.
- 페이지들의 배경이미지는 다음의 링크에서 마음에 드는 이미지를 골라서 진행하세요.
- 각 위젯별 애니메이션은 최소 3개 이상이 적용되어야 합니다. 이 때 적용되는 애니메이션은 자유입니다.
- 페이지는 3개 이상입니다. 필수 페이지는 다음과 같습니다.
- SecretPage : 비밀을 볼 수 있는 페이지며, 모든 비밀을 데이터로 불러오며 각 비밀은 페이지로 이루어짐.
- AuthorPage : 모든 작성자(회원)을 볼 수 있는 페이지
- UploadPage: 비밀을 업로드할 수 있는 페이지
- 페키지를 설치하면 Author와 Secret 데이터타입을 사용할 수 있습니다. 데이터와 데이터타입을 활용하여 최대한 위 결과물의 비슷하게 앱을 만들어보세요.
pubspec.yaml에 이미지랑 폰트를 저장했다

내 메인코드
import 'package:assigment18/authorpage.dart';
import 'package:assigment18/secretpage.dart';
import 'package:assigment18/uploadpage.dart';
import 'package:flutter/material.dart';
import 'package:secret_cat_sdk/secret_cat_sdk.dart';
import 'package:animate_do/animate_do.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
scaffoldBackgroundColor: Colors.greenAccent, fontFamily: "Neo"),
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
const MainPage({super.key});
@override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
CircleAvatar(
radius: 60,
backgroundColor: Colors.white60,
backgroundImage: NetworkImage(
),
SizedBox(
height: 8,
),
Text(
'뉴진스의 하입보이요',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 30,
color: Colors.white),
),
SizedBox(
height: 50,
),
ListTile(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: ((context) {
return SecretPage();
})));
},
tileColor: Colors.white,
title: Text('비밀보기'),
subtitle: Text('놀러가기'),
trailing: Image.network(
),
SizedBox(
height: 50,
),
ListTile(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: ((context) {
return AuthorPage();
})));
},
tileColor: Colors.white,
title: Text('작성자들보기'),
subtitle: Text('놀러가기'),
trailing: Image.network(
),
SizedBox(
height: 50,
),
ListTile(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: ((context) {
return UploadPage();
})));
},
tileColor: Colors.white,
title: Text('비밀공유'),
subtitle: Text('놀러가기'),
trailing: Image.network(
),
],
),
),
),
),
);
}
}
ThemeData로 배경색을 greentAccent로 주고 폰트를 설정하고, 각 ListTile을 누르면 해당하는 페이지로 이동할수 있도록 하였다.

비밀페이지
import 'package:animate_do/animate_do.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:secret_cat_sdk/secret_cat_sdk.dart';
class SecretPage extends StatelessWidget {
const SecretPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
title: Text(
'뒤로가기',
style: TextStyle(fontWeight: FontWeight.bold),
),
elevation: 0,
backgroundColor: Colors.transparent,
),
body: Container(
alignment: Alignment.center, //컨테이너 고정
decoration: BoxDecoration(
image: DecorationImage(
colorFilter: ColorFilter.mode(Colors.black38, BlendMode.darken), //이미지를 어둡게
image: NetworkImage(
fit: BoxFit.cover),
),
child: FutureBuilder(
future: SecretCatApi.fetchSecrets(),
builder: (context, snapshot) {
if (snapshot.hasData) {
var reversedList = snapshot.data!.reversed.toList(); //★ 리스트를 역순으로 ★
return PageView.builder(
itemCount: snapshot.data?.length,
itemBuilder: (context, index) {
return FadeInRight(
duration: Duration(seconds: 1),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircleAvatar(
radius: 60,
backgroundColor: Colors.white60,
backgroundImage: NetworkImage(
),
SizedBox(
height: 20,
),
Text(
textAlign: TextAlign.center,
reversedList[index].secret,
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 5,
),
Text(
reversedList[index].author?.username ?? "익명",
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold),
),
],
),
);
},
);
}
return CircularProgressIndicator();
})),
);
}
}

작성자페이지
import 'package:animate_do/animate_do.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter/material.dart';
import 'package:secret_cat_sdk/secret_cat_sdk.dart';
class AuthorPage extends StatelessWidget {
const AuthorPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
title: Text(
'뒤로가기',
style: TextStyle(fontWeight: FontWeight.bold),
),
elevation: 0,
backgroundColor: Colors.transparent,
),
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
colorFilter: ColorFilter.mode(Colors.black38, BlendMode.darken),
image: NetworkImage(
fit: BoxFit.cover),
),
child: FutureBuilder(
future: SecretCatApi.fetchAuthors(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3),
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return ZoomIn(
delay: Duration(milliseconds: 300 * index), //순서대로 0.3초 간격으로 애니메이션(줌인)
child: Column(
children: [
CircleAvatar(
radius: 50,
backgroundColor: Colors.transparent,
backgroundImage: NetworkImage(
snapshot.data![index].avatar!)),
SizedBox(
height: 10,
),
Text(
textAlign: TextAlign.center,
snapshot.data![index].username,
style: TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold),
),
],
),
);
},
);
}
return CircularProgressIndicator();
})),
);
}
}

비밀공유페이지
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:secret_cat_sdk/api/api.dart';
class UploadPage extends StatelessWidget {
const UploadPage({super.key});
@override
Widget build(BuildContext context) {
var myController = TextEditingController();
return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
title: Text(
'뒤로가기',
style: TextStyle(fontWeight: FontWeight.bold),
),
elevation: 0,
backgroundColor: Colors.transparent,
),
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
colorFilter: ColorFilter.mode(Colors.black38, BlendMode.darken),
image: NetworkImage(
fit: BoxFit.cover),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
decoration:
InputDecoration(fillColor: Colors.white30, filled: true,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.pink))
), maxLines: 8,
controller: myController,
),
ElevatedButton(
onPressed: () async {
if (myController.text != '') {
var secret =
await SecretCatApi.addSecret(myController.text);
if (secret != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('비밀공유완료~~')));
}
}
},
child: Text('비밀공유'))
],
),
),
),
);
}
}
ElevatedButton을 누르면 텍스트필드에 적힌 내용을 secret데이터에 추가시키는 작업을 수행했다.
작업을 끝내면 스낵바가 나타난다
