Last active
July 23, 2021 04:55
-
-
Save ChangJoo-Park/95ef23ed23a169cd34ef87de9a8d5a52 to your computer and use it in GitHub Desktop.
왓섭 whatssub 온보딩 UI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:async'; | |
import 'package:flutter/material.dart'; | |
void main() { | |
runApp(MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Flutter Demo', | |
theme: ThemeData( | |
primarySwatch: Colors.blue, | |
), | |
home: MyHomePage(title: 'Flutter Demo Home Page'), | |
); | |
} | |
} | |
class MyHomePage extends StatefulWidget { | |
MyHomePage({Key? key, required this.title}) : super(key: key); | |
final String title; | |
@override | |
_MyHomePageState createState() => _MyHomePageState(); | |
} | |
class _MyHomePageState extends State<MyHomePage> { | |
final Color primaryColor = Color(0xFF0068FF); | |
final Color lightColor = Color(0xFFF0F0F5); | |
final double fontSize = 16; | |
final BorderRadius chatMessageBorderRadius = BorderRadius.circular(12); | |
final Border chatDefaultBorder = | |
Border.all(color: Colors.grey, width: 0.8, style: BorderStyle.solid); | |
final int baseDelayMilliseconds = 2000; | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
backgroundColor: Colors.white, | |
body: CustomScrollView( | |
physics: BouncingScrollPhysics(), | |
slivers: [ | |
SliverToBoxAdapter( | |
child: AnimatedAppear( | |
delay: Duration(milliseconds: baseDelayMilliseconds ~/ 2), | |
duration: Duration(milliseconds: 400), | |
child: AppHeader(), | |
), | |
), | |
SliverList( | |
delegate: SliverChildListDelegate.fixed([ | |
AnimatedAppear( | |
delay: Duration(milliseconds: baseDelayMilliseconds), | |
duration: Duration(milliseconds: 400), | |
child: ChatMessage( | |
text: "[WEB 발신] 구독서비스가 갱신되었습니다", | |
border: chatDefaultBorder, | |
borderRadius: chatMessageBorderRadius, | |
), | |
), | |
AnimatedAppear( | |
delay: Duration(milliseconds: baseDelayMilliseconds + 1000), | |
duration: Duration(milliseconds: 250), | |
child: ChatMessage( | |
text: "??? 뭔데", | |
mainAxisAlignment: MainAxisAlignment.end, | |
backgroundColor: lightColor, | |
borderRadius: chatMessageBorderRadius, | |
), | |
), | |
AnimatedAppear( | |
delay: Duration(milliseconds: baseDelayMilliseconds + 1800), | |
duration: Duration(milliseconds: 250), | |
child: ChatMessage( | |
text: "하... 맞다...", | |
mainAxisAlignment: MainAxisAlignment.end, | |
backgroundColor: lightColor, | |
borderRadius: chatMessageBorderRadius, | |
), | |
), | |
AnimatedAppear( | |
delay: Duration(milliseconds: baseDelayMilliseconds + 2500), | |
duration: Duration(milliseconds: 250), | |
child: ChatMessage( | |
text: "이런 경험 다들 있으시죠?", | |
border: chatDefaultBorder, | |
borderRadius: chatMessageBorderRadius, | |
), | |
), | |
AnimatedAppear( | |
delay: Duration(milliseconds: baseDelayMilliseconds + 3500), | |
duration: Duration(milliseconds: 250), | |
child: ChatMessage( | |
text: "🤗 우리, 왓섭에서 편하게 알림받고 해지해요", | |
textColor: primaryColor, | |
backgroundColor: primaryColor.withOpacity(0.1), | |
borderRadius: chatMessageBorderRadius, | |
), | |
), | |
]), | |
), | |
SliverFillRemaining( | |
fillOverscroll: false, | |
hasScrollBody: false, | |
child: AnimatedAppear( | |
delay: Duration(milliseconds: baseDelayMilliseconds + 5000), | |
duration: Duration(milliseconds: 250), | |
child: Container( | |
padding: const EdgeInsets.symmetric(vertical: 16), | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.end, | |
crossAxisAlignment: CrossAxisAlignment.center, | |
children: [ | |
RichText( | |
textAlign: TextAlign.center, | |
text: TextSpan( | |
children: [ | |
TextSpan( | |
text: '\'동의 및 시작\'을 누르는 것으로 ', | |
style: TextStyle(color: Colors.grey, fontSize: 12), | |
), | |
TextSpan( | |
text: '왓섭 필수 이용약관', | |
style: TextStyle( | |
color: Colors.grey, | |
decoration: TextDecoration.underline, | |
fontSize: 12), | |
), | |
TextSpan( | |
text: ' 및\n', | |
style: TextStyle(color: Colors.grey, fontSize: 12), | |
), | |
TextSpan( | |
text: '개인정보 이용방침', | |
style: TextStyle( | |
color: Colors.grey, | |
decoration: TextDecoration.underline, | |
fontSize: 12), | |
), | |
TextSpan( | |
text: '에 동의하게 됩니다.', | |
style: TextStyle(color: Colors.grey, fontSize: 12), | |
), | |
], | |
), | |
), | |
SizedBox(height: 16), | |
Container( | |
width: double.infinity, | |
padding: EdgeInsets.symmetric(horizontal: 16), | |
child: ElevatedButton( | |
style: ElevatedButton.styleFrom( | |
minimumSize: Size(0, 48), | |
elevation: 0, | |
primary: primaryColor, | |
shape: RoundedRectangleBorder( | |
borderRadius: BorderRadius.circular(12), | |
), | |
), | |
onPressed: () {}, | |
child: Text('동의하고 시작하기'), | |
), | |
), | |
], | |
), | |
), | |
), | |
) | |
], | |
), | |
); | |
} | |
} | |
class AppHeader extends StatelessWidget { | |
AppHeader({Key? key}) : super(key: key); | |
final Color primaryColor = Color(0xFF0068FF); | |
@override | |
Widget build(BuildContext context) { | |
return Container( | |
margin: const EdgeInsets.only(top: 40), | |
padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 16), | |
child: RichText( | |
text: TextSpan(children: [ | |
TextSpan( | |
text: '내가 ', | |
style: TextStyle( | |
color: Colors.black, | |
fontSize: 24, | |
fontWeight: FontWeight.bold, | |
)), | |
TextSpan( | |
text: '어떤 서비스', | |
style: TextStyle( | |
color: primaryColor, | |
fontSize: 24, | |
fontWeight: FontWeight.bold, | |
), | |
), | |
TextSpan( | |
text: '를\n', | |
style: TextStyle( | |
color: Colors.black, | |
fontSize: 24, | |
fontWeight: FontWeight.bold, | |
)), | |
TextSpan( | |
text: '언제 구독', | |
style: TextStyle( | |
color: primaryColor, | |
fontSize: 24, | |
fontWeight: FontWeight.bold, | |
), | |
), | |
TextSpan( | |
text: '했더라?', | |
style: TextStyle( | |
color: Colors.black, | |
fontSize: 24, | |
fontWeight: FontWeight.bold, | |
), | |
), | |
]), | |
), | |
); | |
} | |
} | |
class AnimatedAppear extends StatefulWidget { | |
const AnimatedAppear( | |
{Key? key, | |
required this.child, | |
this.delay = const Duration(milliseconds: 0), | |
this.duration = const Duration(milliseconds: 0)}) | |
: super(key: key); | |
final Widget child; | |
final Duration delay; | |
final Duration duration; | |
@override | |
_AnimatedAppearState createState() => _AnimatedAppearState(); | |
} | |
class _AnimatedAppearState extends State<AnimatedAppear> | |
with TickerProviderStateMixin { | |
late AnimationController _opacityController; | |
late Animation<Offset> _slideAnimationOffset; | |
Timer? _timer; | |
@override | |
void initState() { | |
super.initState(); | |
_opacityController = | |
AnimationController(vsync: this, duration: widget.duration); | |
_slideAnimationOffset = Tween<Offset>( | |
begin: const Offset(0.0, 0.35), | |
end: Offset.zero, | |
).animate( | |
CurvedAnimation( | |
parent: _opacityController, | |
curve: Curves.fastOutSlowIn, | |
), | |
); | |
_animate(); | |
} | |
@override | |
void dispose() { | |
_opacityController.dispose(); | |
_timer?.cancel(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return FadeTransition( | |
opacity: _opacityController, | |
child: SlideTransition( | |
position: _slideAnimationOffset, | |
child: widget.child, | |
), | |
); | |
} | |
_animate() { | |
_timer = Timer(widget.delay, () { | |
_opacityController.forward(); | |
}); | |
} | |
} | |
class ChatMessage extends StatelessWidget { | |
const ChatMessage({ | |
Key? key, | |
required this.text, | |
this.textColor = Colors.black, | |
this.backgroundColor, | |
this.alignment, | |
this.mainAxisAlignment = MainAxisAlignment.start, | |
this.border, | |
this.borderRadius, | |
}) : super(key: key); | |
final String text; | |
final Color? textColor; | |
final Color? backgroundColor; | |
final Alignment? alignment; | |
final MainAxisAlignment mainAxisAlignment; | |
final Border? border; | |
final BorderRadius? borderRadius; | |
@override | |
Widget build(BuildContext context) { | |
return Padding( | |
padding: const EdgeInsets.symmetric(horizontal: 16), | |
child: Row( | |
mainAxisAlignment: mainAxisAlignment, | |
children: [ | |
Container( | |
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), | |
margin: const EdgeInsets.only(bottom: 8), | |
decoration: BoxDecoration( | |
border: border, | |
borderRadius: borderRadius, | |
color: backgroundColor, | |
), | |
child: Text( | |
text, | |
style: TextStyle(color: textColor), | |
), | |
) | |
], | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment