【Flutter】iOS 앱의 마이 페이지 화면등에서 자주 있는 스크롤하면 이미지가 줌 되는 UI를 구현해 보았다

15212 단어 iOSDartFlutter

소개



아래의 캡처 동영상과 같은, iOS 앱의 마이 페이지 화면등에서 자주 있는 스크롤하면 이미지가 줌 되는 UI를 구현해 보았습니다. (이 UI라고 부르면 좋을까?)

소스 코드는 여기 리포지토리 에 올리고 있습니다.



운영 환경



Flutter: 1.22
Dart: 2.10

구현



구현 절차는 다음과 같습니다.

  • CustomScrollView 및 SliverAppBar에서 이미지 헤더가있는 목록을 표시합니다
  • 헤더의 너비와 높이를 이미지 비율에 맞추기

  • CustomScrollView 및 SliverAppBar에서 이미지 헤더가 있는 목록 보기



    먼저 헤더가 있는 목록을 만듭니다. 소스 코드는 여기입니다.
    import 'package:flutter/cupertino.dart';
    import 'package:flutter/material.dart';
    
    class ScrollZoomHeaderScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        // ヘッダー画像
        final Image headerImage = Image.asset(
          'assets/view.jpg',
          fit: BoxFit.cover,
        );
        return Scaffold(
            body: SafeArea(
          child: CustomScrollView(
            slivers: <Widget>[
              SliverAppBar(
                  backgroundColor: Colors.white,
                  pinned: true,
                  stretch: true,
                  expandedHeight: 200,
                  flexibleSpace: FlexibleSpaceBar(
                      collapseMode: CollapseMode.parallax,
                      stretchModes: [
                        StretchMode.zoomBackground,
                      ],
                      background: headerImage)),
              SliverPadding(
                padding: const EdgeInsets.all(10),
                sliver: SliverList(
                    delegate: SliverChildBuilderDelegate((context, index) {
                  return Card(
                    child: ListTile(
                      title: Text("List:$index"),
                      leading: Icon(Icons.person),
                    ),
                  );
                }, childCount: 100)),
              ),
            ],
          ),
        ));
      }
    }
    

    여기를 실행하면 머리글 이미지가 있는 목록이 표시되고 스크롤 위치가 맨 위로 아래로 당겨지도록 스크롤하면 이미지가 확대되면서 표시됩니다.
    확대하면서 표시하려면 SliverAppBar의 stretch 속성을 true로 설정해야 합니다.

    헤더의 너비와 높이를 이미지 비율에 맞추기



    여기까지의 구현으로 거의 요건은 채웠습니다만, 상기의 구현이라면 아래와 같은 동영상과 같이, 아래로 당겨서부터 화상이 확대될 때까지의 사이에 러그가 있는 것을 알 수 있습니다. (전해지나요?)



    이는 이미지 높이와 헤더 높이를 설정하는 SliverAppBar의 expandedHeight가 맞지 않기 때문입니다. (이전의 구현이라고 expandedHeight는 200으로 고정하고 있었습니다)

    그래서 헤더의 높이를 이미지의 높이와 맞추어 줄 필요가 있습니다만, 그대로 설정하면 큰 이미지였을 경우에 헤더가 화면에 들어갈 수 없기 때문에, 헤더의 높이를 이미지의 비율로 계산합니다 .
    // ヘッダーの幅
    // 画面幅いっぱいにする
    final headerWidth = MediaQuery.of(context).size.width;
    
    // ヘッダーの高さ
    // 画像の比率から計算する
    final headerHeight = headerWidth * (画像の高さ / 画像の幅)
    
    

    ※ (상기와 같이 헤더의 높이를 동적으로 하는 것 이외에는, 헤더의 높이를 고정치로 해 화상을 그 높이에 맞추어 자르기 위한 방침도 있습니다. 헤더의 높이는 고정에 하는 것이 많기 때문에 그쪽이 좋을지도 모릅니다)

    코드 전체



    전체 소스 코드는 여기입니다.
    이미지는 ImageStreamListener 로 읽어들여, Completer 로 로드 완료를 발화시키고, FutureBuilder 내에서 사이즈를 참조하고 있습니다.
    import 'dart:async';
    
    import 'package:flutter/cupertino.dart';
    import 'package:flutter/material.dart';
    import 'dart:ui' as ui show Image;
    
    class ScrollZoomHeaderScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        // ヘッダー画像
        final Image headerImage = Image.asset(
          'assets/view.jpg',
          fit: BoxFit.cover,
        );
    
        // ヘッダー画像のCompleter
        Completer<ui.Image> completer = new Completer<ui.Image>();
        headerImage.image
            .resolve(ImageConfiguration())
            .addListener(ImageStreamListener((ImageInfo info, bool _) {
          completer.complete(info.image);
        }));
    
        // ヘッダー画像の幅
        final headerWidth = MediaQuery.of(context).size.width;
    
        return Scaffold(
            body: SafeArea(
          child: CustomScrollView(
            slivers: <Widget>[
              FutureBuilder<ui.Image>(
                  future: completer.future,
                  builder: (context, snapshot) {
                    return SliverAppBar(
                        backgroundColor: Colors.white,
                        pinned: true,
                        stretch: true,
                        expandedHeight: snapshot.hasData
                            ? headerWidth *
                                snapshot.data.height /
                                snapshot.data.width
                            : 0,
                        flexibleSpace: FlexibleSpaceBar(
                            collapseMode: CollapseMode.parallax,
                            stretchModes: [
                              StretchMode.zoomBackground,
                            ],
                            background: headerImage));
                  }),
              SliverPadding(
                padding: const EdgeInsets.all(10),
                sliver: SliverList(
                    delegate: SliverChildBuilderDelegate((context, index) {
                  return Card(
                    child: ListTile(
                      title: Text("List:$index"),
                      leading: Icon(Icons.person),
                    ),
                  );
                }, childCount: 100)),
              ),
            ],
          ),
        ));
      }
    }
    
    

    이상입니다.

    참고한 기사


  • SliverAppBar 문서
  • ImageStreamListener 정보
  • 좋은 웹페이지 즐겨찾기