Flutter

FlutterでCupertinoPickerっぽいのを自作する方法(振動有)

2020/02/10

概要

以下のようなCupertinoPickerみたいなUIを自作する方法を紹介します.

ListWheelScrollView

CupertinoPickerの拡張性が低い問題

趣味アプリで先ほど紹介したようなUIを作りたくなりました.

調べてみるとCupertinoPickerという便利なWidgetが存在するではないですか!

しかし使ってみると, リスト間の幅や角度などは自由にいじれるのですが,
選択している部分のborderを消したり・選択している部分のみに背景色をつけたいなど,
細かいUIの変更はできない...

ListViewで車輪の再発明

どうしてもタップしたことで遷移したという表現をしたかったので,
ListViewを使ってドラムロールの作成に取り掛かりました.

ListViewで無限スクロールを実装するのも結構大変で, うまくセンターに寄せるのも大変でした...

numberpickerなどを読みながら頑張って実装していた時,
ScrollPhysicとやらがListViewのスクロールの仕方を変更することができると知り調べてみると...

僕がやりたかったことが書いてあるじゃないですか...
そう.ListWheelScrollViewという素晴らしいウィジェットがありました.

僕の実装方法

上記のDevenさんの記事を読んでもらってもよいのですが,
あれだけだと無限スクロールやCupertinoPickerのようにスクロール時に振動などがありません.

なので, HapticFeedbackやListWheelChildLoopingListDelegateを利用して,
拡張性のあるCupertinoPickerを作っていきます.

※上記の画像のような選択部分のみを背景色にしたりやアニメーションは,
StackやOffsetなどで実装しています.
需要があれば記事にします.

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class CustomeListWheel extends StatefulWidget {
  
  State<StatefulWidget> createState() => CustomeListWheelState();
}

class CustomeListWheelState extends State<CustomeListWheel> {
  FixedExtentScrollController _fixedExtentScrollController;
  int currentIndex = 0;
  final list = ['aaaa', 'bbb', 'ccc', 'ddd'];
  
  
  void initState() {
    _fixedExtentScrollController = FixedExtentScrollController();
    super.initState();
  }
  
  
  Widget build(BuildContext context) {
    return NotificationListener<ScrollNotification>(
      onNotification: (ScrollNotification scrollnotification) {
        /// スクロールされた際に選択している値が変わっていたら振動させる
        if (currentIndex != _getCurrentIndex()) {
          currentIndex = _getCurrentIndex();
          HapticFeedback.mediumImpact();
        }
        return true;
      },
      /// ドラムロールUI
      child: ListWheelScrollView.useDelegate(
        controller: _fixedExtentScrollController,
        itemExtent: 56,
        physics: FixedExtentScrollPhysics(),
        diameterRatio: 1.7,
        squeeze: 0.9,
        /// 無限スクロールできるようにする
        childDelegate: ListWheelChildLoopingListDelegate(
          children: list
              .map((String selectItem) => Container(
                    width: double.infinity,
                    height: 56,
                    child: Center(
                      child: Text(selectItem),
                    ),
                  ))
              .toList(),
        ),
      ),
    );
  }
  
  /// 現在のindexを取得できるように処理してあげる
  int _getCurrentIndex() {
    int index = _fixedExtentScrollController.selectedItem % list.length;
    return index;
  }
}

Twitterフォロー待ってます!