본문 바로가기
기타개발/Flutter

[Android vs Flutter] View vs Widget

by 궝테스트 2020. 9. 18.

flutter-ko.dev/docs/get-started/flutter-for/android-devs

 

Android 개발자를 위한 Flutter

Android 개발 지식을 적용하여 Flutter 앱을 만드는 방법에 대해 알아보십시오.

flutter-ko.dev

 

Android View vs Flutter Widget

  • 비슷한 점
    • 화면에 표시되는 모든것의 기초 (ex. Button, Toolbar 등)
    • 플러터 위젯이 안드로이드 뷰와 정확하게 같진 않지만 UI 선언 및 구성방식이라는 건 같다
  • 다른 점
    • View
      • 한 번 그려지고 invalidate() 가 호출 될 때까지 다시 그려지지 않는다
    • Widget 
      • 변경 불가능하며 변경이 필요할 때까지만 존재하는 생명주기(lifespan) 이다
      • 위젯 또는 위젯의 상태가 변경 될 때마다 플러터 프레임워크는 위젯 인스턴스의 새로운 트리를 생성한다
      • 불변 (immutable) 하기때문에 가볍다

 

그럼 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 레이아웃 파일 어디에 있을까?

@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 넣을 수 있음
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

댓글