【Flutter】ShellRoute/StatefulShellRouteを使用中にiOSのステータスバータップを検出する
1. はじめに
iOSにおいて、ステータスバーをタップしたときにスクロールをトップへ戻す動きがあると思います。iOSユーザは、頻繁にステータスバーをタップしてScroll To Topさせることを行うと思いますので、UXとしても重要な点なのかなと思っています。
例えば単純な以下のコードは、実装上特に何も考慮せずともiOSのステータスバーをタップするとスクロールがトップへ戻る挙動になります。
これはScaffoldがPrimaryScrollControllerを使用して機能させていると考えれば良さそうです。
Inheriting this ScrollController can provide default behavior for scroll views in a subtree. For example, the Scaffold uses this mechanism to implement the scroll-to-top gesture on iOS.
では、以下のようにgo_routerのShellRouteやStatefulShellRouteを利用した場合はどうでしょうか。
- ShellRouteを使用したサンプル
- StatefulShellRouteを使用したサンプル
上記コードのようにShellRoute/StatefulShellRouteをすると、ステータスバーをタップしてもスクロールが動きません。
そこで、issueを起票しました。ステータスバーのタップを検出する方法があるのでしょうか?
(おそらく既存のissueも存在しそうで重複だと言われてクローズされるかもしれませんが)
2. 正しいのかどうか自信の無いソリューション
PrimaryScrollControllerは、以下のようにInheritedWidgetであり、
Creates a widget that associates a [ScrollController] with a subtree.
ということのようです。
class PrimaryScrollController extends InheritedWidget {
/// Creates a widget that associates a [ScrollController] with a subtree.
const PrimaryScrollController({
それならば、ScrollControllerのattachメソッドを使ってなんとかできないものかと模索しました。
attach
メソッドの引数にはScrollPositionが必要です。
ScrollPositionWithSingleContextを継承したダミーのScrollPositionを渡しておけば、ステータスバータップ時にanimateToが呼び出されるのではないのかと。
animateTo
が呼び出されてさえくれれば、あとはそこをコールバックで処理してなんとかなりそうだ、と考えて模索したのが下記のコードです。
- 作成したTapStatusBarNotifierクラス
以下は、TapStatusBarNotifierを使用したコードです。onTapStatusBar
コールバック内で自前でスクロールトップさせています。
- ShellRoute
- StatefulShellRoute
matchedLocation
を確認して、現在表示中の画面であればスクロールをトップに戻しています。この確認を忘れてしまうと、StatefulShellRouteの場合は他の画面も一緒にスクロールトップされてしまうことになります。(逆に言えば、そんなこともできるのか!)
onTapStatusBar: () {
if (GoRouter.of(context).routerDelegate.currentConfiguration.last.matchedLocation == '/home') {
_controller.animateTo(0, duration: const Duration(milliseconds: 300), curve: Curves.linear);
}
},
以下は、StatefulShellRouteの挙動です。Like画面のステータスバータップにてスクロールトップしても、Home画面には影響を与えていないことを確認できます。
3. おわりに
一応やりたいことはできたのですが、これが正しい対応なのか最善なのか自信が持てません。issueへのコメントを待ちたいと思います。どなたかよろしくお願い致します🙇♂
Discussion