my_webpage
mywebpage-220b8.web.app
블로그
GitHub - ujo-orr/my_webpage: webPage prac
webPage prac. Contribute to ujo-orr/my_webpage development by creating an account on GitHub.
github.com
GitHub에 코드를 정리해서 올려두었으니,
혹시나 필요하시다면 봐주시고 틀린 부분이 분명 있을 터이니
지적이나 참고할 사항, 고칠 점은 언제든지 감사히 듣고 저 역시 참고하겠습니다.
[플러터 Flutter] web Blog 만들어보기 2
my_webpage mywebpage-220b8.web.app블로그 GitHub - ujo-orr/my_webpage: webPage pracwebPage prac. Contribute to ujo-orr/my_webpage development by creating an account on GitHub.github.comGitHub [플러터 Flutter] web Blog 만들어보기 1Flutter w
ujo-orr.tistory.com
2편에 걸쳐서 진행하겠습니다.
Flutter web으로 나만의 블로그를 만들어 보았습니다.
그중에서도 옛날 감성인 커스텀 홈페이지를 만들어 보고 싶었기 때문에..
코드는 엉망진창이지만..
구현해보고 싶었던 기능들은 크게
- FireBase를 통해 CRUD (사진, 동영상 포함)
- go_router 사용하기
- flutter_riverpod 사용하기
- post List는 GridView.builder로 post count의 갯수만큼
- 커스텀 폰트 사용하기
- 배경 쪽에 반짝거리는 별 애니메이션
- BGM
- 마우스커서 커스텀
- 커스텀 사이드바
- 홈페이지 이름(AppBar TextButton)을 눌렀을 때 새로고침
- URL링크 리다이렉트
- 로그인, 로그아웃 (나만 쓸 수 있는)
이렇게 볼 수 있습니다.
우선 FireBase에서는
- FireStore DataBase
- Authentication
- Hosting
이렇게 세 가지만 사용하여서 프로젝트를 꾸려 보았습니다.
FireBase를 통해 CRUD (사진, 동영상 포함)
FireStore DataBase에서는 이러한 식으로 DB를 구성해 놓았으며
사실상 Update기능과 Delete기능은 저 혼자만 쓸 거기 때문에.. 따로 구현해놓지는 않았습니다.
뭔가 저 혼자만 쓴다고 생각하니 그냥 FireStore에서 수정하고 삭제할 거란 생각에 나중에 천천히 해 볼 생각입니다.
그리고 사용한 TextEdit은
flutter_quill: ^10.8.5
flutter_quill_extension: ^10.8.5
vsc_quill_delta_to_html: ^1.0.5
flutter_quill을 사용하였으며
flutter_quill은 단순히 텍스트를 담당하고
flutter_quill_extension은 이미지와 동영상을 담당합니다
그리고 vsc_quill_delta_to_html은 quill 자체가 텍스트나 이미지 등 자료를 delta형태로 저장하기 때문에
delta형태를 html로 컨버트 해주는 라이브러리입니다.
여기서 문제가 생겼습니다.
각종 폰트 크기, 글꼴, 굵기, 정렬 등등을 delta형태로 저장하는데 이를 html로 변환시켜 FireStore에 적용시키니
각종 저장 사항들을 불러오지 못하고 단순한
사진, 텍스트 형태로만 불러온다는 점입니다.
그래서 사용하면서도 굉장히 시간도 많이 잡아먹고 stackOverFlow나 Gitd을 전전긍긍해보면서 느꼈던 점은
굳이 quill 라이브러리를 쓰지 않고 시간이 많이 걸리더라도 하나하나 기능을 구현하는 게 좀 더 접근성이 좋고 사용감이 좋을 거란 생각이 들었습니다.
go_router 사용하기
go_router는 Flutter에서 Navigator.push 같은 방식의 복잡함을 줄이고
선언식 라우트 패턴을 사용하여 라우팅을 간편하게 구현할 수 있는 라이브러리입니다.
웹과 모바일 모두 URL을 기반으로 한 라우팅처리를 할 수 있기에 페이지가 많거나 이동해야 하는 경로가 많을 시 쓰기 편하겠다 느꼈습니다.
이런 식으로 페이지당 URL을 할당하여 바로바로 해당 페이지로 이동하는 형식입니다.
flutter_riverpod 사용하기
원래 한참 flutter_provider를 사용하다가 이번에 처음으로 flutter_riverpod 라이브러리를 사용해 보았습니다.
이유는 간단합니다.
기존 provider의 단점을 보완했으며 riverpod사용자가 점점 늘어나는 추세에 따라
provider가 riverpod에 합병당할 것 같다는 느낌 때문에 조금이라도 손에, 눈에 익혀놓고자 하는 마음으로 사용해 보았습니다.
이러한 형태로 좀 더 직관적이고 쓰기 쉽게 돼있다고는 하나..
아직 provider가 익숙해서 이해하는데 조금 시간이 걸렸습니다..
커스텀 폰트 사용하기
따로 홈페이지에 폰트를 설정하는 기능을 만들고 싶은 게 아니기 때문에
assets/fonts/... 에 설정을 해두고
theme: ThemeData(fontFamily: '$font_name') 을 통하여 전역적으로 폰트를 설정해 두었습니다.
여기서 나중에 서술할 audio파일, mp3파일에서도 문제가 생기기 시작하는데 아래에 서술하도록 하겠습니다.
배경 쪽에 반짝거리는 별 애니메이션
class NightAnimation extends StatefulWidget {
const NightAnimation({super.key});
@override
State<NightAnimation> createState() => _NightAnimationState();
}
class _NightAnimationState extends State<NightAnimation>
with SingleTickerProviderStateMixin {
계속 별이 움직이는 상태이기에 StatefulWidget을 사용하였고 굳이 변화에 대한 알림을 받을 필요가 없기 때문에
riverpod은 사용하지 않았습니다.
SingleTickerProviderStateMixin으로 클래스로 Ticker를 관리할 수 있게 해 주었으며
여기서 Ticker는 플러터에서 애니메이션을 실행하는 데 사용되는 툴로써 매 프레임마다(플러터앱은 60fps) 틱을 발생시켜 애니메이션 상태를 콜백 호출하여 애니메이션이 부드럽게 동작하도록 하는 역할을 합니다.
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
_animation = Tween<double>(begin: 0.5, end: 1.0).animate(_controller);
_controller.repeat(reverse: true); // 반복 애니메이션
WidgetsBinding.instance.addPostFrameCallback((_) {
_initializeStars(context.size!); // 화면 크기 정보로 별 위치 초기화
});
}
initState를 사용하여 StarryNight위젯이 생성될 때 초기상태를 설정하고 리소스를 할당하는 초기화 작업을 하였습니다.
- AnimationController내부에
- vsync: ,
- 이 부분이 애니메이션의 리소스를 최적하하기 위해 사용되는 파라미입니다.
- 이 애니메이션 위젯이 화면에 표시될 때에만 애니메이션을 실행하고 표시되지 않을 때에는 애니메이션을 정지시켜 리소스 낭비를 방지합니다.
- this
- 현재 상태 클래스. 즉, _NightAnimationState를 지칭합니다.
- duration: 애니메이션 지속시간을 1초로 설정합니다.
- vsync: ,
- Tween
- begin:0.5, end:1.0을 지정하여 0.5와 1.0 사이의 값으로 애니메이션이 반복적으로 변화하게 됩니다.
- _controller.repeat(reverse:true);
- 애니메이션이 앞뒤로 반복합니다.
- WidgetsBinding.instance.addPostFrameCallback((){})
- 화면 크기를 기반으로 별의 위치를 초기화시킵니다.
@override
void dispose() {
_controller.dispose();
super.dispose();
}
이후 dispose를 통해 애니메이션 컨트롤러의 리소스를 정리합니다.
// 별의 위치 초기화
void _initializeStars(Size size) {
if (starPositions.isEmpty) {
final random = Random();
for (int i = 0; i < 400; i++) {
final x = random.nextDouble() * size.width;
final y = random.nextDouble() * size.height;
starPositions.add(Offset(x, y));
}
}
}
약 400개의 별들의 위치를 랜덤 하게 생성하여 List<Offset>starPositions = []에 저장합니다.
Random은 플러터에서 제공하는 랜덤 숫자 생성기이며
Offset(x, y)는 별의 위치를 나타내는 좌표입니다.
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return CustomPaint(
painter: _StarryNightPainter(
starPositions: starPositions,
opacity: _animation.value,
),
child: Container(),
);
},
);
}
- AnimatedBuilder()
- 애니메이션 상태가 변경될 때마다 UI를 다시 빌드해줍니다.
- animation: _animation
- 애니메이션의 값이 변화가 있을 때마다 빌드 트리를 다시 렌더링 합니다.
- CustomPaint()
- 별을 그리기 위해 캔버스를 사용합니다.
- painter: _StarryNightPainter()
- _StarryNightPainter 클래스에서 별을 그리는 작업을 수행합니다.
- opacity: _animation.value,
- 애니메이션 값 (0.5~1.0)을 전달하여 별의 투명도를 조절합니다.
class _StarryNightPainter extends CustomPainter {
final List<Offset> starPositions;
final double opacity;
_StarryNightPainter({required this.starPositions, required this.opacity});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.white
..strokeWidth = 1.0;
for (var position in starPositions) {
final radius = Random().nextDouble() * 2.0;
paint.color = Colors.white.withOpacity(opacity);
canvas.drawCircle(position, radius, paint);
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
앞서 적었던 코드들에서 opacity, position을 받아오고
Paint..color..stroke 로 색상과 굵기를 정해줍니다.
for루프를 통해 List<Offset>내부에 저장된 각 position에 반응하게 하고.
radius를 랜덤 하게, 원으로 그릴 수 있도록 합니다.
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
shouldRepaint를 통해
CustomPainter가 canvas에 그림을 그릴 때 다시 그려야 할지 판단하려면 이전에 사용한 도구 oldDelegate를 확인하여 판단하게 되고
convariant는 CustomPainter에게 이전 도구(oldDelegate)는 반드시 _StarryNightPainter라고 알려주는 역할을 하게 됩니다.
이후 return true를 통하여 '캔버스를 다시 그려라'라는 명령을 Flutter의 렌더링 엔진으로 전달하도록 합니다.
'Flutter > web Making Learn' 카테고리의 다른 글
[플러터 Flutter] web Blog 만들어보기 2 (1) | 2024.11.21 |
---|