Flutter 2.0 ์ด ์ถ์๋จ์ ๋ฐ๋ผ.
Web ๊ฐ๋ฐ ๋ํ ์ ์์ผ๋ก ๊ฐ๋ฅํด์ก์ต๋๋ค.
์ค๋์ Flutter ๋ก Web ๊ฐ๋ฐ์ ์ด๋ป๊ฒ ํ๋์ง ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์ด๋ฒ ๊ธ์ ์๋์ ์๋ฃ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์์ฑ๋์ต๋๋ค.
์ด ๊ธ์์ ์์๋ณผ ๋ด์ฉ
- ๊ฐ๋จํ ์คํ
- ์ฝ๋๋ฉ ๊ฐ์
- ์ฝ๋๋ฉ ๊ตฌํ
- ๊ตฌํ ์ค ๋๋ ์
๊ฐ๋จํ ์คํ
๋ค์ด๊ฐ๊ธฐ ์์ Web์ผ๋ก ํ๋ฒ ์คํ์์ผ ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์๋ ์ฝ๋๋ ์ ํ๋ก์ ํธ ์์ฑ์ ์๋ ์นด์ดํธ ํ๋ก๊ทธ๋จ์ ๋๋ค.
๊ฐ๋จํ ์คํ์์ผ๋ณด๋ ์ฑ๊ฐ๋ฐ๊ณผ ํฌ๊ฒ ๋ค๋ฅธ๊ฒ ์๋ ๋๋์ ๋๋ค.
๋ฌผ๋ก UI ๋ฐฐ์น๋ Web ํ๋ฉด์ ๋ง๊ฒ ์์ ํด์ผ๊ฒ ์ง๋ง.
๊ทธ ์ธ์๋ ๊ทธ๋ฅ ์ฑ๊ฐ๋ฐ๊ณผ ๋๊ฐ์ ๊ฒ ๊ฐ์ต๋๋ค.
์ฝ๋๋ฉ ๊ฐ์
ํด๋น ์ฝ๋๋ฉ์ ์ ๋ง ๊ฐ๋จํฉ๋๋ค.
ํ์๊ฐ์ ์ ํ๊ณ ์๋ฃ์์๋ Welcome ํ์ด์ง๋ฅผ ๋ณด์ฌ์ค๋๋ค.
ํน๋ณํ ์ ์ด๋ผ๊ณ ํ๋ค๋ฉด ํ์๊ฐ์
์์ ProgressBar์ ์ ๋๋ฉ์ด์
๊ตฌํ ์ ๋์
๋๋ค.
ํ์ง๋ง ๊ทธ ์ ๋๋ฉ์ด์ ์ ๋๊ฐ ์ด๋ ต์ง ์์์ ๊ฑฑ์ ํ์ค ํ์ ์์ต๋๋ค.
๋ ์์ธํ ๋ด์ฉ์ ํ์ธํ๊ณ ์ถ์ผ์๋ฉด ์๋์ ๋งํฌ๋ฅผ ํ์ธํด์ฃผ์ธ์.
Write your first Flutter app on the web
์ฝ๋๋ฉ ๊ตฌํ
์ด๋ฒ ์ฝ๋๋ฉ์๋ Null Safety๊ฐ ์ ์ฉ๋์ต๋๋ค.
pubspec.yaml ์์ sdk๋ฅผ 2.12.0 ์ด์์ผ๋ก ์ค์ ํด์ฃผ์ธ์.
์ด๋ฒ ๊ตฌํ์ ์ ๋ง ๊ฐ๋จํฉ๋๋ค.
๊ตฌ์ฐจํ ์ค๋ช ๋ณด๋ค๋ ์ ๋ฆฌ๋ ์ ์ฒด ์ฝ๋๊ฐ ๋ ์ข์ ๊ฒ ๊ฐ์ต๋๋ค.
์ฝ๋์ ๊ตฌํ๋ ํ๋ฉด์ ๊ณต์ ๋๋ฆฌ๋ ๊ฒ์ผ๋ก ์ค๋ช ์ ๋์ฒดํ๊ฒ ์ต๋๋ค.
๊ทธ๋ผ ๋ฐ๋ก ์์ํ๊ฒ ์ต๋๋ค.
1. ์ฝ๋
import 'package:flutter/material.dart';
void main() => runApp(SignUpApp());
class SignUpApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
routes: {
'/': (context) => SignUpScreen(),
'/welcome': (context) => WelcomeScreen(),
},
);
}
}
class SignUpScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[200],
body: Center(
child: SizedBox(
width: 400,
child: Card(
child: SignUpForm(),
),
),
),
);
}
}
class WelcomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text('Welcome!', style: Theme.of(context).textTheme.headline2),
),
);
}
}
class SignUpForm extends StatefulWidget {
@override
_SignUpFormState createState() => _SignUpFormState();
}
class _SignUpFormState extends State<SignUpForm> {
final _firstNameTextController = TextEditingController();
final _lastNameTextController = TextEditingController();
final _usernameTextController = TextEditingController();
double _formProgress = 0;
void _updateFormProgress() {
var progress = 0.0;
final controllers = [
_firstNameTextController,
_lastNameTextController,
_usernameTextController
];
for (final controller in controllers) {
if (controller.value.text.isNotEmpty) {
progress += 1 / controllers.length;
}
}
setState(() {
_formProgress = progress;
});
}
void _showWelcomeScreen() {
Navigator.of(context).pushNamed('/welcome');
}
@override
Widget build(BuildContext context) {
return Form(
onChanged: _updateFormProgress,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedProgressIndicator(value: _formProgress),
Text('Sign up', style: Theme.of(context).textTheme.headline4),
Padding(
padding: EdgeInsets.all(8.0),
child: TextFormField(
controller: _firstNameTextController,
decoration: InputDecoration(hintText: 'First name'),
),
),
Padding(
padding: EdgeInsets.all(8.0),
child: TextFormField(
controller: _lastNameTextController,
decoration: InputDecoration(hintText: 'Last name'),
),
),
Padding(
padding: EdgeInsets.all(8.0),
child: TextFormField(
controller: _usernameTextController,
decoration: InputDecoration(hintText: 'Username'),
),
),
TextButton(
style: ButtonStyle(
foregroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
return states.contains(MaterialState.disabled) ? null : Colors.white;
}),
backgroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
return states.contains(MaterialState.disabled) ? null : Colors.blue;
}),
),
onPressed: _formProgress == 1 ? _showWelcomeScreen : null,
child: Text('Sign up'),
),
],
),
);
}
}
class AnimatedProgressIndicator extends StatefulWidget {
final double value;
AnimatedProgressIndicator({
required this.value,
});
@override
State<StatefulWidget> createState() {
return _AnimatedProgressIndicatorState();
}
}
class _AnimatedProgressIndicatorState extends State<AnimatedProgressIndicator>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<Color?> _colorAnimation;
late Animation<double> _curveAnimation;
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(milliseconds: 1200), vsync: this);
final colorTween = TweenSequence([
TweenSequenceItem(
tween: ColorTween(begin: Colors.red, end: Colors.orange),
weight: 1,
),
TweenSequenceItem(
tween: ColorTween(begin: Colors.orange, end: Colors.yellow),
weight: 1,
),
TweenSequenceItem(
tween: ColorTween(begin: Colors.yellow, end: Colors.green),
weight: 1,
),
]);
_colorAnimation = _controller.drive(colorTween);
_curveAnimation = _controller.drive(CurveTween(curve: Curves.easeIn));
}
void didUpdateWidget(oldWidget) {
super.didUpdateWidget(oldWidget);
_controller.animateTo(widget.value);
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (context, child) => LinearProgressIndicator(
value: _curveAnimation.value,
valueColor: _colorAnimation,
backgroundColor: _colorAnimation.value?.withOpacity(0.4),
),
);
}
}
2. ํ๋ฉด
๊ตฌํ ์ค ๋๋ ์
์ ๋ง ๊ฐ๋จํ ๊ตฌํ์ด๊ธด ํ์์ง๋ง.
Flutter Web์ ๋งค๋ ฅ์ ๋๋ ๊ฒ ๊ฐ์ต๋๋ค.
๊ธฐ์กด์ Flutter๋ก ์ฑ๊ฐ๋ฐ์ ํด๋ณธ ์ฌ๋์ด๋ผ๋ฉด ์ง์ ์ฅ๋ฒฝ์ด ์๋นํ ๋ฎ์ต๋๋ค.
์ฆ ๋ณ๋ค๋ฅธ ํ์ต์ ๊ณต์ ๋ค์ด์ง ์์๋ ์น๊ฐ๋ฐ์ ์์ํ ์ ์์ต๋๋ค.
ํผํฌ๋จผ์ค์ ๊ดํด์๋ ์์ง ์๋ชจ๋ฅด๋.
ํ์ฌ Flutter๊ฐ IOS, Android ํ๊ฒฝ์์ ๋ณด์ฌ์ฃผ๊ณ ์๋ ํผํฌ๋จผ์ค๋ฅผ ๋ณด๋ฉด.
์ถฉ๋ถํ ์ฌ์ฉํด๋ณผ๋งํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
์ฌ๊ธฐ์ Desktop๊น์ง ๊ฐ๋ฐํ ์ ์์ผ๋.
์๋ง ๊ตฌ์ฑํ๋ค๋ฉด ํ๋์ ์ฝ๋๋ก Mobile, Web, Desktop ์ ์ง์ํ ์ ์์ง ์์๊น.
๋ง์ฐํ ๊ธฐ๋๋ฅผ ํด๋ณผ ์ ์๋ ๊ฒ ๊ฐ์ต๋๋ค.
๋ง์น๋ฉด์
์ด๋ฒ ๊ตฌํ์ ๋๋ฌด ๊ฐ๋จํ์ต๋๋ค.
๋ค์๋ฒ์๋ ์ข ๋ ์ฌํ๋ ๊ตฌํ์ ํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
๊ทธ์ ๋ฐ๋ผ ์ ๋ง ์ค์ ์๋น์ค์ ์ฌ์ฉํ ๋งํ์ง ํ์ ํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
๊ธด ๊ธ ์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค.
์ง๋ฌธ์ ์ธ์ ๋ ํ์์ ๋๋ค :)
'Flutter' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Flutter BLoC ๊ฟํ - 1ํ : BlocProvider ๋ฐฑ๋ฐฐ ์ฌ์ฉํ๊ธฐ (0) | 2022.01.21 |
---|---|
Flutter ๊ธฐ์ด - 1ํ : Flutter ์ ๊ณผ๊ฑฐ, ํ์ฌ, ๋ฏธ๋ (0) | 2021.12.17 |
Flutter Null Safety๋ฅผ ์ฌ์ฉํด์ผ ํ๋ ์ด์ (0) | 2021.07.02 |
Flutter์์ Webview(์น๋ทฐ) ์ฌ์ฉํ๊ธฐ (0) | 2021.07.01 |
Flutter / multi_image_picker Asset์ Image๋ก ๋ณด์ด๊ฒ ํ๊ธฐ (0) | 2021.02.19 |