【Flutter】SingleChildScrollViewとGestureDetectorを併用してジェスチャー(スワイプ)を感知する

ネコニウム研究所

PCを利用したモノづくりに関連する情報や超個人的なナレッジを掲載するブログ

【Flutter】SingleChildScrollViewとGestureDetectorを併用してジェスチャー(スワイプ)を感知する

2024-3-22 | ,

FlutterでSingleChildScrollViewGestureDetectorを併用してジェスチャー(スワイプ)を感知したい!

概要

今回の記事では、FlutterでSingleChildScrollViewGestureDetectorを併用してジェスチャー(スワイプ)を感知する手順を掲載する。

仕様書

環境

  • Android Studio Giraffe | 2023.2.1 Patch 2
  • Flutter 3.19.6

手順書

過去のFlutternのプロジェクトをいじってたんだけども、画面上部から下に向けたスワイプのジェスチャーが感知しなくなった。

コードは下記のような感じでGestureDetectorの中にSingleChildScrollViewが配置されてる感じ。

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        body: const GestureAndScrollViewSample1()
      )
    )
  );
}

class GestureAndScrollViewSample1 extends StatelessWidget {
  const GestureAndScrollViewSample1({super.key});

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: GestureDetector(
        onVerticalDragEnd: (DragEndDetails details) {
          if (details.primaryVelocity! > 0.0) {
            debugPrint('Swiped Down!!!');
          } else if (details.primaryVelocity! < 0.0) {
            debugPrint('Swiped Up!!!');
          }
        },
        child: SingleChildScrollView(
          child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              mainAxisSize: MainAxisSize.max,
              children: [
                Container(
                  color: Colors.amber,
                  height: 500,
                  child: const Center(
                    child: Text('Amber'),
                  ),
                ),
                Container(
                  color: Colors.red,
                  height: 500,
                  child: const Center(
                    child: Text('Red'),
                  ),
                ),
                Container(
                  color: Colors.green,
                  height: 500,
                  child: const Center(
                    child: Text('Green'),
                  ),
                ),
                Container(
                  color: Colors.blue,
                  height: 500,
                  child: const Center(
                    child: Text('Blue'),
                  ),
                ),
                Container(
                  color: Colors.purple,
                  height: 500,
                  child: const Center(
                    child: Text('Purple'),
                  ),
                ),
              ]),
        ),
      ),
    );
  }
}

画面上部から下に向けたスワイプを感知するとSwiped Down!!!と出力されるはずなんだけども、出力されない!以前は表示されたはずなのに!

いろいろ試してて、親子関係を逆にSingleChildScrollViewの中にGestureDetectorを配置したらジェスチャーを感知するようになった。

import 'package:flutter/material.dart';

class GestureAndScrollViewSample2 extends StatelessWidget {
  const GestureAndScrollViewSample2({super.key});

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: SingleChildScrollView(
        child: GestureDetector(
          onVerticalDragEnd: (DragEndDetails details) {
            if (details.primaryVelocity! > 0.0) {
              debugPrint('Swiped Down!!!');
            } else if (details.primaryVelocity! < 0.0) {
              debugPrint('Swiped Up!!!');
            }
          },
          child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              mainAxisSize: MainAxisSize.max,
              children: [
                Container(
                  color: Colors.amber,
                  height: 500,
                  child: const Center(
                    child: Text('Amber'),
                  ),
                ),
                Container(
                  color: Colors.red,
                  height: 500,
                  child: const Center(
                    child: Text('Red'),
                  ),
                ),
                Container(
                  color: Colors.green,
                  height: 500,
                  child: const Center(
                    child: Text('Green'),
                  ),
                ),
                Container(
                  color: Colors.blue,
                  height: 500,
                  child: const Center(
                    child: Text('Blue'),
                  ),
                ),
                Container(
                  color: Colors.purple,
                  height: 500,
                  child: const Center(
                    child: Text('Purple'),
                  ),
                ),
              ]),
        ),
      ),
    );
  }
}

バージョンアップで使用が変わったのか、自分の勘違いなのか。

もうちょっといじってて分かったのが、SingleChildScrollViewがスクロール不要な状態(子が小さいとか)だとGestureDetectorが親だろうが子だろうがジェクチャーを感知できる。

class GestureAndScrollViewSample2 extends StatelessWidget {
  const GestureAndScrollViewSample2({super.key});

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: GestureDetector(
        onVerticalDragEnd: (DragEndDetails details) {
          if (details.primaryVelocity! > 0.0) {
            debugPrint('Swiped Down!!!');
          } else if (details.primaryVelocity! < 0.0) {
            debugPrint('Swiped Up!!!');
          }
        },
        child: SingleChildScrollView(
          child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              mainAxisSize: MainAxisSize.max,
              children: [
                Container(
                  color: Colors.amber,
                  child: const Center(
                    child: Text('Amber'),
                  ),
                ),
                Container(
                  color: Colors.red,
                  child: const Center(
                    child: Text('Red'),
                  ),
                ),
                Container(
                  color: Colors.green,
                  child: const Center(
                    child: Text('Green'),
                  ),
                ),
                Container(
                  color: Colors.blue,
                  child: const Center(
                    child: Text('Blue'),
                  ),
                ),
                Container(
                  color: Colors.purple,
                  child: const Center(
                    child: Text('Purple'),
                  ),
                ),
              ]),
        ),
      ),
    );
  }
}

うーん。

まとめ(感想文)

ケースにもよるんだけども、自分の場合は単純にスワイプしたら画面を更新したかっただけないのでGestureDetectorじゃなくてRefreshIndicatorの方が合ってたということに後日気づくのであった。