조용한 담장

Flutter: Hero 본문

Flutter

Flutter: Hero

iosroid 2020. 2. 12. 17:19

앱의 페이지(Page) 또는 루트(Route) 사이의 이동할 때 animation 을 만들어주는 위젯이다.

특히 이동하는 페이지 사이에 공통된 이미지 영역이 있다면 이를 기반으로 animation 을 만들기 위해 사용할 수 있다.

예를 들어 쇼핑앱의 제품 사진으로 된 목록중 하나를 선택하면 그 사진이 커지면서 제품의 상세 내용을 보여주는 동작을 생각해 볼 수 있다.

hero

위와 같이 이동하는 페이지 사이의 공통되게 보여주는 부분에 대한 animation 을 만드는데 사용한다.

Hero({
    Key key, @required Object tag, CreateRectTween createRectTween, 
    HeroFlightShuttleBuilder flightShuttleBuilder, 
    HeroPlaceholderBuilder placeholderBuilder, bool transitionOnUserGestures: false, 
    @required Widget child})

두 페이지의 공통된 영역을 tag 를 통해 child 위젯을 animation 생성 대상으로 연결하므로 같은 값을 가져야 올바른 대상과 작동이 된다.

Behind the scenes

https://flutter.dev/docs/development/ui/animations/hero-animations#behind-the-scenes

 

Hero animation 이 동작하는 과정을 첫 route 에서 다음 route 까지의 과정을 살펴보자.

hero 동작이 시작하면 첫 화면인 source route 의 화면은 사라지고(offscreen), 최종 화면인 destination hero 를 source route 와 같은 위치와 같은 크기로 Overlay 를 화면에 그린다. (t = 0.0)

위에서 선택한 첫번째 꽃 사진이 움직이는 animation 이 그려지는 공간이 Overlay 이다.

이때는 Tween<Rect> 를 이용하며 createRectTween 속성값에 따라 달라질 수 있다.

기본은 MaterialRectArcTween 을 사용한다.

animation 이 완료되면 hero 위젯을 overlay 에서 destination route 으로 옮긴다. (t = 1.0)

destination hero 가 destination route 의 최종 위치에 나타난다.

Sample code

List<Widget> items(BuildContext context) {
    _loadAsset(context);
    return List.generate(assets[assetImages].length, (index) {
        return Container(
            padding: EdgeInsets.all(8),
            child: Container(
                child: Hero(
                    tag: index,
                    //..
                ),
            ));
    });
}
//...
body: Center(
    child: GridView.count(
        crossAxisCount: 2,
        children: items(context),
    ),
),

첫 페이지에 GridView 를 사용한다.

이미지 목록은 assets 에 저장되어 있다.

이미지의 index 값을 Herotag 로 사용하여 구분한다.

Hero(
    tag: index,
    child: Material(
        color: Colors.transparent,
        child: InkWell(
            child: Image(
                image: AssetImage(
                    "$imagePath${assets[assetImages]['image${index + 1}']}"),
            ),
            onTap: () {
                Navigator.of(context).push(MaterialPageRoute<void>(
                    builder: (BuildContext context) {
                        return _buildPage(context, index);
                    }
                ));
            },
        ),
    ),
),

Hero 는 이미지의 indextag 로 가지고 Image 를 최종 child 로 가진다.

터치 반응을 위해 MaterialInkWell 을 사용한다.

이미지는 assets 에서 index 값에 따라 불러온다.

여기까지로 source route 의 화면이 만들어 졌다.

onTap 에서 navigate 를 통해 MaterialPageRoute 로 destination route 를 만든다.

Widget _buildPage(BuildContext context, int index) {
    return Scaffold(
        appBar: AppBar(
            title: Text('Item'),
        ),
        body: Center(
            child: Container(
                width: 300.0,
                height: 300.0,
                child: Column(
                    children: <Widget>[
                        Card(
                            child: Column(
                                children: <Widget>[
                                    Hero(
                                        tag: index,
                                        child: Image(
                                            image: AssetImage(
                                                "$imagePath${assets[assetImages]['image${index + 1}']}"),
                                        ),
                                    ),
                                    Container(
                                        height: 40.0,
                                        alignment: Alignment.center,
                                        child: Text("File name: ${assets[assetImages]['image${index + 1}']}"),
                                    ),
                                ],
                            ),
                        ),
                        FlatButton(
                            color: Colors.blue[100],
                            child: Text("Back"),
                            onPressed: () {
                                Navigator.of(context).pop();
                            },
                        ),
                    ],
                ),
            ),
        ),
    );
}

CardImage 와 "File name" 텍스트를 index 에 따라 넣어 만든다.

ImageHerochild 로 지정하고 tagindex 로 하여 source route 의 hero widget 과 매칭한다.

Reference

https://api.flutter.dev/flutter/widgets/Hero-class.html

https://flutter.dev/docs/development/ui/animations/hero-animations

'Flutter' 카테고리의 다른 글

WHO 에서 Flutter 로 앱 개발 중  (0) 2020.03.27
Flutter: Staggered Animations  (0) 2020.02.19
Flutter : Animation  (3) 2020.02.07
플러터 프로젝트 코멘트 지우기 (remove comments)  (0) 2020.01.28
[플러터] Flutter 1.12 release  (0) 2019.12.12
Comments