Flutter WebアプリでDataTabelを横スクロールさせる

FlutterのWebアプリ内で、csvファイルを読み込んで表示させる必要があったのですが、データが大きい時にスクロールをさせたいと思い調べてみました。

通常の縦スクロールはSingleChildScrollViewもしくはListViewを使えば問題なくできます。

横スクロールは、SingleChildScrollViewのscrollDirectionに、Axis.horizontalを指定すれば良いとありました。

SingleChildScrollView(    // これは縦スクロール
      children: [
        SingleChildScrollView(                               // <- これは横スクロール
          scrollDirection: Axis.horizontal,  // 方向を指定
          child: DataTable(columns: columnList, rows: rowList),
        )
      ],
)

しかし、この方法では、どうしても横スクロールができません。

悩んでいましたところ、以下のサイトに原因と解決策がありました。

モバイル開発ではドラッグでスクロールできますが、WEB開発のデフォルトではドラッグでスクロールすることはできません。

https://note.com/hatchoutschool/n/nfe12a0aa069a

この最初の一文はちょっと衝撃的でした。まだまだ勉強不足です。

こちらのサイトを参考に、csvファイルを読み込み画面に表示するコードを書いてみました。
縦横方向ともに、スクロールできるかと思います。

import 'package:flutter/material.dart';
import 'package:csv/csv.dart';
import 'dart:async' show Future;
import 'package:universal_html/html.dart' as html;
import 'dart:ui';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'CSV to Table',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<List<dynamic>> data = [];

  Future<void> loadCSV() async {
    final html.InputElement? uploadInput =
        html.FileUploadInputElement() as html.InputElement?;

    uploadInput!.click();

    uploadInput.onChange.listen((e) {
      final html.File file = uploadInput.files!.first!;
      final html.FileReader reader = html.FileReader();

      reader.readAsText(file);

      reader.onLoadEnd.listen((e) {
        final csvData = reader.result as String;
        final List<List<dynamic>> csvTable =
            CsvToListConverter().convert(csvData);
        setState(() {
          data = csvTable;
        });
      });
    });
  }

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('CSV to Table'),
      ),
      body: SizedBox(
        width: double.infinity,
        child: data.isNotEmpty
            ? ScrollConfiguration(                              // <-- これでくくる
                behavior: CustomScrollBehavior(),
                child: SingleChildScrollView(      
                    scrollDirection: Axis.vertical,             // 縦スクロール
                    child: SingleChildScrollView(   
                        scrollDirection: Axis.horizontal,   //横スクロール
                        child: DataTable(
                          columns: data[0]
                              .map<DataColumn>((column) =>
                                  DataColumn(label: Text(column.toString())))
                              .toList(),
                          rows: data
                              .skip(1)
                              .map<DataRow>((row) => DataRow(
                                    cells: row
                                        .map<DataCell>((cell) =>
                                            DataCell(Text(cell.toString())))
                                        .toList(),
                                  ))
                              .toList(),
                        ))))
            : const Center(
                child: Text('No data available.'),
              ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          loadCSV();
        },
        child: Icon(Icons.file_upload),
      ),
    );
  }
}

// 以下のクラスを追加
class CustomScrollBehavior extends MaterialScrollBehavior {
  @override
  Set<PointerDeviceKind> get dragDevices => {
        PointerDeviceKind.touch,
        PointerDeviceKind.mouse,
      };
}