[Flutter]BottomSheetでスクロールできるようにする方法

BottomSheetBarと呼ばれる、便利なPackageがあります。デザイナーが作成したUIを実現するのにとても役立つことができました。実際に使ってみて、とても便利ですが、スクロールで期待通りではありませんでした。
その内容としては、`SingleChildScrollView`のスクロールができずにBottomSheetが下がってしまうバグがありました。その治し方をご紹介します。

# 初めに
簡単な使い方はこちらを参考にしてください。
https://pub.dev/packages/bottom_sheet_bar



# サンプルコード

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({
    Key? key,
    required this.title,
  }) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool _isLocked = false;
  bool _isCollapsed = true;
  bool _isExpanded = false;
  final _bsbController = BottomSheetBarController();
  final _listSizeController = TextEditingController(text: '5');

  @override
  void initState() {
    _bsbController.addListener(_onBsbChanged);
    super.initState();
  }

  @override
  void dispose() {
    _bsbController.removeListener(_onBsbChanged);
    super.dispose();
  }

  void _onBsbChanged() {
    if (_bsbController.isCollapsed && !_isCollapsed) {
      setState(() {
        _isCollapsed = true;
        _isExpanded = false;
      });
    } else if (_bsbController.isExpanded && !_isExpanded) {
      setState(() {
        _isCollapsed = false;
        _isExpanded = true;
      });
    }
  }

  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: BottomSheetBar(
          willPopScope: true,
          backdropColor: Colors.green.withOpacity(0.8),
          locked: _isLocked,
          controller: _bsbController,
          borderRadius: const BorderRadius.only(
            topLeft: Radius.circular(32.0),
            topRight: Radius.circular(32.0),
          ),
          borderRadiusExpanded: const BorderRadius.only(
            topLeft: Radius.circular(0.0),
            topRight: Radius.circular(0.0),
          ),
          boxShadows: [
            BoxShadow(
              color: Colors.grey.withOpacity(0.5),
              spreadRadius: 5.0,
              blurRadius: 32.0,
              offset: const Offset(0, 0), // changes position of shadow
            ),
          ],
          expandedBuilder: (scrollController) {
            return Material(
              color: Colors.transparent,
              child: CustomScrollView(
                //TODO ここがとても大切!!
                controller: scrollController,
                shrinkWrap: true,
                slivers: [
                  SliverFixedExtentList(
                    itemExtent: 56.0, // I'm forcing item heights
                    delegate: SliverChildBuilderDelegate(
                      (context, index) => ListTile(
                        title: Text(
                          '$index',
                          style: const TextStyle(fontSize: 20.0),
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            );
          },
          collapsed: TextButton(
            onPressed: () => _bsbController.expand(),
            child: Text(
              'Click${_isLocked ? "" : " or swipe"} to expand',
              style: const TextStyle(color: Colors.white),
            ),
          ),
          body: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 12.0),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text('BottomSheetBar is'),
                Text(
                  _isLocked ? 'Locked' : 'Unlocked',
                  style: Theme.of(context).textTheme.headline4,
                ),
                Text(
                  _isLocked
                      ? 'Bottom sheet cannot be expanded or collapsed by swiping'
                      : 'Swipe it to expand or collapse the bottom sheet',
                  textAlign: TextAlign.center,
                ),
              ],
            ),
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: _toggleLock,
          tooltip: 'Toggle Lock',
          child:
              _isLocked ? const Icon(Icons.lock) : const Icon(Icons.lock_open),
        ),
      );

  void _toggleLock() {
    setState(() {
      _isLocked = !_isLocked;
    });
  }
}

```

# 解説



動画はこちらです。


## ScrollControllerを追加しなかった場合

![](https://storage.googleapis.com/zenn-user-upload/c46f8e183a48-20221127.gif)


## ScrollControllerを追加した場合

![](https://storage.googleapis.com/zenn-user-upload/637fd6f003a5-20221127.gif)



# 最後に
![Frame 9 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/87f2a488-c5cd-549d-f07f-01b44a1317b8.png)
<b>Veroo</b>は、Blockchain技術を活用した「想いの巡るグルメSNSアプリ」です。食に対する熱量、例えばラーメンがめちゃくちゃ好きで年間400杯食べているとか、変態的に具材にこだわった中華料理を作っているなどのような、今まで直接お金にはならなかった食に対する情熱や熱量、その裏側にある想いがトークンを使うことで巡る世界。自分のラーメンに費やす熱量がコミュニティに承認される。自分がほんとにいいと思って作った食べ物がコミュニティで広まる。Verooは単なるSNSアプリではなく、クリエイターたちの想いが紡がれるソーシャルグルメアプリです。

##### Homepage: https://veroo.xyz/
##### Discord URL：https://t.co/VoT0gpsflA