flutter-ko.dev/docs/get-started/flutter-for/android-devs
Android View vs Flutter Widget
- 비슷한 점
- 화면에 표시되는 모든것의 기초 (ex. Button, Toolbar 등)
- 플러터 위젯이 안드로이드 뷰와 정확하게 같진 않지만 UI 선언 및 구성방식이라는 건 같다
- 다른 점
- View
- 한 번 그려지고 invalidate() 가 호출 될 때까지 다시 그려지지 않는다
- Widget
- 변경 불가능하며 변경이 필요할 때까지만 존재하는 생명주기(lifespan) 이다
- 위젯 또는 위젯의 상태가 변경 될 때마다 플러터 프레임워크는 위젯 인스턴스의 새로운 트리를 생성한다
- 불변 (immutable) 하기때문에 가볍다
- View
그럼 Widget 은 어떻게 변경할까?
- 안드로이드는 뷰를 직접 수정해서 변경할 수 있지만 플러터에서는 불변이기 때문에 직접 변경할 수 없다
- 그래서 나온것이 Stateful 과 Stateless 이며 위젯의 state 를 변경하여 위젯을 변경할 수 있다!
- Stateful vs Stateless 사용 예)
- StatelessWidget 은 UI 와 다른 정보에 의존하지 않을 때 유용하다 (ex. 이미지 로고)
- StatefulWidget 은 HTTP 호출, 사용자와 상호작용을 통해 받은 데이터를 기반으로 UI 를 동적으로 변경해야 할 때 사용하며, Flutter 프레임워크에 State 가 변경되었다고 알려주면 위젯이 변경된다!
- Stateful vs Stateless 비교
- 공통점 : 둘 다 모든 프레임을 다시 빌드한다
- 차이점 : Stateful 은 프레임 전체에 걸쳐 상태를 저장하고 다시 반환하는 State 객체가 있다는 점,,
- 이래도 헷갈리니 예제를 통해 다시 학습
- Text 위젯은 StatelessWidget 을 상속받고있다
- Text 위젯은 생성자로 전달된 데이터들을 기반으로 그려서 보여주기만 하면 되기 때문이다
- 그런데! 만약 버튼을 눌렀을 때 텍스트 내용이 바뀌길 원한다면 어떻게 해야하냐
- Text 를 StatefulWidget 내부에 작성한다!
import 'package:flutter/material.dart';
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
// 이 위젯은 어플리케이션의 root 이다
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
// Text 위젯에 보여줄 기본 문구
String textToShow = "I Like Flutter";
void _updateText() {
setState(() {
// setState() 를 통해 State 를 업데이트 해준다
textToShow = "Flutter is Awesome!";
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: Center(child: Text(textToShow)),
floatingActionButton: FloatingActionButton(
onPressed: _updateText,
tooltip: 'Update Text',
child: Icon(Icons.update),
),
);
}
}
ㅇ ㅏ니 근데 위젯은 어디에 작성할까? XML 레이아웃 파일 어디에 있을까?
- 안드로이드는 레이아웃을 XML 에 작성하지만 플러터는 위젯 트리로 작성한다
- 위젯 카탈로그를 보면 다양한 방법을 학습할 수 있다
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: Center(
child: MaterialButton(
onPressed: () {},
child: Text('Hello'),
padding: EdgeInsets.only(left: 10.0, right: 10.0),
),
),
);
}
그럼 레이아웃에 컴포넌트를 추가/제거는 어떻게 할 수 있을까?
- 안드로이드에서는 addChild()/removeChild() 로 동적으로 자식뷰를 추가/제거할 수 있다
- 플러터 위젯은 불변이기 때문에 addChild() 같은 메소드는 없다
- 대신 부모에게 위젯을 리턴하는 함수를 전달하고, 자식의 생성 여부를 boolean 플래그로 제어할 수 있다
import 'package:flutter/material.dart';
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
bool toggle = true;
void _toggle() {
setState(() {
toggle = !toggle;
});
}
_getToggleChild() {
if (toggle) {
return Text('Toggle One');
} else {
return MaterialButton(onPressed: () {}, child: Text('Toggle Two'));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: Center(
child: _getToggleChild(),
),
floatingActionButton: FloatingActionButton(
onPressed: _toggle,
tooltip: 'Update Text',
child: Icon(Icons.update),
),
);
}
}
애니메이션 넣는건 어떻게 하는걸까?
- 안드로이드에서는 xml 또는 뷰에서 anmate() 를 통해 애니메이션을 적용한다
- 플러터는 애니메이션용 위젯을 감싸는 방법으로 애니메이션 라이브러리를 사용해서 적용한다
- AnimationController 통해서 pause, seek, stop, reverse 제어할 수 있음
- duration 과 linear interpolation (0~1) 생성하는 Ticker 넣을 수 있음
- AnimationController 통해서 pause, seek, stop, reverse 제어할 수 있음
import 'package:flutter/material.dart';
void main() {
runApp(FadeAppTest());
}
class FadeAppTest extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fade Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyFadeTest(title: 'Fade Demo'),
);
}
}
class MyFadeTest extends StatefulWidget {
MyFadeTest({Key key, this.title}) : super(key: key);
final String title;
@override
_MyFadeTest createState() => _MyFadeTest();
}
class _MyFadeTest extends State<MyFadeTest> with TickerProviderStateMixin {
AnimationController controller;
CurvedAnimation curve;
@override
void initState() {
super.initState();
controller = AnimationController(duration: const Duration(milliseconds: 2000), vsync: this);
curve = CurvedAnimation(parent: controller, curve: Curves.easeIn);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Container(
child: FadeTransition(
opacity: curve,
child: FlutterLogo(
size: 100.0,
)))),
floatingActionButton: FloatingActionButton(
tooltip: 'Fade',
child: Icon(Icons.brush),
onPressed: () {
controller.forward();
},
),
);
}
}
그렇다면 Canvas 사용해서 그리는건 어떻게 할까?
- 안드로이드에서는 Canvas 와 Drawable 을 사용해 그린다
- 플러터도 Canvas 와 유사한 API 가 있다
- CustomPaint
- CustomPainter : Canvas 에 어떻게 그릴지 알고리즘을 구현
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: DemoApp()));
class DemoApp extends StatelessWidget {
Widget build(BuildContext context) => Scaffold(body: Signature());
}
class Signature extends StatefulWidget {
SignatureState createState() => SignatureState();
}
class SignatureState extends State<Signature> {
List<Offset> _points = <Offset>[];
Widget build(BuildContext context) {
return GestureDetector(
onPanUpdate: (DragUpdateDetails details) {
setState(() {
RenderBox referenceBox = context.findRenderObject();
Offset localPosition =
referenceBox.globalToLocal(details.globalPosition);
_points = List.from(_points)..add(localPosition);
});
},
onPanEnd: (DragEndDetails details) => _points.add(null),
child: CustomPaint(painter: SignaturePainter(_points), size: Size.infinite),
);
}
}
class SignaturePainter extends CustomPainter {
SignaturePainter(this.points);
final List<Offset> points;
void paint(Canvas canvas, Size size) {
var paint = Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0;
for (int i = 0; i < points.length - 1; i++) {
if (points[i] != null && points[i + 1] != null)
canvas.drawLine(points[i], points[i + 1], paint);
}
}
bool shouldRepaint(SignaturePainter other) => other.points != points;
}
커스텀 위젯을 만드는 방법은?
- 안드로이드에서는 View 를 상속받는 클래스를 만들어서 기본 생성자/메서드를 오버라이드해서 구현한다
- 플러터는 상속 대신에 더 작은 위젯들로 새로운 위젯을 만든다 (ViewGroup 을 만드는 것과 유사)
- 아래는 CustomButtom 의 생성자가 label 을 받게하는 예제이다
- RaisedButton 을 상속하는 대신, RaisedButton 을 감싸는 CusomButton 을 만들었다
- 그리고 생성자에서 label 을 받게하면 된다
class CustomButton extends StatelessWidget {
final String label;
CustomButton(this.label);
@override
Widget build(BuildContext context) {
return RaisedButton(onPressed: () {}, child: Text(label));
}
}
'기타개발 > Flutter' 카테고리의 다른 글
Streams (0) | 2020.09.19 |
---|---|
Bloc Library - package:bloc (0) | 2020.09.19 |
Flutter 문서 링크 모음 (0) | 2020.09.13 |
Flutter : StatefulWidget & StatelessWidget (0) | 2020.09.13 |
Dart # 11 : Callable classes, Isolates, Typedefs (0) | 2020.08.28 |
댓글