본문 바로가기

Flutter

Flutter Hero

전체코드

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  HeroSample createState() => HeroSample();
}

class HeroSample extends State<MyHomePage>{
  @override
  Widget build(BuildContext context){
    double appBarHeight = 50.0;
    return MaterialApp(
      home: Scaffold(
        appBar: PreferredSize(
          preferredSize: Size.fromHeight(appBarHeight),
          child:AppBar(
            title: Text('Hero Demo'),
            backgroundColor: Colors.cyan,
          ),
        ),
        body: GestureDetector(
            onTap: (){
              Navigator.push(context, MaterialPageRoute(builder: (context) => HeroBlue()));
            },
            child: Blue()
        ),
      ),
    );
  }
}

class HeroBlue extends StatelessWidget {
  double appBarHeight = 40.0;
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: PreferredSize(
          preferredSize: Size.fromHeight(appBarHeight),
          child: AppBar(
            leading: IconButton(
              icon: Icon(Icons.arrow_back),
              onPressed: () {
                Navigator.pop(context); //이전 페이지로 이동.
              },
            ),
            backgroundColor: Colors.cyan,
          ),
        ),
        body: DoubleBlue(),
      )
    );
  }
}

class Blue extends StatelessWidget {
  @override
  Widget build(BuildContext context){
    return new LayoutBuilder(builder: (context, constraint) {
      double min = (constraint.biggest.width < constraint.biggest.height)
          ? constraint.biggest.width : constraint.biggest.height;
      return Container(
        width: constraint.biggest.width,
        height: constraint.biggest.height,
        color: Colors.blue,
        child: Hero(
          tag: "b",
          child: Icon(Icons.access_alarms, size: min / 2, color: Colors.white),
        )
      );
    });
  }
}

class DoubleBlue extends StatelessWidget {
  @override
  Widget build(BuildContext context){
    return new LayoutBuilder(builder: (context, constraint) {
      double min = (constraint.biggest.width < constraint.biggest.height)
          ? constraint.biggest.width : constraint.biggest.height;
      return Column(
        children: [
          Container(
            width: constraint.biggest.width,
            height: constraint.biggest.height / 2,
            color: Colors.blue,
            child: Hero(
              tag: "b",
              child: Icon(Icons.access_alarms, size: min / 2, color: Colors.white),
            )
          ),
          Container(
            width: constraint.biggest.width,
            height: constraint.biggest.height / 2,
            color: Colors.blue,
            child: Icon(Icons.access_alarms, size: min / 2, color: Colors.white),
          ),
        ]
      );
    });
  }
}

 

실행순서 : void main() > class MyApp (Widgt build) > class MyHomePage (_MyHomePageState createState) > class HeroSample > body : blue(), body에 해당하는 부분 click 시 class HeroBlue, 이전버튼 click 시 class HeroSample

 

GestureDetector

class HeroSample extends State<MyHomePage>{
  ...
        body: GestureDetector(
            onTap: (){
              Navigator.push(context, MaterialPageRoute(builder: (context) => HeroBlue()));
            },
            child: Blue()
        ),
  ...
}

사용자의 터치제스처를 감지한다. "onTap: " 은 한번 터치했을때 실행되는 함수이다. "child: " 에 할당된 Blue() Widget이 처음 화면에 출력되고 Blue()가 출력되는 공간을 한번 터치하면 Navigator.push와 MeterialPageRoute를 통해 다음 Page인 HeroBlue()를 출력한다.

Navigator : Stack 규칙을 사용하여 하위 Widget들을 관리하는 Widget. push, pop을 통해 Widget을 삽입하고 삭제할 수 있다.(LIFO)

MeterialPageRoute : 사용하는 플랫폼에 맞게 화면이 전환되는 Widget. (Android 는 아래위로 전환되고 IOS는 좌우로 전환된다.)

(cmd + B) GestureDetector의 요소들

 

Hero(1)

class Blue extends StatelessWidget {
  ...
        child: Hero(
          tag: "b",
          child: Icon(Icons.access_alarms, size: min / 2, color: Colors.white),
        )
  ...
}

기존 Blue() Widget에서 Icon 부분을 Hero로 감쌌다. Hero Widget은 Page가 변화할때 "tag : "가 같은 것 끼리 자연스러운 애니메이션을 형성해 준다. 이번 sample에서는 DoubleBlue와 Blue 에서 공통적으로 들어가는 Icon 한개에 적용하여 Page가 이동할 때 자연스럽게 전환되는 것을 구현하였다. (Hero Widget은 "tag: " 요소와 "child: " 요소를 반드시 할당하여야 한다.)

 

HeroBlue

class HeroBlue extends StatelessWidget {
  double appBarHeight = 40.0;
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: PreferredSize(
          preferredSize: Size.fromHeight(appBarHeight),
          child: AppBar(
            leading: IconButton(
              icon: Icon(Icons.arrow_back),
              onPressed: () {
                Navigator.pop(context); //이전 페이지로 이동.
              },
            ),
            backgroundColor: Colors.cyan,
          ),
        ),
        body: DoubleBlue(),
      )
    );
  }
}

상단에 IconButton을 통해 뒤로가기 버튼을 만들고 "Onpressed: " 에 Navigator.pop을 할당하여 Icon클릭 시 이전Page로 돌아가게 구현하였다. "body: " 에 할당되는 DoubleBlue() 와 Blue() 에서 Hero를 통해 전환애니메이션이 실행된다.

 

Hero(2)

class DoubleBlue extends StatelessWidget {
  @override
  Widget build(BuildContext context){
    return new LayoutBuilder(builder: (context, constraint) {
      double min = (constraint.biggest.width < constraint.biggest.height)
          ? constraint.biggest.width : constraint.biggest.height;
      return Column(
        children: [
          Container(
            width: constraint.biggest.width,
            height: constraint.biggest.height / 2,
            color: Colors.blue,
            child: Hero(
              tag: "b",
              child: Icon(Icons.access_alarms, size: min / 2, color: Colors.white),
            )
          ),
          Container(
            width: constraint.biggest.width,
            height: constraint.biggest.height / 2,
            color: Colors.blue,
            child: Icon(Icons.access_alarms, size: min / 2, color: Colors.white),
          ),
        ]
      );
    });
  }
}

Blue() Widget을 위아래로 2등분하여 2개 출력하는 Widget이다. Hero로 묶인 Icon이 Blue()의 Icon과 "tag: " 가 같기 때문에 화면이 전환될 때 해당 Icon에 애니메이션 효과가 나타난다.

'Flutter' 카테고리의 다른 글

Flutter Column, Row _ Expanded, Stack  (0) 2020.07.24
Flutter 해상도별 Image  (0) 2020.07.24
Flutter TabBar  (0) 2020.07.21
Flutter Swiper  (0) 2020.07.21
Flutter 기본 Widget 구현  (0) 2020.07.17